comparison lisp/prim/itimer.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 0293115a14e9
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
1 ;;; Interval timers for XEmacs
2 ;;; Copyright (C) 1988, 1991, 1993 Kyle E. Jones
3 ;;; Modified 5 Feb 91 by Jamie Zawinski <jwz@lucid.com> for Lucid Emacs
4 ;;; And again, 15 Dec 93.
5 ;;;
6 ;; This file is part of XEmacs.
7
8 ;; XEmacs is free software; you can redistribute it and/or modify it
9 ;; under the terms of the GNU General Public License as published by
10 ;; the Free Software Foundation; either version 2, or (at your option)
11 ;; any later version.
12
13 ;; XEmacs is distributed in the hope that it will be useful, but
14 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 ;; General Public License for more details.
17
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with XEmacs; see the file COPYING. If not, write to the Free
20 ;; Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 ;;; Synched up with: Not in FSF.
23
24 ;;;
25 ;;; Send bug reports to kyle@uunet.uu.net.
26
27 ;; The original v18 version of this file worked by having an external program
28 ;; wake up once a second to generate an interrupt for emacs; then an emacs
29 ;; process filter was used to schedule timers.
30 ;;
31 ;; This version works by associating with each timer a "timeout" object,
32 ;; since the XEmacs/Lucid Emacs event loop has the concept of timers built
33 ;; in to it. There is no single scheduler function; instead, each timer
34 ;; re-sets itself as it is invoked.
35
36 ;; `itimer' feature means Emacs-Lisp programers get:
37 ;; itimerp, itimer-value, itimer-restart, itimer-function,
38 ;; set-itimer-value, set-itimer-restart, set-itimer-function
39 ;; get-itimer, start-itimer, read-itimer, delete-itimer
40 ;;
41 ;; Interactive users get these commands:
42 ;; edit-itimers, list-itimers, start-itimer
43 ;;
44 ;; See the doc strings of these functions for more information.
45
46 (defvar itimer-version "1.00"
47 "Version number of the itimer package.")
48
49 (defvar itimer-list nil
50 "List of all active itimers.")
51
52 ;; not needed in XEmacs
53 ;(defvar itimer-process nil
54 ; "Process that drives all itimers.")
55
56 ;; This value is maintained internally; it does not determine itimer
57 ;; granularity. Itimer granularity is 1 second, plus delays due to
58 ;; system and Emacs internal activity that delay dealing with process
59 ;; output.
60 ;; not needed in XEmacs
61 ;(defvar itimer-process-next-wakeup 1
62 ; "Itimer process will wakeup to service running itimers within this
63 ;many seconds.")
64
65 (defvar itimer-edit-map nil
66 "Keymap used when in Itimer Edit mode.")
67
68 (if itimer-edit-map
69 ()
70 (setq itimer-edit-map (make-sparse-keymap))
71 (define-key itimer-edit-map "s" 'itimer-edit-set-field)
72 (define-key itimer-edit-map "d" 'itimer-edit-delete-itimer)
73 (define-key itimer-edit-map "q" 'itimer-edit-quit)
74 (define-key itimer-edit-map "\t" 'itimer-edit-next-field)
75 (define-key itimer-edit-map " " 'next-line)
76 (define-key itimer-edit-map "n" 'next-line)
77 (define-key itimer-edit-map "p" 'previous-line)
78 (define-key itimer-edit-map "\C-?" 'itimer-edit-previous-field)
79 (define-key itimer-edit-map "x" 'start-itimer)
80 (define-key itimer-edit-map "?" 'itimer-edit-help))
81
82 (defvar itimer-edit-start-marker nil)
83
84 ;; macros must come first... or byte-compile'd code will throw back its
85 ;; head and scream.
86
87 (defmacro itimer-decf (variable)
88 (list 'setq variable (list '1- variable)))
89
90 (defmacro itimer-incf (variable)
91 (list 'setq variable (list '1+ variable)))
92
93 (defmacro itimer-signum (n)
94 (list 'if (list '> n 0) 1
95 (list 'if (list 'zerop n) 0 -1)))
96
97 ;; Itimer access functions should behave as if they were subrs. These
98 ;; macros are used to check the arguments to the itimer functions and
99 ;; signal errors appropriately if the arguments are not valid.
100
101 (defmacro check-itimer (var)
102 "If VAR is not bound to an itimer, signal wrong-type-argument.
103 This is a macro."
104 (list 'setq var
105 (list 'if (list 'itimerp var) var
106 (list 'signal ''wrong-type-argument
107 (list 'list ''itimerp var)))))
108
109 (defmacro check-itimer-coerce-string (var)
110 "If VAR is not bound to a string, look up the itimer that it names and
111 bind VAR to it. Otherwise if VAR is not bound to an itimer, signal
112 wrong-type-argument. This is a macro."
113 (list 'setq var
114 (list 'cond
115 (list (list 'itimerp var) var)
116 (list (list 'stringp var) (list 'get-itimer var))
117 (list t (list 'signal ''wrong-type-argument
118 (list 'list ''string-or-itimer-p var))))))
119
120 (defmacro itimer-check-natnum (var)
121 "If VAR is not bound to a non-negative number, signal wrong-type-argument.
122 This is a macro."
123 (list 'setq var
124 (list 'if (list 'natnump var) var
125 (list 'signal ''wrong-type-argument
126 (list 'list ''natnump var)))))
127
128 (defmacro itimer-check-string (var)
129 "If VAR is not bound to a string, signal wrong-type-argument.
130 This is a macro."
131 (list 'setq var
132 (list 'if (list 'stringp var) var
133 (list 'signal ''wrong-type-argument
134 (list 'list ''stringp var)))))
135
136 ;; Functions to access and modify itimer attributes.
137
138 (defun itimerp (obj)
139 "Returns non-nil iff OBJ is an itimer."
140 (and (consp obj) (stringp (car obj)) (eq (length obj)
141 5 ; for XEmacs
142 ;4 ; original version
143 )))
144
145 (defun itimer-name (itimer)
146 "Returns the name of ITIMER."
147 (check-itimer itimer)
148 (car itimer))
149
150 (defun itimer-value (itimer)
151 "Returns the number of seconds until ITIMER expires."
152 (check-itimer itimer)
153 (nth 1 itimer))
154
155 (defun itimer-restart (itimer)
156 "Returns the value to which ITIMER will be set at restart.
157 nil is returned if this itimer doesn't restart."
158 (check-itimer itimer)
159 (nth 2 itimer))
160
161 (defun itimer-function (itimer)
162 "Returns the function of ITIMER.
163 This function is called each time ITIMER expires."
164 (check-itimer itimer)
165 (nth 3 itimer))
166
167 ;; XEmacs-specific
168 (defun itimer-id (itimer)
169 "Returns the timeout-id of ITIMER."
170 (check-itimer itimer)
171 (nth 4 itimer))
172
173 (defun set-itimer-value (itimer value
174 ;; XEmacs doesn't need this
175 ;; &optional nowakeup
176 )
177 "Set the timeout value of ITIMER to be VALUE.
178 Itimer will expire is this many seconds.
179 Returns VALUE."
180 ;; Optional third arg NOWAKEUP non-nil means do not wakeup the itimer
181 ;; process to recompute a correct wakeup time, even if it means this
182 ;; itimer will expire late. itimer-process-filter uses this option.
183 ;; This is not meant for ordinary usage, which is why it is not
184 ;; mentioned in the doc string.
185 (check-itimer itimer)
186 (itimer-check-natnum value)
187 (let ((inhibit-quit t))
188
189 ; ;; If we're allowed to wakeup the itimer process,
190 ; ;; and the itimer process's next wakeup needs to be recomputed,
191 ; ;; and the itimer is running, then we wakeup the itimer process.
192 ; (or (and (not nowakeup) (< value itimer-process-next-wakeup)
193 ; (get-itimer (itimer-name itimer))
194 ; (progn (itimer-process-wakeup)
195 ; (setcar (cdr itimer) value)
196 ; (itimer-process-wakeup)))
197 ; (setcar (cdr itimer) value))
198
199 ;; the XEmacs way:
200 (if (itimer-id itimer)
201 (deactivate-itimer itimer))
202 (setcar (cdr itimer) value)
203 (activate-itimer itimer)
204
205 value))
206
207 (defun set-itimer-restart (itimer restart)
208 "Set the restart value of ITIMER to be RESTART.
209 If RESTART is nil, ITIMER will not restart when it expires.
210 Returns RESTART."
211 (check-itimer itimer)
212 (if restart (itimer-check-natnum restart))
213 (and restart (< restart 1) (signal 'args-out-of-range (list restart)))
214 ;; (setcar (cdr (cdr itimer)) restart)
215 ;; the XEmacs way
216 (let ((was-active (itimer-id itimer))
217 (inhibit-quit t))
218 (if was-active
219 (deactivate-itimer itimer))
220 (setcar (cdr (cdr itimer)) restart)
221 (if was-active
222 (progn
223 (setcar (cdr itimer) restart)
224 (if restart
225 (activate-itimer itimer)))))
226 restart)
227
228 (defun set-itimer-function (itimer function)
229 "Set the function of ITIMER to be FUNCTION.
230 FUNCTION will be called when itimer expires.
231 Returns FUNCTION."
232 (check-itimer itimer)
233 (setcar (cdr (cdr (cdr itimer))) function))
234
235 ;; XEmacs-specific
236 (defun set-itimer-id (itimer id)
237 (check-itimer itimer)
238 (setcar (cdr (cdr (cdr (cdr itimer)))) id))
239
240 (defun get-itimer (name)
241 "Return itimer named NAME, or nil if there is none."
242 (itimer-check-string name)
243 (assoc name itimer-list))
244
245 (defun read-itimer (prompt &optional initial-input)
246 "Read the name of an itimer from the minibuffer and return the itimer
247 associated with that name. The user is prompted with PROMPT.
248 Optional second arg INITIAL-INPUT non-nil is inserted into the
249 minibuffer as initial user input."
250 (get-itimer (completing-read prompt itimer-list nil 'confirm initial-input)))
251
252 (defun delete-itimer (itimer)
253 "Deletes ITIMER. ITIMER may be an itimer or the name of one."
254 (check-itimer-coerce-string itimer)
255 (deactivate-itimer itimer) ;; for XEmacs
256 (setq itimer-list (delq itimer itimer-list)))
257
258 ;jwz: this is preloaded so don't ;;;###autoload
259 (defun start-itimer (name function value &optional restart)
260 "Start an itimer.
261 Args are NAME, FUNCTION, VALUE &optional RESTART.
262 NAME is an identifier for the itimer. It must be a string. If an itimer
263 already exists with this name, NAME will be modified slightly to until
264 it is unique.
265 FUNCTION should be a function (or symbol naming one) of no arguments. It
266 will be called each time the itimer expires. The function can access
267 itimer that invoked it through the variable `current-itimer'.
268 VALUE is the number of seconds until this itimer expires.
269 Optional fourth arg RESTART non-nil means that this itimer should be
270 restarted automatically after its function is called. Normally an itimer
271 is deleted at expiration after its function has returned.
272 If non-nil RESTART should be a number indicating the value at which the
273 itimer should be set at restart time.
274 Returns the newly created itimer."
275 (interactive
276 (list (completing-read "Start itimer: " itimer-list)
277 (read (completing-read "Itimer function: " obarray 'fboundp))
278 (let (value)
279 (while (not (natnump value))
280 (setq value (read-from-minibuffer "Itimer value: " nil nil t)))
281 value)
282 (let ((restart t))
283 (while (and restart (not (natnump restart)))
284 (setq restart (read-from-minibuffer "Itimer restart: " nil nil t)))
285 restart)))
286 (itimer-check-string name)
287 (itimer-check-natnum value)
288 (if restart (itimer-check-natnum restart))
289 ;; Make proposed itimer name unique if it's not already.
290 (let ((oname name)
291 (num 2))
292 (while (get-itimer name)
293 (setq name (concat oname "<" num ">"))
294 (itimer-incf num)))
295 ; ;; If there's no itimer process, start one now.
296 ; ;; Otherwise wake up the itimer process so that seconds slept before
297 ; ;; the new itimer is created won't be counted against it.
298 ; (if itimer-process
299 ; (itimer-process-wakeup)
300 ; (itimer-process-start))
301 (let ((inhibit-quit t))
302 ;; add the itimer to the global list
303 (setq itimer-list
304 (cons (list name value restart function nil) ; extra slot for XEmacs
305 itimer-list))
306 ; ;; If the itimer process is scheduled to wake up too late for the itimer
307 ; ;; we wake it up to calculate a correct wakeup value giving consideration
308 ; ;; to the newly added itimer.
309 ; (if (< value itimer-process-next-wakeup)
310 ; (itimer-process-wakeup)))
311 ;; for XEmacs
312 (activate-itimer (car itimer-list))
313 )
314 (car itimer-list))
315
316 ;; User level functions to list and modify existing itimers.
317 ;; Itimer Edit major mode, and the editing commands thereof.
318
319 (defun list-itimers ()
320 "Pop up a buffer containing a list of all itimers.
321 The major mode of the buffer is Itimer Edit mode. This major mode provides
322 commands to manipulate itimers; see the documentation for
323 `itimer-edit-mode' for more information."
324 (interactive)
325 (let* ((buf (get-buffer-create "*Itimer List*"))
326 (opoint (point))
327 (standard-output buf)
328 (itimers (reverse itimer-list)))
329 (set-buffer buf)
330 (itimer-edit-mode)
331 (setq buffer-read-only nil)
332 (erase-buffer)
333 (insert "Name Value Restart Function\n"
334 "---- ----- ------- --------")
335 (if (null itimer-edit-start-marker)
336 (setq itimer-edit-start-marker (point)))
337 (while itimers
338 (newline 1)
339 (prin1 (itimer-name (car itimers)))
340 (tab-to-tab-stop)
341 (prin1 (itimer-value (car itimers)))
342 (tab-to-tab-stop)
343 (prin1 (itimer-restart (car itimers)))
344 (tab-to-tab-stop)
345 (prin1 (itimer-function (car itimers)))
346 (setq itimers (cdr itimers)))
347 ;; restore point
348 (goto-char opoint)
349 (if (< (point) itimer-edit-start-marker)
350 (goto-char itimer-edit-start-marker))
351 (setq buffer-read-only t)
352 (display-buffer buf)))
353
354 (defun edit-itimers ()
355 "Display a list of all itimers and select it for editing.
356 The major mode of the buffer containing the listing is Itimer Edit mode.
357 This major mode provides commands to manipulate itimers; see the documentation
358 for `itimer-edit-mode' for more information."
359 (interactive)
360 ;; since user is editing, make sure displayed data is reasonably up-to-date
361 ; (if itimer-process
362 ; (itimer-process-wakeup))
363 (list-itimers)
364 (select-window (get-buffer-window "*Itimer List*"))
365 (goto-char itimer-edit-start-marker)
366 (if itimer-list
367 (progn
368 (forward-sexp 2)
369 (backward-sexp)))
370 (message "type q to quit, ? for help"))
371
372 ;; no point in making this interactive.
373 (defun itimer-edit-mode ()
374 "Major mode for manipulating itimers.
375 Atrributes of running itimers are changed by moving the cursor to the
376 desired field and typing `s' to set that field. The field will then be
377 set to the value read from the minibuffer.
378
379 Commands:
380 TAB move forward a field
381 DEL move backward a field
382 s set a field
383 d delete the selected itimer
384 x start a new itimer
385 ? help"
386 (kill-all-local-variables)
387 (make-local-variable 'tab-stop-list)
388 (setq major-mode 'itimer-edit-mode
389 mode-name "Itimer Edit"
390 truncate-lines t
391 tab-stop-list '(22 32 42))
392 (abbrev-mode 0)
393 (auto-fill-mode 0)
394 (buffer-disable-undo (current-buffer))
395 (use-local-map itimer-edit-map)
396 (and lisp-mode-syntax-table (set-syntax-table lisp-mode-syntax-table)))
397
398 (put 'itimer-edit-mode 'mode-class 'special)
399
400 (defun itimer-edit-help ()
401 "Help function for Itimer Edit."
402 (interactive)
403 (if (eq last-command 'itimer-edit-help)
404 (describe-mode)
405 (message "TAB, DEL select fields, (s)et field, (d)elete itimer (type ? for more help)")))
406
407 (defun itimer-edit-quit ()
408 "End Itimer Edit."
409 (interactive)
410 (bury-buffer (current-buffer))
411 (if (one-window-p t)
412 (switch-to-buffer (other-buffer (current-buffer)))
413 (delete-window)))
414
415 (defun itimer-edit-set-field ()
416 (interactive)
417 ;; First two lines in list buffer are headers.
418 ;; Cry out against the luser who attempts to change a field there.
419 (if (<= (point) itimer-edit-start-marker)
420 (error ""))
421 ;; field-value must be initialized to be something other than a
422 ;; number, symbol, or list.
423 (let (itimer field (field-value ""))
424 (setq itimer (save-excursion
425 ;; read the name of the itimer from the beginning of
426 ;; the current line.
427 (beginning-of-line)
428 (get-itimer (read (current-buffer))))
429 field (save-excursion
430 (itimer-edit-beginning-of-field)
431 (let ((opoint (point))
432 (n 0))
433 ;; count the number of sexprs until we reach the cursor
434 ;; and use this info to determine which field the user
435 ;; wants to modify.
436 (beginning-of-line)
437 (while (and (>= opoint (point)) (< n 4))
438 (forward-sexp 2)
439 (backward-sexp)
440 (itimer-incf n))
441 (cond ((eq n 1) (error "Cannot change itimer name."))
442 ((eq n 2) 'value)
443 ((eq n 3) 'restart)
444 ((eq n 4) 'function)))))
445 (cond ((eq field 'value)
446 ;; XEmacs: rewritten for I18N3 snarfing
447 (while (not (natnump field-value))
448 (setq field-value (read-from-minibuffer "Set itimer value: "
449 nil nil t))))
450 ((eq field 'restart)
451 (while (and field-value (not (natnump field-value)))
452 (setq field-value (read-from-minibuffer "Set itimer restart: "
453 nil nil t))))
454 ((eq field 'function)
455 (while (not (or (and (symbolp field-value) (fboundp field-value))
456 (and (consp field-value)
457 (memq (car field-value) '(lambda macro)))))
458 (setq field-value
459 (read (completing-read "Set itimer function: "
460 obarray 'fboundp nil))))))
461 ;; set the itimer field
462 (funcall (intern (concat "set-itimer-" (symbol-name field)))
463 itimer field-value)
464 ;; move to beginning of field to be changed
465 (itimer-edit-beginning-of-field)
466 ;; modify the list buffer to reflect the change.
467 (let (buffer-read-only kill-ring)
468 (kill-sexp 1)
469 (kill-region (point) (progn (skip-chars-forward " \t") (point)))
470 (prin1 field-value (current-buffer))
471 (if (not (eolp))
472 (tab-to-tab-stop))
473 (backward-sexp))))
474
475 (defun itimer-edit-delete-itimer ()
476 (interactive)
477 ;; First two lines in list buffer are headers.
478 ;; Cry out against the luser who attempts to change a field there.
479 (if (<= (point) itimer-edit-start-marker)
480 (error ""))
481 (delete-itimer
482 (read-itimer "Delete itimer: "
483 (save-excursion (beginning-of-line) (read (current-buffer)))))
484 ;; update list information
485 (list-itimers))
486
487 (defun itimer-edit-next-field (count)
488 (interactive "p")
489 (itimer-edit-beginning-of-field)
490 (cond ((> (itimer-signum count) 0)
491 (while (not (zerop count))
492 (forward-sexp)
493 ;; wrap from eob to itimer-edit-start-marker
494 (if (eobp)
495 (progn
496 (goto-char itimer-edit-start-marker)
497 (forward-sexp)))
498 (forward-sexp)
499 (backward-sexp)
500 ;; treat fields at beginning of line as if they weren't there.
501 (if (bolp)
502 (progn
503 (forward-sexp 2)
504 (backward-sexp)))
505 (itimer-decf count)))
506 ((< (itimer-signum count) 0)
507 (while (not (zerop count))
508 (backward-sexp)
509 ;; treat fields at beginning of line as if they weren't there.
510 (if (bolp)
511 (backward-sexp))
512 ;; wrap from itimer-edit-start-marker to field at eob.
513 (if (<= (point) itimer-edit-start-marker)
514 (progn
515 (goto-char (point-max))
516 (backward-sexp)))
517 (itimer-incf count)))))
518
519 (defun itimer-edit-previous-field (count)
520 (interactive "p")
521 (itimer-edit-next-field (- count)))
522
523 (defun itimer-edit-beginning-of-field ()
524 (let ((forw-back (save-excursion (forward-sexp) (backward-sexp) (point)))
525 (back (save-excursion (backward-sexp) (point))))
526 (cond ((eq forw-back back) (backward-sexp))
527 ((eq forw-back (point)) t)
528 (t (backward-sexp)))))
529
530
531 ;; internals of the itimer implementation.
532
533 (defun itimer-process-filter (process string)
534 (error "itimer-process-filter is not used in XEmacs")
535 ; ;; If the itimer process dies and generates output while doing
536 ; ;; so, we may be called before the process-sentinel. Sanity
537 ; ;; check the output just in case...
538 ; (if (not (string-match "^[0-9]" string))
539 ; (progn (message "itimer process gave odd output: %s" string)
540 ; ;; it may be still alive and waiting for input
541 ; (process-send-string itimer-process "3\n"))
542 ; ;; if there are no active itimers, return quickly.
543 ; (if itimer-list
544 ; (let ((time-elapsed (string-to-int string))
545 ; (itimers itimer-list)
546 ; (itimer)
547 ; ;; process filters can be hit by stray C-g's from the user,
548 ; ;; so we must protect this stuff appropriately.
549 ; ;; Quit's are allowed from within itimer functions, but we
550 ; ;; catch them.
551 ; (inhibit-quit t))
552 ; (setq itimer-process-next-wakeup 600)
553 ; (while itimers
554 ; (setq itimer (car itimers))
555 ; (set-itimer-value itimer (max 0 (- (itimer-value itimer) time-elapsed)) t)
556 ; (if (> (itimer-value itimer) 0)
557 ; (setq itimer-process-next-wakeup
558 ; (min itimer-process-next-wakeup (itimer-value itimer)))
559 ; ;; itimer has expired, we must call its function.
560 ; ;; protect our local vars from the itimer function.
561 ; ;; allow keyboard quit to occur, but catch and report it.
562 ; ;; provide the variable `current-itimer' in case the function
563 ; ;; is interested.
564 ; (condition-case condition-data
565 ; (let* ((current-itimer itimer)
566 ; itimer itimers time-elapsed
567 ; quit-flag inhibit-quit)
568 ; (funcall (itimer-function current-itimer)))
569 ; (error (message "itimer \"%s\" signaled: %s" (itimer-name itimer)
570 ; (prin1-to-string condition-data)))
571 ; (quit (message "itimer \"%s\" quit" (itimer-name itimer))))
572 ; ;; restart the itimer if we should, otherwise delete it.
573 ; (if (null (itimer-restart itimer))
574 ; (delete-itimer itimer)
575 ; (set-itimer-value itimer (itimer-restart itimer) t)
576 ; (setq itimer-process-next-wakeup
577 ; (min itimer-process-next-wakeup (itimer-value itimer)))))
578 ; (setq itimers (cdr itimers)))
579 ; ;; if user is editing itimers, update displayed info
580 ; (if (eq major-mode 'itimer-edit-mode)
581 ; (list-itimers)))
582 ; (setq itimer-process-next-wakeup 600))
583 ; ;; tell itimer-process when to wakeup again
584 ; (process-send-string itimer-process
585 ; (concat (int-to-string itimer-process-next-wakeup)
586 ; "\n")))
587 )
588
589 (defun itimer-process-sentinel (process message)
590 (error "itimer-process-sentinel is not used in XEmacs")
591 ; (let ((inhibit-quit t))
592 ; (if (eq (process-status process) 'stop)
593 ; (continue-process process)
594 ; ;; not stopped, so it must have died.
595 ; ;; cleanup first...
596 ; (delete-process process)
597 ; (setq itimer-process nil)
598 ; ;; now, if there are any active itimers then we need to immediately
599 ; ;; start another itimer process, otherwise we can wait until the next
600 ; ;; start-itimer call, which will start one automatically.
601 ; (if (null itimer-list)
602 ; ()
603 ; ;; there may have been an error message in the echo area;
604 ; ;; give the user at least a little time to read it.
605 ; (sit-for 2)
606 ; (message "itimer process %s... respawning." (substring message 0 -1))
607 ; (itimer-process-start))))
608 )
609
610 (defun itimer-process-start ()
611 (error "itimer-process-start is not used in XEmacs")
612 ; (let ((inhibit-quit t)
613 ; (process-connection-type nil))
614 ; (setq itimer-process (start-process "itimer" nil "itimer"))
615 ; (process-kill-without-query itimer-process)
616 ; (set-process-filter itimer-process 'itimer-process-filter)
617 ; (set-process-sentinel itimer-process 'itimer-process-sentinel)
618 ; ;; Tell itimer process to wake up quickly, so that a correct wakeup
619 ; ;; time can be computed. Zero instead of one here loses because of
620 ; ;; underlying itimer implementations that use 0 to mean `disable the
621 ; ;; itimer'.
622 ; (setq itimer-process-next-wakeup 1)
623 ; (process-send-string itimer-process "1\n"))
624 )
625
626 (defun itimer-process-wakeup ()
627 (error "itimer-process-wakeup is not used in XEmacs")
628 ; (interrupt-process itimer-process)
629 ; (accept-process-output)
630 )
631
632
633 ;; XEmacs-specific code
634
635 (defun activate-itimer (itimer)
636 (let ((inhibit-quit t))
637 (set-itimer-id itimer
638 (add-timeout (itimer-value itimer)
639 'itimer-callback
640 itimer
641 (itimer-restart itimer))))
642 itimer)
643
644 (defun deactivate-itimer (itimer)
645 (let ((inhibit-quit t)
646 (id (itimer-id itimer)))
647 (and id (disable-timeout id))
648 (set-itimer-id itimer nil))
649 itimer)
650
651 (defun itimer-callback (current-itimer)
652 (funcall (itimer-function current-itimer)))
653
654
655 ;;; itimer-driven auto-saves
656
657 ;jwz: this is preloaded so don't ;;;###autoload
658 (defvar auto-save-timeout 30
659 "*Number of seconds idle time before auto-save.
660 Zero or nil means disable auto-saving due to idleness.
661
662 The actual amount of idle time between auto-saves is logarithmically related
663 to the size of the current buffer. This variable is the number of seconds
664 after which an auto-save will happen when the current buffer is 50k or less;
665 the timeout will be 2 1/4 times this in a 200k buffer, 3 3/4 times this in a
666 1000k buffer, and 4 1/2 times this in a 2000k buffer.
667
668 See also the variable `auto-save-interval', which controls auto-saving based
669 on the number of characters typed.")
670
671 ;jwz: this is preloaded so don't ;;;###autoload
672 (defvar auto-gc-threshold (/ gc-cons-threshold 3)
673 "*GC when this many bytes have been consed since the last GC,
674 and the user has been idle for `auto-save-timeout' seconds.")
675
676 (defun auto-save-itimer ()
677 "For use as a itimer callback function.
678 Auto-saves and garbage-collects based on the size of the current buffer
679 and the value of `auto-save-timeout', `auto-gc-threshold', and the current
680 keyboard idle-time."
681 (if (or (null auto-save-timeout)
682 (<= auto-save-timeout 0)
683 (eq (minibuffer-window) (selected-window)))
684 nil
685 (let ((buf-size (1+ (ash (buffer-size) -8)))
686 (delay-level 0)
687 (now (current-time))
688 delay)
689 (while (> buf-size 64)
690 (setq delay-level (1+ delay-level)
691 buf-size (- buf-size (ash buf-size -2))))
692 (if (< delay-level 4)
693 (setq delay-level 4))
694 ;; delay_level is 4 for files under around 50k, 7 at 100k, 9 at 200k,
695 ;; 11 at 300k, and 12 at 500k, 15 at 1 meg, and 17 at 2 meg.
696 (setq delay (/ (* delay-level auto-save-timeout) 4))
697 (let ((idle-time (if (or (not (consp last-input-time))
698 (/= (car now) (car last-input-time)))
699 (1+ delay)
700 (- (car (cdr now)) (cdr last-input-time)))))
701 (and (> idle-time delay)
702 (do-auto-save))
703 (and (> idle-time auto-save-timeout)
704 (> (consing-since-gc) auto-gc-threshold)
705 (garbage-collect)))))
706 ;; Look at the itimer that's currently running; if the user has changed
707 ;; the value of auto-save-timeout, modify this itimer to have the correct
708 ;; restart time. There will be some latency between when the user changes
709 ;; this variable and when it takes effect, but it will happen eventually.
710 (let ((self (get-itimer "auto-save")))
711 (or self (error "auto-save-itimer can't find itself"))
712 (if (and auto-save-timeout (> auto-save-timeout 4))
713 (or (= (itimer-restart self) (/ auto-save-timeout 4))
714 (set-itimer-restart self (/ auto-save-timeout 4)))))
715 nil)
716
717 (defun itimer-init-auto-gc ()
718 (or noninteractive ; may be being run from after-init-hook in -batch mode.
719 (get-itimer "auto-save")
720 ;; the time here is just the first interval; if the user changes it
721 ;; later, it will adjust.
722 (let ((time (max 2 (/ (or auto-save-timeout 30) 4))))
723 (start-itimer "auto-save" 'auto-save-itimer time time))))
724
725 (cond (purify-flag
726 ;; This file is being preloaded into an emacs about to be dumped.
727 ;; So arrange for the auto-save itimer to be started once emacs
728 ;; is launched.
729 (add-hook 'after-init-hook 'itimer-init-auto-gc))
730 (t
731 ;; Otherwise, this file is being loaded into a normal, interactive
732 ;; emacs. Start the auto-save timer now.
733 (itimer-init-auto-gc)))
734
735
736 (provide 'itimer)