diff --git a/ChangeLog b/ChangeLog index 309b04f..53d7bb4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2015-01-20 Daniel Koning + + Prevent artist-mode from creating runaway timers. + * lisp/textmodes/artist.el: Cancel timers if an error occurs + during continuous drawing. + * lisp/subr.el: Make `posn-col-row' work with all mouse position + objects. Correct docstring of `posnp'. + * doc/lispref/commands.texi: Describe actual range of values that + mouse position objects can have. + Fixes: bug#6130 + 2015-01-16 Paul Eggert Give up on -Wsuggest-attribute=const diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index 36c7445..6fdc8e2 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -1489,8 +1489,10 @@ prefix @samp{drag-}. For example, dragging the mouse with button 2 held down generates a @code{drag-mouse-2} event. The second and third elements of the event give the starting and ending position of the drag, as mouse position lists (@pxref{Click Events}). You can access -the second element of any mouse event in the same way, with no need to -distinguish drag events from others. +the second element of any mouse event in the same way. However, the +drag event may end outside the boundaries of the frame that was +initially selected. In that case, the third element's position list +contains that frame in place of a window. The @samp{drag-} prefix follows the modifier key prefixes such as @samp{C-} and @samp{M-}. @@ -1635,7 +1637,10 @@ represented by lists that look like this: @noindent @var{position} is a mouse position list (@pxref{Click Events}), -specifying the current position of the mouse cursor. +specifying the current position of the mouse cursor. As with the +end-position of a drag event, this position list may represent a +location outside the boundaries of the initially selected frame, in +which case the list contains that frame in place of a window. The special form @code{track-mouse} enables generation of motion events within its body. Outside of @code{track-mouse} forms, Emacs @@ -1850,6 +1855,14 @@ into another window. That produces a pair of events like these: -453816)) @end smallexample +The frame with input focus might not take up the entire screen, and +the user might move the mouse outside the scope of the frame. Inside +the @code{track-mouse} special form, that produces an event like this: + +@smallexample +(mouse-movement (# nil (563 . 205) 532301936)) +@end smallexample + To handle a SIGUSR1 signal, define an interactive function, and bind it to the @code{signal usr1} event sequence: @@ -2014,7 +2027,9 @@ Events}); and @code{nil} otherwise. various parts of it: @defun posn-window position -Return the window that @var{position} is in. +Return the window that @var{position} is in. If @var{position} +represents a location outside the frame where the event was initiated, +return that frame instead. @end defun @defun posn-area position diff --git a/lisp/subr.el b/lisp/subr.el index 0534585..b37d17f 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -1082,7 +1082,10 @@ The return value is a positive integer." ;;;; Extracting fields of the positions in an event. (defun posnp (obj) - "Return non-nil if OBJ appears to be a valid `posn' object." + "Return non-nil if OBJ appears to be a valid `posn' object that specifies a window. If OBJ is a valid `posn' object, but specifies a frame rather than a window, return nil." + ;; FIXME: Correct the behavior of this function so that all valid + ;; `posn' objects are recognized, after updating other code that + ;; depends on its present behavior. (and (windowp (car-safe obj)) (atom (car-safe (setq obj (cdr obj)))) ;AREA-OR-POS. (integerp (car-safe (car-safe (setq obj (cdr obj))))) ;XOFFSET. @@ -1142,24 +1145,28 @@ For a scroll-bar event, the result column is 0, and the row corresponds to the vertical position of the click in the scroll bar. POSITION should be a list of the form returned by the `event-start' and `event-end' functions." - (let* ((pair (posn-x-y position)) - (window (posn-window position)) - (area (posn-area position))) + (let* ((pair (posn-x-y position)) + (frame-or-window (posn-window position)) + (frame (if (framep frame-or-window) + frame-or-window + (window-frame frame-or-window))) + (window (when (windowp frame-or-window) frame-or-window)) + (area (posn-area position))) (cond - ((null window) + ((null frame-or-window) '(0 . 0)) ((eq area 'vertical-scroll-bar) (cons 0 (scroll-bar-scale pair (1- (window-height window))))) ((eq area 'horizontal-scroll-bar) (cons (scroll-bar-scale pair (window-width window)) 0)) (t - (let* ((frame (if (framep window) window (window-frame window))) - ;; FIXME: This should take line-spacing properties on - ;; newlines into account. - (spacing (when (display-graphic-p frame) - (or (with-current-buffer (window-buffer window) - line-spacing) - (frame-parameter frame 'line-spacing))))) + ;; FIXME: This should take line-spacing properties on + ;; newlines into account. + (let* ((spacing (when (display-graphic-p frame) + (or (with-current-buffer + (window-buffer (frame-selected-window frame)) + line-spacing) + (frame-parameter frame 'line-spacing))))) (cond ((floatp spacing) (setq spacing (truncate (* spacing (frame-char-height frame))))) diff --git a/lisp/textmodes/artist.el b/lisp/textmodes/artist.el index 8a2383c..85d9410 100644 --- a/lisp/textmodes/artist.el +++ b/lisp/textmodes/artist.el @@ -4963,52 +4963,55 @@ The event, EV, is the mouse event." (artist-funcall init-fn x1 y1) (if (not artist-rubber-banding) (artist-no-rb-set-point1 x1 y1)) - (track-mouse - (while (or (mouse-movement-p ev) - (member 'down (event-modifiers ev))) - (setq ev-start-pos (artist-coord-win-to-buf - (posn-col-row (event-start ev)))) - (setq x1 (car ev-start-pos)) - (setq y1 (cdr ev-start-pos)) - - ;; Cancel previous timer - (if timer - (cancel-timer timer)) - - (if (not (eq initial-win (posn-window (event-start ev)))) - ;; If we moved outside the window, do nothing - nil - - ;; Still in same window: - ;; - ;; Check if user presses or releases shift key - (if (artist-shift-has-changed shift-state ev) - - ;; First check that the draw-how is the same as we - ;; already have. Otherwise, ignore the changed shift-state. - (if (not (eq draw-how - (artist-go-get-draw-how-from-symbol - (if (not shift-state) shifted unshifted)))) - (message "Cannot switch to shifted operation") - - ;; progn is "implicit" since this is the else-part - (setq shift-state (not shift-state)) - (setq op (if shift-state shifted unshifted)) - (setq draw-how (artist-go-get-draw-how-from-symbol op)) - (setq draw-fn (artist-go-get-draw-fn-from-symbol op)))) - - ;; Draw the new shape - (setq shape (artist-funcall draw-fn x1 y1)) - (artist-move-to-xy x1 y1) - - ;; Start the timer to call `draw-fn' repeatedly every - ;; `interval' second - (if (and interval draw-fn) - (setq timer (run-at-time interval interval draw-fn x1 y1)))) - - ;; Read next event - (setq ev (read-event)))) - + (unwind-protect + (track-mouse + (while (or (mouse-movement-p ev) + (member 'down (event-modifiers ev))) + (setq ev-start-pos (artist-coord-win-to-buf + (posn-col-row (event-start ev)))) + (setq x1 (car ev-start-pos)) + (setq y1 (cdr ev-start-pos)) + + ;; Cancel previous timer + (if timer + (cancel-timer timer)) + + (if (not (eq initial-win (posn-window (event-start ev)))) + ;; If we moved outside the window, do nothing + nil + + ;; Still in same window: + ;; + ;; Check if user presses or releases shift key + (if (artist-shift-has-changed shift-state ev) + + ;; First check that the draw-how is the same as we + ;; already have. Otherwise, ignore the changed shift-state. + (if (not (eq draw-how + (artist-go-get-draw-how-from-symbol + (if (not shift-state) shifted unshifted)))) + (message "Cannot switch to shifted operation") + + ;; progn is "implicit" since this is the else-part + (setq shift-state (not shift-state)) + (setq op (if shift-state shifted unshifted)) + (setq draw-how (artist-go-get-draw-how-from-symbol op)) + (setq draw-fn (artist-go-get-draw-fn-from-symbol op)))) + + ;; Draw the new shape + (setq shape (artist-funcall draw-fn x1 y1)) + (artist-move-to-xy x1 y1) + + ;; Start the timer to call `draw-fn' repeatedly every + ;; `interval' second + (if (and interval draw-fn) + (setq timer (run-at-time interval interval draw-fn x1 y1)))) + + ;; Read next event + (setq ev (read-event)))) + ;; Cleanup: get rid of any active timer. + (if timer + (cancel-timer timer))) ;; Cancel any timers (if timer (cancel-timer timer))