(insert_1_both, insert_from_string_1)
[bpt/emacs.git] / lisp / play / zone.el
CommitLineData
abb2db1c
GM
1;;; zone.el --- idle display hacks
2
b0fa1513 3;; Copyright (C) 2000, 2001 Free Software Foundation, Inc.
abb2db1c 4
60370d40
PJ
5;; Author: Victor Zandy <zandy@cs.wisc.edu>
6;; Maintainer: Thien-Thi Nguyen <ttn@gnu.org>
7;; Keywords: games
8;; Created: June 6, 1998
abb2db1c
GM
9
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software; you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation; either version 2, or (at your option)
15;; any later version.
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
23;; along with GNU Emacs; see the file COPYING. If not, write to the
24;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25;; Boston, MA 02111-1307, USA.
26
27;;; Commentary:
28
29;; Don't zone out in front of Emacs! Try M-x zone.
30;; If it eventually irritates you, try M-x zone-leave-me-alone.
31
32;; Bored by the zone pyrotechnics? Write your own! Add it to
df9d055e 33;; `zone-programs'. See `zone-call' for higher-ordered zoning.
abb2db1c
GM
34
35;; WARNING: Not appropriate for Emacs sessions over modems or
36;; computers as slow as mine.
37
38;; THANKS: Christopher Mayer, Scott Flinchbaugh, Rachel Kalmar,
df9d055e 39;; Max Froumentin.
abb2db1c
GM
40
41;;; Code:
42
43(require 'timer)
44(require 'tabify)
45(eval-when-compile (require 'cl))
46
abb2db1c
GM
47(defvar zone-idle 20
48 "*Seconds to idle before zoning out.")
49
df9d055e
TTN
50(defvar zone-timeout nil
51 "*Seconds to timeout the zoning.
52If nil, don't interrupt for about 1^26 seconds.")
53
abb2db1c
GM
54;; Vector of functions that zone out. `zone' will execute one of
55;; these functions, randomly chosen. The chosen function is invoked
56;; in the *zone* buffer, which contains the text of the selected
57;; window. If the function loops, it *must* periodically check and
58;; halt if `input-pending-p' is t (because quitting is disabled when
59;; Emacs idle timers are run).
60(defvar zone-programs [
61 zone-pgm-jitter
62 zone-pgm-putz-with-case
63 zone-pgm-dissolve
df9d055e 64 ;; zone-pgm-explode
abb2db1c
GM
65 zone-pgm-whack-chars
66 zone-pgm-rotate
67 zone-pgm-rotate-LR-lockstep
68 zone-pgm-rotate-RL-lockstep
69 zone-pgm-rotate-LR-variable
70 zone-pgm-rotate-RL-variable
71 zone-pgm-drip
72 zone-pgm-drip-fretfully
73 zone-pgm-five-oclock-swan-dive
74 zone-pgm-martini-swan-dive
75 zone-pgm-paragraph-spaz
76 zone-pgm-stress
df9d055e 77 zone-pgm-stress-destress
abb2db1c
GM
78 ])
79
80(defmacro zone-orig (&rest body)
81 `(with-current-buffer (get 'zone 'orig-buffer)
82 ,@body))
83
df9d055e
TTN
84(defmacro zone-hiding-modeline (&rest body)
85 `(let (bg mode-line-fg mode-line-bg mode-line-box)
86 (unwind-protect
87 (progn
88 (when (and (= 0 (get 'zone 'modeline-hidden-level))
89 (display-color-p))
90 (setq bg (face-background 'default)
91 mode-line-box (face-attribute 'mode-line :box)
92 mode-line-fg (face-attribute 'mode-line :foreground)
93 mode-line-bg (face-attribute 'mode-line :background))
94 (set-face-attribute 'mode-line nil
95 :foreground bg
96 :background bg
97 :box nil))
98 (put 'zone 'modeline-hidden-level
99 (1+ (get 'zone 'modeline-hidden-level)))
100 ,@body)
101 (put 'zone 'modeline-hidden-level
102 (1- (get 'zone 'modeline-hidden-level)))
103 (when (and (> 1 (get 'zone 'modeline-hidden-level))
104 mode-line-fg)
105 (set-face-attribute 'mode-line nil
106 :foreground mode-line-fg
107 :background mode-line-bg
108 :box mode-line-box)))))
109
110(defun zone-call (program &optional timeout)
111 "Call PROGRAM in a zoned way.
112If PROGRAM is a function, call it, interrupting after the amount
113 of time in seconds specified by optional arg TIMEOUT, or `zone-timeout'
114 if unspecified, q.v.
115PROGRAM can also be a list of elements, which are interpreted like so:
116If the element is a function or a list of a function and a number,
117 apply `zone-call' recursively."
118 (cond ((functionp program)
119 (with-timeout ((or timeout zone-timeout (ash 1 26)))
120 (funcall program)))
121 ((listp program)
122 (mapcar (lambda (elem)
123 (cond ((functionp elem) (zone-call elem))
124 ((and (listp elem)
125 (functionp (car elem))
126 (numberp (cadr elem)))
127 (apply 'zone-call elem))
128 (t (error "bad `zone-call' elem:" elem))))
129 program))))
130
abb2db1c
GM
131;;;###autoload
132(defun zone ()
133 "Zone out, completely."
134 (interactive)
930baf47
TTN
135 (let ((timer (get 'zone 'timer)))
136 (and (timerp timer) (cancel-timer timer)))
137 (put 'zone 'timer nil)
2e78d4ab 138 (let ((f (selected-frame))
abb2db1c 139 (outbuf (get-buffer-create "*zone*"))
930baf47
TTN
140 (text (buffer-substring (window-start) (window-end)))
141 (wp (1+ (- (window-point (selected-window))
142 (window-start)))))
abb2db1c 143 (put 'zone 'orig-buffer (current-buffer))
df9d055e 144 (put 'zone 'modeline-hidden-level 0)
abb2db1c
GM
145 (set-buffer outbuf)
146 (setq mode-name "Zone")
147 (erase-buffer)
148 (insert text)
149 (switch-to-buffer outbuf)
150 (setq buffer-undo-list t)
151 (untabify (point-min) (point-max))
152 (set-window-start (selected-window) (point-min))
153 (set-window-point (selected-window) wp)
154 (sit-for 0 500)
155 (let ((pgm (elt zone-programs (random (length zone-programs))))
156 (ct (and f (frame-parameter f 'cursor-type))))
157 (when ct (modify-frame-parameters f '((cursor-type . (bar . 0)))))
158 (condition-case nil
930baf47 159 (progn
abb2db1c 160 (message "Zoning... (%s)" pgm)
930baf47
TTN
161 (garbage-collect)
162 ;; If some input is pending, zone says "sorry", which
163 ;; isn't nice; this might happen e.g. when they invoke the
164 ;; game by clicking the menu bar. So discard any pending
165 ;; input before zoning out.
166 (if (input-pending-p)
167 (discard-input))
df9d055e 168 (zone-call pgm)
930baf47
TTN
169 (message "Zoning...sorry"))
170 (error
171 (while (not (input-pending-p))
172 (message (format "We were zoning when we wrote %s..." pgm))
173 (sit-for 3)
174 (message "...here's hoping we didn't hose your buffer!")
175 (sit-for 3)))
176 (quit (ding) (message "Zoning...sorry")))
abb2db1c
GM
177 (when ct (modify-frame-parameters f (list (cons 'cursor-type ct)))))
178 (kill-buffer outbuf)
179 (zone-when-idle zone-idle)))
180
181;;;; Zone when idle, or not.
182
abb2db1c
GM
183(defun zone-when-idle (secs)
184 "Zone out when Emacs has been idle for SECS seconds."
185 (interactive "nHow long before I start zoning (seconds): ")
186 (or (<= secs 0)
930baf47
TTN
187 (let ((timer (get 'zone 'timer)))
188 (or (eq timer t)
189 (timerp timer)))
190 (put 'zone 'timer (run-with-idle-timer secs t 'zone))))
abb2db1c
GM
191
192(defun zone-leave-me-alone ()
193 "Don't zone out when Emacs is idle."
194 (interactive)
930baf47
TTN
195 (let ((timer (get 'zone 'timer)))
196 (and (timerp timer) (cancel-timer timer)))
197 (put 'zone 'timer t)
abb2db1c
GM
198 (message "I won't zone out any more"))
199
200
201;;;; zone-pgm-jitter
202
203(defun zone-shift-up ()
204 (let* ((b (point))
df9d055e
TTN
205 (e (progn
206 (end-of-line)
207 (if (looking-at "\n") (1+ (point)) (point))))
208 (s (buffer-substring b e)))
abb2db1c
GM
209 (delete-region b e)
210 (goto-char (point-max))
211 (insert s)))
212
213(defun zone-shift-down ()
214 (goto-char (point-max))
215 (forward-line -1)
216 (beginning-of-line)
217 (let* ((b (point))
df9d055e
TTN
218 (e (progn
219 (end-of-line)
220 (if (looking-at "\n") (1+ (point)) (point))))
221 (s (buffer-substring b e)))
abb2db1c
GM
222 (delete-region b e)
223 (goto-char (point-min))
224 (insert s)))
225
226(defun zone-shift-left ()
227 (while (not (eobp))
228 (or (eolp)
df9d055e
TTN
229 (let ((c (following-char)))
230 (delete-char 1)
231 (end-of-line)
232 (insert c)))
abb2db1c
GM
233 (forward-line 1)))
234
235(defun zone-shift-right ()
236 (while (not (eobp))
237 (end-of-line)
238 (or (bolp)
df9d055e
TTN
239 (let ((c (preceding-char)))
240 (delete-backward-char 1)
241 (beginning-of-line)
242 (insert c)))
abb2db1c
GM
243 (forward-line 1)))
244
245(defun zone-pgm-jitter ()
246 (let ((ops [
247 zone-shift-left
248 zone-shift-left
249 zone-shift-left
250 zone-shift-left
251 zone-shift-right
252 zone-shift-down
253 zone-shift-down
254 zone-shift-down
255 zone-shift-down
256 zone-shift-down
257 zone-shift-up
258 ]))
259 (goto-char (point-min))
260 (while (not (input-pending-p))
261 (funcall (elt ops (random (length ops))))
262 (goto-char (point-min))
263 (sit-for 0 10))))
264
265
266;;;; zone-pgm-whack-chars
267
abb2db1c 268(defun zone-pgm-whack-chars ()
930baf47 269 (let ((tbl (copy-sequence (get 'zone-pgm-whack-chars 'wc-tbl))))
abb2db1c
GM
270 (while (not (input-pending-p))
271 (let ((i 48))
df9d055e
TTN
272 (while (< i 122)
273 (aset tbl i (+ 48 (random (- 123 48))))
274 (setq i (1+ i)))
275 (translate-region (point-min) (point-max) tbl)
276 (sit-for 0 2)))))
930baf47
TTN
277
278(put 'zone-pgm-whack-chars 'wc-tbl
df9d055e 279 (let ((tbl (make-string 128 ?x))
930baf47
TTN
280 (i 0))
281 (while (< i 128)
282 (aset tbl i i)
283 (setq i (1+ i)))
284 tbl))
abb2db1c
GM
285
286;;;; zone-pgm-dissolve
287
288(defun zone-remove-text ()
289 (let ((working t))
290 (while working
291 (setq working nil)
292 (save-excursion
df9d055e
TTN
293 (goto-char (point-min))
294 (while (not (eobp))
295 (if (looking-at "[^(){}\n\t ]")
296 (let ((n (random 5)))
297 (if (not (= n 0))
298 (progn
299 (setq working t)
300 (forward-char 1))
301 (delete-char 1)
302 (insert " ")))
303 (forward-char 1))))
abb2db1c
GM
304 (sit-for 0 2))))
305
306(defun zone-pgm-dissolve ()
307 (zone-remove-text)
308 (zone-pgm-jitter))
309
310
311;;;; zone-pgm-explode
312
313(defun zone-exploding-remove ()
314 (let ((i 0))
315 (while (< i 20)
316 (save-excursion
df9d055e
TTN
317 (goto-char (point-min))
318 (while (not (eobp))
319 (if (looking-at "[^*\n\t ]")
320 (let ((n (random 5)))
321 (if (not (= n 0))
322 (forward-char 1))
323 (insert " ")))
324 (forward-char 1)))
abb2db1c
GM
325 (setq i (1+ i))
326 (sit-for 0 2)))
327 (zone-pgm-jitter))
328
329(defun zone-pgm-explode ()
330 (zone-exploding-remove)
331 (zone-pgm-jitter))
332
333
334;;;; zone-pgm-putz-with-case
335
336;; Faster than `zone-pgm-putz-with-case', but not as good: all
337;; instances of the same letter have the same case, which produces a
338;; less interesting effect than you might imagine.
339(defun zone-pgm-2nd-putz-with-case ()
340 (let ((tbl (make-string 128 ?x))
df9d055e 341 (i 0))
abb2db1c
GM
342 (while (< i 128)
343 (aset tbl i i)
344 (setq i (1+ i)))
345 (while (not (input-pending-p))
346 (setq i ?a)
347 (while (<= i ?z)
df9d055e
TTN
348 (aset tbl i
349 (if (zerop (random 5))
350 (upcase i)
351 (downcase i)))
352 (setq i (+ i (1+ (random 5)))))
abb2db1c
GM
353 (setq i ?A)
354 (while (<= i ?z)
df9d055e
TTN
355 (aset tbl i
356 (if (zerop (random 5))
357 (downcase i)
358 (upcase i)))
359 (setq i (+ i (1+ (random 5)))))
abb2db1c
GM
360 (translate-region (point-min) (point-max) tbl)
361 (sit-for 0 2))))
362
363(defun zone-pgm-putz-with-case ()
364 (goto-char (point-min))
365 (while (not (input-pending-p))
366 (let ((np (+ 2 (random 5)))
df9d055e 367 (pm (point-max)))
abb2db1c 368 (while (< np pm)
df9d055e 369 (goto-char np)
abb2db1c
GM
370 (let ((prec (preceding-char))
371 (props (text-properties-at (1- (point)))))
372 (insert (if (zerop (random 2))
373 (upcase prec)
374 (downcase prec)))
375 (set-text-properties (1- (point)) (point) props))
df9d055e
TTN
376 (backward-char 2)
377 (delete-char 1)
378 (setq np (+ np (1+ (random 5))))))
abb2db1c
GM
379 (goto-char (point-min))
380 (sit-for 0 2)))
381
382
383;;;; zone-pgm-rotate
384
385(defun zone-line-specs ()
386 (let (ret)
387 (save-excursion
388 (goto-char (window-start))
389 (while (< (point) (window-end))
df9d055e
TTN
390 (when (looking-at "[\t ]*\\([^\n]+\\)")
391 (setq ret (cons (cons (match-beginning 1) (match-end 1)) ret)))
392 (forward-line 1)))
abb2db1c
GM
393 ret))
394
395(defun zone-pgm-rotate (&optional random-style)
396 (let* ((specs (apply
930baf47 397 'vector
abb2db1c
GM
398 (let (res)
399 (mapcar (lambda (ent)
400 (let* ((beg (car ent))
401 (end (cdr ent))
402 (amt (if random-style
403 (funcall random-style)
404 (- (random 7) 3))))
405 (when (< (- end (abs amt)) beg)
406 (setq amt (random (- end beg))))
407 (unless (= 0 amt)
408 (setq res
409 (cons
410 (vector amt beg (- end (abs amt)))
411 res)))))
412 (zone-line-specs))
413 res)))
930baf47
TTN
414 (n (length specs))
415 amt aamt cut paste txt i ent)
abb2db1c
GM
416 (while (not (input-pending-p))
417 (setq i 0)
418 (while (< i n)
930baf47
TTN
419 (setq ent (aref specs i))
420 (setq amt (aref ent 0) aamt (abs amt))
421 (if (> 0 amt)
422 (setq cut 1 paste 2)
423 (setq cut 2 paste 1))
424 (goto-char (aref ent cut))
425 (setq txt (buffer-substring (point) (+ (point) aamt)))
426 (delete-char aamt)
427 (goto-char (aref ent paste))
428 (insert txt)
429 (setq i (1+ i)))
abb2db1c
GM
430 (sit-for 0.04))))
431
432(defun zone-pgm-rotate-LR-lockstep ()
433 (zone-pgm-rotate (lambda () 1)))
434
435(defun zone-pgm-rotate-RL-lockstep ()
436 (zone-pgm-rotate (lambda () -1)))
437
438(defun zone-pgm-rotate-LR-variable ()
439 (zone-pgm-rotate (lambda () (1+ (random 3)))))
440
441(defun zone-pgm-rotate-RL-variable ()
442 (zone-pgm-rotate (lambda () (1- (- (random 3))))))
443
444
445;;;; zone-pgm-drip
446
447(defun zone-cpos (pos)
448 (buffer-substring pos (1+ pos)))
449
450(defun zone-fret (pos)
451 (let* ((case-fold-search nil)
452 (c-string (zone-cpos pos))
453 (hmm (cond
454 ((string-match "[a-z]" c-string) (upcase c-string))
455 ((string-match "[A-Z]" c-string) (downcase c-string))
456 (t " "))))
457 (do ((i 0 (1+ i))
458 (wait 0.5 (* wait 0.8)))
459 ((= i 20))
460 (goto-char pos)
461 (delete-char 1)
462 (insert (if (= 0 (% i 2)) hmm c-string))
463 (sit-for wait))
464 (delete-char -1) (insert c-string)))
465
466(defun zone-fall-through-ws (c col wend)
467 (let ((fall-p nil) ; todo: move outward
468 (wait 0.15)
df9d055e 469 (o (point)) ; for terminals w/o cursor hiding
abb2db1c
GM
470 (p (point)))
471 (while (progn
472 (forward-line 1)
473 (move-to-column col)
474 (looking-at " "))
475 (setq fall-p t)
476 (delete-char 1)
477 (insert (if (< (point) wend) c " "))
478 (save-excursion
479 (goto-char p)
480 (delete-char 1)
481 (insert " ")
482 (goto-char o)
483 (sit-for (setq wait (* wait 0.8))))
484 (setq p (1- (point))))
485 fall-p))
486
487(defun zone-pgm-drip (&optional fret-p pancake-p)
488 (let* ((ww (1- (window-width)))
489 (wh (window-height))
490 (mc 0) ; miss count
491 (total (* ww wh))
492 (fall-p nil))
493 (goto-char (point-min))
494 ;; fill out rectangular ws block
495 (while (not (eobp))
496 (end-of-line)
497 (let ((cc (current-column)))
498 (if (< cc ww)
499 (insert (make-string (- ww cc) ? ))
500 (delete-char (- ww cc))))
501 (unless (eobp)
502 (forward-char 1)))
df9d055e 503 ;; pad ws past bottom of screen
abb2db1c
GM
504 (let ((nl (- wh (count-lines (point-min) (point)))))
505 (when (> nl 0)
506 (let ((line (concat (make-string (1- ww) ? ) "\n")))
507 (do ((i 0 (1+ i)))
508 ((= i nl))
509 (insert line)))))
df9d055e 510 (catch 'done
abb2db1c
GM
511 (while (not (input-pending-p))
512 (goto-char (point-min))
513 (sit-for 0)
514 (let ((wbeg (window-start))
515 (wend (window-end)))
516 (setq mc 0)
517 ;; select non-ws character, but don't miss too much
518 (goto-char (+ wbeg (random (- wend wbeg))))
519 (while (looking-at "[ \n\f]")
520 (if (= total (setq mc (1+ mc)))
521 (throw 'done 'sel)
522 (goto-char (+ wbeg (random (- wend wbeg))))))
523 ;; character animation sequence
524 (let ((p (point)))
525 (when fret-p (zone-fret p))
526 (goto-char p)
527 (setq fall-p (zone-fall-through-ws
528 (zone-cpos p) (current-column) wend))))
529 ;; assuming current-column has not changed...
530 (when (and pancake-p
531 fall-p
532 (< (count-lines (point-min) (point))
533 wh))
534 (previous-line 1)
535 (forward-char 1)
536 (sit-for 0.137)
537 (delete-char -1)
538 (insert "@")
539 (sit-for 0.137)
540 (delete-char -1)
541 (insert "*")
542 (sit-for 0.137)
543 (delete-char -1)
544 (insert "_"))))))
545
546(defun zone-pgm-drip-fretfully ()
547 (zone-pgm-drip t))
548
549(defun zone-pgm-five-oclock-swan-dive ()
550 (zone-pgm-drip nil t))
551
552(defun zone-pgm-martini-swan-dive ()
553 (zone-pgm-drip t t))
554
555
556;;;; zone-pgm-paragraph-spaz
557
558(defun zone-pgm-paragraph-spaz ()
559 (if (memq (zone-orig major-mode) '(text-mode fundamental-mode))
560 (let ((fill-column fill-column)
561 (fc-min fill-column)
562 (fc-max fill-column)
563 (max-fc (1- (frame-width))))
564 (while (sit-for 0.1)
565 (fill-paragraph 1)
566 (setq fill-column (+ fill-column (- (random 5) 2)))
567 (when (< fill-column fc-min)
568 (setq fc-min fill-column))
569 (when (> fill-column max-fc)
570 (setq fill-column max-fc))
571 (when (> fill-column fc-max)
572 (setq fc-max fill-column))))
573 (message "Zoning... (zone-pgm-rotate)")
574 (zone-pgm-rotate)))
575
576
577;;;; zone-pgm-stress
578
579(defun zone-pgm-stress ()
580 (goto-char (point-min))
df9d055e 581 (let (lines)
abb2db1c
GM
582 (while (< (point) (point-max))
583 (let ((p (point)))
584 (forward-line 1)
585 (setq lines (cons (buffer-substring p (point)) lines))))
586 (sit-for 5)
df9d055e
TTN
587 (zone-hiding-modeline
588 (let ((msg "Zoning... (zone-pgm-stress)"))
589 (while (not (string= msg ""))
590 (message (setq msg (substring msg 1)))
591 (sit-for 0.05)))
592 (while (not (input-pending-p))
593 (when (< 50 (random 100))
594 (goto-char (point-max))
595 (forward-line -1)
596 (let ((kill-whole-line t))
597 (kill-line))
598 (goto-char (point-min))
599 (insert (nth (random (length lines)) lines)))
600 (message (concat (make-string (random (- (frame-width) 5)) ? ) "grrr"))
601 (sit-for 0.1)))))
602
603
604;;;; zone-pgm-stress-destress
605
606(defun zone-pgm-stress-destress ()
607 (zone-call 'zone-pgm-stress 25)
608 (zone-hiding-modeline
609 (sit-for 3)
610 (erase-buffer)
611 (sit-for 3)
612 (insert-buffer "*Messages*")
613 (message "")
614 (goto-char (point-max))
615 (recenter -1)
616 (sit-for 3)
617 (delete-region (point-min) (window-start))
618 (message "hey why stress out anyway?")
619 (zone-call '((zone-pgm-rotate 30)
620 (zone-pgm-whack-chars 10)
621 zone-pgm-drip))))
622
623
624;;;;;;;;;;;;;;;
abb2db1c
GM
625(provide 'zone)
626
627;;; zone.el ends here