X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/00ed33e7ab1f430e43aeff27c3aa767590b2207e..3ff786247389f0eb280defb0389d690ee3610bf8:/lisp/avoid.el diff --git a/lisp/avoid.el b/lisp/avoid.el index 48f0914f16..adfb1dd78c 100644 --- a/lisp/avoid.el +++ b/lisp/avoid.el @@ -1,16 +1,17 @@ ;;; avoid.el --- make mouse pointer stay out of the way of editing -;;; Copyright (C) 1993, 1994 Free Software Foundation, Inc. +;; Copyright (C) 1993, 1994, 2000, 2001, 2002, 2003, 2004, +;; 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. -;; Author: Boris Goldowsky +;; Author: Boris Goldowsky ;; Keywords: mouse ;; This file is part of GNU Emacs. -;; GNU Emacs is free software; you can redistribute it and/or modify +;; GNU Emacs 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. +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -18,28 +19,28 @@ ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, -;; Boston, MA 02111-1307, USA. +;; along with GNU Emacs. If not, see . ;;; Commentary: ;; For those who are annoyed by the mouse pointer obscuring text, ;; this mode moves the mouse pointer - either just a little out of -;; the way, or all the way to the corner of the frame. +;; the way, or all the way to the corner of the frame. ;; To use, load or evaluate this file and type M-x mouse-avoidance-mode . -;; To set up permanently, put the following in your .emacs: +;; To set up permanently, put the following in your .emacs: ;; -;; (if window-system (mouse-avoidance-mode 'animate)) +;; (if (display-mouse-p) (mouse-avoidance-mode 'animate)) ;; -;; The 'animate can be 'jump or 'banish or 'exile or 'protean if you prefer. +;; Other legitimate alternatives include +;; `banish', `exile', `jump', `cat-and-mouse', and `proteus'. +;; They do somewhat different things. ;; See the documentation for function `mouse-avoidance-mode' for ;; details of the different modes. ;; ;; For added silliness, make the animatee animate... ;; put something similar to the following into your .emacs: ;; -;; (if window-system +;; (if (eq window-system 'x) ;; (mouse-avoidance-set-pointer-shape ;; (eval (nth (random 4) ;; '(x-pointer-man x-pointer-spider @@ -47,17 +48,17 @@ ;; ;; For completely random pointer shape, replace the setq above with: ;; (setq x-pointer-shape (mouse-avoidance-random-shape)) -;; +;; ;; Bugs / Warnings / To-Do: ;; -;; - Using this code does slow emacs down. "banish" mode shouldn't +;; - Using this code does slow Emacs down. "banish" mode shouldn't ;; be too bad, and on my workstation even "animate" is reasonable. ;; ;; - It ought to find out where any overlapping frames are and avoid them, ;; rather than always raising the frame. ;; Credits: -;; This code was helped by all those who contributed suggestions, +;; This code was helped by all those who contributed suggestions, ;; fixes, and additions ;; Joe Harrington (and his advisor), for the original inspiration. ;; Ken Manheimer, for dreaming up the Protean mode. @@ -74,22 +75,33 @@ :prefix "mouse-avoidance-" :group 'mouse) +;;;###autoload +(defcustom mouse-avoidance-mode nil + "Activate mouse avoidance mode. +See function `mouse-avoidance-mode' for possible values. +Setting this variable directly does not take effect; +use either \\[customize] or the function `mouse-avoidance-mode'." + :set (lambda (symbol value) + ;; 'none below prevents toggling when value is nil. + (mouse-avoidance-mode (or value 'none))) + :initialize 'custom-initialize-default + :type '(choice (const :tag "none" nil) (const banish) (const jump) + (const animate) (const exile) (const proteus) + ) + :group 'avoid + :require 'avoid + :version "20.3") -(defvar mouse-avoidance-mode nil - "Value is t or a symbol if the mouse pointer should avoid the cursor. -See function `mouse-avoidance-mode' for possible values. Changing this -variable is NOT the recommended way to change modes; use that function -instead.") (defcustom mouse-avoidance-nudge-dist 15 - "*Average distance that mouse will be moved when approached by cursor. -Only applies in mouse-avoidance-mode `jump' and its derivatives. + "Average distance that mouse will be moved when approached by cursor. +Only applies in Mouse-Avoidance mode `jump' and its derivatives. For best results make this larger than `mouse-avoidance-threshold'." :type 'integer :group 'avoid) (defcustom mouse-avoidance-nudge-var 10 - "*Variability of `mouse-avoidance-nudge-dist' (which see)." + "Variability of `mouse-avoidance-nudge-dist' (which see)." :type 'integer :group 'avoid) @@ -99,7 +111,7 @@ For best results make this larger than `mouse-avoidance-threshold'." :group 'avoid) (defcustom mouse-avoidance-threshold 5 - "*Mouse-pointer's flight distance. + "Mouse-pointer's flight distance. If the cursor gets closer than this, the mouse pointer will move away. Only applies in mouse-avoidance-modes `animate' and `jump'." :type 'integer @@ -110,38 +122,33 @@ Only applies in mouse-avoidance-modes `animate' and `jump'." (defvar mouse-avoidance-pointer-shapes nil) (defvar mouse-avoidance-n-pointer-shapes 0) (defvar mouse-avoidance-old-pointer-shape nil) +(defvar mouse-avoidance-animating-pointer nil) + +;; This timer is used to run something when Emacs is idle. +(defvar mouse-avoidance-timer nil) ;;; Functions: (defsubst mouse-avoidance-set-pointer-shape (shape) "Set the shape of the mouse pointer to SHAPE." - (setq x-pointer-shape shape) - (set-mouse-color nil)) + (when (boundp 'x-pointer-shape) + (setq x-pointer-shape shape) + (set-mouse-color nil))) (defun mouse-avoidance-point-position () "Return the position of point as (FRAME X . Y). -Analogous to mouse-position." - (let* ((w (selected-window)) - (edges (window-edges w)) - (list - (compute-motion (max (window-start w) (point-min)) ; start pos - ;; window-start can be < point-min if the - ;; latter has changed since the last redisplay - '(0 . 0) ; start XY - (point) ; stop pos - (cons (window-width) (window-height)); stop XY: none - (1- (window-width)) ; width - (cons (window-hscroll w) 0) ; 0 may not be right? - (selected-window)))) - ;; compute-motion returns (pos HPOS VPOS prevhpos contin) - ;; we want: (frame hpos . vpos) +Analogous to `mouse-position'." + (let ((edges (window-inside-edges)) + (x-y (posn-x-y (posn-at-point)))) (cons (selected-frame) - (cons (+ (car edges) (car (cdr list))) - (+ (car (cdr edges)) (car (cdr (cdr list)))))))) + (cons (+ (car edges) + (/ (car x-y) (frame-char-width))) + (+ (car (cdr edges)) + (/ (cdr x-y) (frame-char-height))))))) ;(defun mouse-avoidance-point-position-test () ; (interactive) -; (message (format "point=%s mouse=%s" +; (message (format "point=%s mouse=%s" ; (cdr (mouse-avoidance-point-position)) ; (cdr (mouse-position))))) @@ -155,23 +162,33 @@ Analogous to mouse-position." (raise-frame f) (set-mouse-position f (car pos) (cdr pos)) t)) - + (defun mouse-avoidance-too-close-p (mouse) - ;; Return t if mouse pointer and point cursor are too close. - ;; Acceptable distance is defined by mouse-avoidance-threshold. - (let ((point (mouse-avoidance-point-position))) - (and (eq (car mouse) (car point)) - (car (cdr mouse)) - (< (abs (- (car (cdr mouse)) (car (cdr point)))) - mouse-avoidance-threshold) - (< (abs (- (cdr (cdr mouse)) (cdr (cdr point)))) - mouse-avoidance-threshold)))) + "Return t if mouse pointer and point cursor are too close. +MOUSE is the current mouse position as returned by `mouse-position'. +Acceptable distance is defined by `mouse-avoidance-threshold'." + (let* ((frame (car mouse)) + (mouse-y (cdr (cdr mouse))) + (tool-bar-lines (frame-parameter nil 'tool-bar-lines))) + (or tool-bar-lines + (setq tool-bar-lines 0)) + (if (and mouse-y (< mouse-y tool-bar-lines)) + nil + (let ((point (mouse-avoidance-point-position)) + (mouse-x (car (cdr mouse)))) + (and (eq frame (car point)) + (not (null mouse-x)) + (< (abs (- mouse-x (car (cdr point)))) + mouse-avoidance-threshold) + (< (abs (- mouse-y (cdr (cdr point)))) + mouse-avoidance-threshold)))))) (defun mouse-avoidance-banish-destination () - "The position to which mouse-avoidance-mode `banish' moves the mouse. + "The position to which Mouse-Avoidance mode `banish' moves the mouse. You can redefine this if you want the mouse banished to a different corner." - (cons (1- (frame-width)) - 0)) + (let* ((pos (window-edges))) + (cons (- (nth 2 pos) 2) + (nth 1 pos)))) (defun mouse-avoidance-banish-mouse () ;; Put the mouse pointer in the upper-right corner of the current frame. @@ -198,38 +215,46 @@ You can redefine this if you want the mouse banished to a different corner." ((or R1 L2)) (t 0)))) -(defun mouse-avoidance-nudge-mouse () - ;; Push the mouse a little way away, possibly animating the move +(defun mouse-avoidance-nudge-mouse () + ;; Push the mouse a little way away, possibly animating the move. ;; For these modes, state keeps track of the total offset that we've ;; accumulated, and tries to keep it close to zero. (let* ((cur (mouse-position)) (cur-frame (car cur)) (cur-pos (cdr cur)) - (deltax (mouse-avoidance-delta + (pos (window-edges)) + (wleft (pop pos)) + (wtop (pop pos)) + (wright (pop pos)) + (wbot (pop pos)) + (deltax (mouse-avoidance-delta (car cur-pos) (- (random mouse-avoidance-nudge-var) (car mouse-avoidance-state)) mouse-avoidance-nudge-dist mouse-avoidance-nudge-var - 0 (frame-width))) - (deltay (mouse-avoidance-delta + wleft (1- wright))) + (deltay (mouse-avoidance-delta (cdr cur-pos) (- (random mouse-avoidance-nudge-var) (cdr mouse-avoidance-state)) mouse-avoidance-nudge-dist mouse-avoidance-nudge-var - 0 (frame-height)))) + wtop (1- wbot)))) (setq mouse-avoidance-state (cons (+ (car mouse-avoidance-state) deltax) (+ (cdr mouse-avoidance-state) deltay))) - (if (or (eq mouse-avoidance-mode 'animate) + (if (or (eq mouse-avoidance-mode 'animate) (eq mouse-avoidance-mode 'proteus)) - (let ((i 0.0)) + (let ((i 0.0) + (incr (max .1 (/ 1.0 mouse-avoidance-nudge-dist)))) + (setq mouse-avoidance-animating-pointer t) (while (<= i 1) - (mouse-avoidance-set-mouse-position + (mouse-avoidance-set-mouse-position (cons (+ (car cur-pos) (round (* i deltax))) (+ (cdr cur-pos) (round (* i deltay))))) - (setq i (+ i (max .1 (/ 1.0 mouse-avoidance-nudge-dist)))) + (setq i (+ i incr)) (if (eq mouse-avoidance-mode 'proteus) - (mouse-avoidance-set-pointer-shape + (mouse-avoidance-set-pointer-shape (mouse-avoidance-random-shape))) - (sit-for mouse-avoidance-animation-delay))) + (sit-for mouse-avoidance-animation-delay)) + (setq mouse-avoidance-animating-pointer nil)) (mouse-avoidance-set-mouse-position (cons (+ (car (cdr cur)) deltax) (+ (cdr (cdr cur)) deltay)))))) @@ -241,26 +266,44 @@ redefine this function to suit your own tastes." (if (null mouse-avoidance-pointer-shapes) (progn (setq mouse-avoidance-pointer-shapes - (mapcar '(lambda (x) (symbol-value (intern x))) + (mapcar (lambda (x) (symbol-value (intern x))) (all-completions "x-pointer-" obarray - '(lambda (x) + (lambda (x) (and (boundp x) (integerp (symbol-value x))))))) - (setq mouse-avoidance-n-pointer-shapes + (setq mouse-avoidance-n-pointer-shapes (length mouse-avoidance-pointer-shapes)))) (nth (random mouse-avoidance-n-pointer-shapes) mouse-avoidance-pointer-shapes)) -(defun mouse-avoidance-banish-hook () - (if (and (not executing-kbd-macro) ; don't check inside macro - (mouse-avoidance-kbd-command (this-command-keys))) +(defun mouse-avoidance-ignore-p () + (let ((mp (mouse-position))) + (or executing-kbd-macro ; don't check inside macro + (null (cadr mp)) ; don't move unless in an Emacs frame + (not (eq (car mp) (selected-frame))) + ;; Don't do anything if last event was a mouse event. + ;; FIXME: this code fails in the case where the mouse was moved + ;; since the last key-press but without generating any event. + (and (consp last-input-event) + (symbolp (car last-input-event)) + (let ((modifiers (event-modifiers (car last-input-event)))) + (or (memq (car last-input-event) + '(mouse-movement scroll-bar-movement + select-window switch-frame)) + (memq 'click modifiers) + (memq 'double modifiers) + (memq 'triple modifiers) + (memq 'drag modifiers) + (memq 'down modifiers))))))) + +(defun mouse-avoidance-banish () + (if (not (mouse-avoidance-ignore-p)) (mouse-avoidance-banish-mouse))) -(defun mouse-avoidance-exile-hook () +(defun mouse-avoidance-exile () ;; For exile mode, the state is nil when the mouse is in its normal ;; position, and set to the old mouse-position when the mouse is in exile. - (if (and (not executing-kbd-macro) - (mouse-avoidance-kbd-command (this-command-keys))) + (if (not (mouse-avoidance-ignore-p)) (let ((mp (mouse-position))) (cond ((and (not mouse-avoidance-state) (mouse-avoidance-too-close-p mp)) @@ -276,10 +319,10 @@ redefine this function to suit your own tastes." ;; but clear state anyway, to be ready for another move (setq mouse-avoidance-state nil)))))) -(defun mouse-avoidance-fancy-hook () +(defun mouse-avoidance-fancy () ;; Used for the "fancy" modes, ie jump et al. - (if (and (not executing-kbd-macro) ; don't check inside macro - (mouse-avoidance-kbd-command (this-command-keys)) + (if (and (not mouse-avoidance-animating-pointer) + (not (mouse-avoidance-ignore-p)) (mouse-avoidance-too-close-p (mouse-position))) (let ((old-pos (mouse-position))) (mouse-avoidance-nudge-mouse) @@ -287,32 +330,17 @@ redefine this function to suit your own tastes." ;; This should never happen. (apply 'set-mouse-position old-pos))))) -(defun mouse-avoidance-kbd-command (key) - "Return t if the KEYSEQENCE is composed of keyboard events only. -Return nil if there are any lists in the key sequence." - (cond ((null key) nil) ; Null event seems to be - ; returned occasionally. - ((not (vectorp key)) t) ; Strings are keyboard events. - ((catch 'done - (let ((i 0) - (l (length key))) - (while (< i l) - (if (listp (aref key i)) - (throw 'done nil)) - (setq i (1+ i)))) - t)))) - ;;;###autoload (defun mouse-avoidance-mode (&optional mode) "Set cursor avoidance mode to MODE. MODE should be one of the symbols `banish', `exile', `jump', `animate', `cat-and-mouse', `proteus', or `none'. -If MODE is nil, toggle mouse avoidance between `none` and `banish' +If MODE is nil, toggle mouse avoidance between `none' and `banish' modes. Positive numbers and symbols other than the above are treated as equivalent to `banish'; negative numbers and `-' are equivalent to `none'. -Effects of the different modes: +Effects of the different modes: * banish: Move the mouse to the upper-right corner on any keypress. * exile: Move the mouse to the corner only if the cursor gets too close, and allow it to return once the cursor is out of the way. @@ -335,9 +363,9 @@ definition of \"random distance\".)" nil t)))) (if (eq mode 'cat-and-mouse) (setq mode 'animate)) - (remove-hook 'post-command-idle-hook 'mouse-avoidance-banish-hook) - (remove-hook 'post-command-idle-hook 'mouse-avoidance-exile-hook) - (remove-hook 'post-command-idle-hook 'mouse-avoidance-fancy-hook) + (if mouse-avoidance-timer + (cancel-timer mouse-avoidance-timer)) + (setq mouse-avoidance-timer nil) ;; Restore pointer shape if necessary (if (eq mouse-avoidance-mode 'proteus) @@ -349,19 +377,23 @@ definition of \"random distance\".)" ((or (eq mode 'jump) (eq mode 'animate) (eq mode 'proteus)) - (add-hook 'post-command-idle-hook 'mouse-avoidance-fancy-hook) + (setq mouse-avoidance-timer + (run-with-idle-timer 0.1 t 'mouse-avoidance-fancy)) (setq mouse-avoidance-mode mode mouse-avoidance-state (cons 0 0) - mouse-avoidance-old-pointer-shape x-pointer-shape)) + mouse-avoidance-old-pointer-shape + (and (boundp 'x-pointer-shape) x-pointer-shape))) ((eq mode 'exile) - (add-hook 'post-command-idle-hook 'mouse-avoidance-exile-hook) + (setq mouse-avoidance-timer + (run-with-idle-timer 0.1 t 'mouse-avoidance-exile)) (setq mouse-avoidance-mode mode mouse-avoidance-state nil)) - ((or (eq mode 'banish) + ((or (eq mode 'banish) (eq mode t) (and (null mode) (null mouse-avoidance-mode)) (and mode (> (prefix-numeric-value mode) 0))) - (add-hook 'post-command-idle-hook 'mouse-avoidance-banish-hook) + (setq mouse-avoidance-timer + (run-with-idle-timer 0.1 t 'mouse-avoidance-banish)) (setq mouse-avoidance-mode 'banish)) (t (setq mouse-avoidance-mode nil))) (force-mode-line-update)) @@ -372,4 +404,9 @@ definition of \"random distance\".)" ;; (setq minor-mode-alist (cons '(mouse-avoidance-mode " Avoid") ;; minor-mode-alist))) -;;; End of avoid.el +;; Needed for custom. +(if mouse-avoidance-mode + (mouse-avoidance-mode mouse-avoidance-mode)) + +;; arch-tag: 64ad4ef8-a870-4183-8d96-3aa93b7a6800 +;;; avoid.el ends here