summaryrefslogtreecommitdiff
path: root/lisp/frame.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/frame.el')
-rw-r--r--lisp/frame.el205
1 files changed, 143 insertions, 62 deletions
diff --git a/lisp/frame.el b/lisp/frame.el
index 545d2665365..8341ba1707f 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -129,22 +129,107 @@ appended when the minibuffer frame is created."
;; Gildea@x.org says it is ok to ask questions before terminating.
(save-buffers-kill-emacs))))
-(defun handle-focus-in (_event)
+(defun frame-focus-state (&optional frame)
+ "Return FRAME's last known focus state.
+If nil or omitted, FRAME defaults to the selected frame.
+
+Return nil if the frame is definitely known not be focused, t if
+the frame is known to be focused, and `unknown' if we don't know."
+ (let* ((frame (or frame (selected-frame)))
+ (tty-top-frame (tty-top-frame frame)))
+ (if (not tty-top-frame)
+ (frame-parameter frame 'last-focus-update)
+ ;; All tty frames are frame-visible-p if the terminal is
+ ;; visible, so check whether the frame is the top tty frame
+ ;; before checking visibility.
+ (cond ((not (eq tty-top-frame frame)) nil)
+ ((not (frame-visible-p frame)) nil)
+ (t (let ((tty-focus-state
+ (terminal-parameter frame 'tty-focus-state)))
+ (cond ((eq tty-focus-state 'focused) t)
+ ((eq tty-focus-state 'defocused) nil)
+ (t 'unknown))))))))
+
+(defvar after-focus-change-function #'ignore
+ "Function called after frame focus may have changed.
+
+This function is called with no arguments when Emacs notices that
+the set of focused frames may have changed. Code wanting to do
+something when frame focus changes should use `add-function' to
+add a function to this one, and in this added function, re-scan
+the set of focused frames, calling `frame-focus-state' to
+retrieve the last known focus state of each frame. Focus events
+are delivered asynchronously, and frame input focus according to
+an external system may not correspond to the notion of the Emacs
+selected frame. Multiple frames may appear to have input focus
+simultaneously due to focus event delivery differences, the
+presence of multiple Emacs terminals, and other factors, and code
+should be robust in the face of this situation.
+
+Depending on window system, focus events may also be delivered
+repeatedly and with different focus states before settling to the
+expected values. Code relying on focus notifications should
+\"debounce\" any user-visible updates arising from focus changes,
+perhaps by deferring work until redisplay.
+
+This function may be called in arbitrary contexts, including from
+inside `read-event', so take the same care as you might when
+writing a process filter.")
+
+(defvar focus-in-hook nil
+ "Normal hook run when a frame gains focus.
+The frame gaining focus is selected at the time this hook is run.
+
+This hook is obsolete. Despite its name, this hook may be run in
+situations other than when a frame obtains input focus: for
+example, we also run this hook when switching the selected frame
+internally to handle certain input events (like mouse wheel
+scrolling) even when the user's notion of input focus
+hasn't changed.
+
+Prefer using `after-focus-change-function'.")
+(make-obsolete-variable
+ 'focus-in-hook "after-focus-change-function" "27.1" 'set)
+
+(defvar focus-out-hook nil
+ "Normal hook run when all frames lost input focus.
+
+This hook is obsolete; see `focus-in-hook'. Depending on timing,
+this hook may be delivered when a frame does in fact have focus.
+Prefer `after-focus-change-function'.")
+(make-obsolete-variable
+ 'focus-out-hook "after-focus-change-function" "27.1" 'set)
+
+(defun handle-focus-in (event)
"Handle a focus-in event.
-Focus-in events are usually bound to this function.
-Focus-in events occur when a frame has focus, but a switch-frame event
-is not generated.
-This function runs the hook `focus-in-hook'."
+Focus-in events are bound to this function; do not change this
+binding. Focus-in events occur when a frame receives focus from
+the window system."
+ ;; N.B. tty focus goes down a different path; see xterm.el.
(interactive "e")
- (run-hooks 'focus-in-hook))
-
-(defun handle-focus-out (_event)
+ (unless (eq (car-safe event) 'focus-in)
+ (error "handle-focus-in should handle focus-in events"))
+ (let ((frame (nth 1 event)))
+ (when (frame-live-p frame)
+ (internal-handle-focus-in event)
+ (setf (frame-parameter frame 'last-focus-update) t)
+ (run-hooks 'focus-in-hook)))
+ (funcall after-focus-change-function))
+
+(defun handle-focus-out (event)
"Handle a focus-out event.
-Focus-out events are usually bound to this function.
-Focus-out events occur when no frame has focus.
-This function runs the hook `focus-out-hook'."
+Focus-out events are bound to this function; do not change this
+binding. Focus-out events occur when a frame loses focus, but
+that's not the whole story: see `after-focus-change-function'."
+ ;; N.B. tty focus goes down a different path; see xterm.el.
(interactive "e")
- (run-hooks 'focus-out-hook))
+ (unless (eq (car event) 'focus-out)
+ (error "handle-focus-out should handle focus-out events"))
+ (let ((frame (nth 1 event)))
+ (when (frame-live-p frame)
+ (setf (frame-parameter frame 'last-focus-update) nil)
+ (run-hooks 'focus-out-hook)))
+ (funcall after-focus-change-function))
(defun handle-move-frame (event)
"Handle a move-frame event.
@@ -614,9 +699,6 @@ frame.")
(defvar after-setting-font-hook nil
"Functions to run after a frame's font has been changed.")
-;; Alias, kept temporarily.
-(define-obsolete-function-alias 'new-frame 'make-frame "22.1")
-
(defvar frame-inherited-parameters '()
"Parameters `make-frame' copies from the selected to the new frame.")
@@ -1147,8 +1229,6 @@ FRAME defaults to the selected frame."
(declare-function x-list-fonts "xfaces.c"
(pattern &optional face frame maximum width))
-(define-obsolete-function-alias 'set-default-font 'set-frame-font "23.1")
-
(defun set-frame-font (font &optional keep-size frames)
"Set the default font to FONT.
When called interactively, prompt for the name of a font, and use
@@ -1302,9 +1382,6 @@ To get the frame's current border color, use `frame-parameters'."
(define-minor-mode auto-raise-mode
"Toggle whether or not selected frames should auto-raise.
-With a prefix argument ARG, enable Auto Raise mode if ARG is
-positive, and disable it otherwise. If called from Lisp, enable
-the mode if ARG is omitted or nil.
Auto Raise mode does nothing under most window managers, which
switch focus on mouse clicks. It only has an effect if your
@@ -1322,9 +1399,6 @@ often have their own auto-raise feature."
(define-minor-mode auto-lower-mode
"Toggle whether or not the selected frame should auto-lower.
-With a prefix argument ARG, enable Auto Lower mode if ARG is
-positive, and disable it otherwise. If called from Lisp, enable
-the mode if ARG is omitted or nil.
Auto Lower mode does nothing under most window managers, which
switch focus on mouse clicks. It only has an effect if your
@@ -2113,10 +2187,6 @@ a live frame and defaults to the selected one."
(delete-frame this))
(setq this next))))
-;; miscellaneous obsolescence declarations
-(define-obsolete-variable-alias 'delete-frame-hook
- 'delete-frame-functions "22.1")
-
;;; Window dividers.
(defgroup window-divider nil
@@ -2221,9 +2291,6 @@ all divider widths to zero."
(define-minor-mode window-divider-mode
"Display dividers between windows (Window Divider mode).
-With a prefix argument ARG, enable Window Divider mode if ARG is
-positive, and disable it otherwise. If called from Lisp, enable
-the mode if ARG is omitted or nil.
The option `window-divider-default-places' specifies on which
side of a window dividers are displayed. The options
@@ -2322,7 +2389,6 @@ command starts, by installing a pre-command hook."
(blink-cursor-suspend)
(add-hook 'post-command-hook 'blink-cursor-check)))
-
(defun blink-cursor-end ()
"Stop cursor blinking.
This is installed as a pre-command hook by `blink-cursor-start'.
@@ -2344,22 +2410,37 @@ frame receives focus."
(cancel-timer blink-cursor-idle-timer)
(setq blink-cursor-idle-timer nil)))
+(defun blink-cursor--should-blink ()
+ "Determine whether we should be blinking.
+Returns whether we have any focused non-TTY frame."
+ (and blink-cursor-mode
+ (let ((frame-list (frame-list))
+ (any-graphical-focused nil))
+ (while frame-list
+ (let ((frame (pop frame-list)))
+ (when (and (display-graphic-p frame) (frame-focus-state frame))
+ (setf any-graphical-focused t)
+ (setf frame-list nil))))
+ any-graphical-focused)))
+
(defun blink-cursor-check ()
"Check if cursor blinking shall be restarted.
-This is done when a frame gets focus. Blink timers may be stopped by
-`blink-cursor-suspend'."
- (when (and blink-cursor-mode
- (not blink-cursor-idle-timer))
- (remove-hook 'post-command-hook 'blink-cursor-check)
- (blink-cursor--start-idle-timer)))
-
-(define-obsolete-variable-alias 'blink-cursor 'blink-cursor-mode "22.1")
+This is done when a frame gets focus. Blink timers may be
+stopped by `blink-cursor-suspend'. Internally calls
+`blink-cursor--should-blink' and returns its result."
+ (let ((should-blink (blink-cursor--should-blink)))
+ (when (and should-blink (not blink-cursor-idle-timer))
+ (remove-hook 'post-command-hook 'blink-cursor-check)
+ (blink-cursor--start-idle-timer))
+ should-blink))
+
+(defun blink-cursor--rescan-frames (&optional _ign)
+ "Called when the set of focused frames changes or when we delete a frame."
+ (unless (blink-cursor-check)
+ (blink-cursor-suspend)))
(define-minor-mode blink-cursor-mode
"Toggle cursor blinking (Blink Cursor mode).
-With a prefix argument ARG, enable Blink Cursor mode if ARG is
-positive, and disable it otherwise. If called from Lisp, enable
-the mode if ARG is omitted or nil.
If the value of `blink-cursor-blinks' is positive (10 by default),
the cursor stops blinking after that number of blinks, if Emacs
@@ -2377,19 +2458,18 @@ terminals, cursor blinking is controlled by the terminal."
:group 'cursor
:global t
(blink-cursor-suspend)
- (remove-hook 'focus-in-hook #'blink-cursor-check)
- (remove-hook 'focus-out-hook #'blink-cursor-suspend)
+ (remove-hook 'after-delete-frame-functions #'blink-cursor--rescan-frames)
+ (remove-function after-focus-change-function #'blink-cursor--rescan-frames)
(when blink-cursor-mode
- (add-hook 'focus-in-hook #'blink-cursor-check)
- (add-hook 'focus-out-hook #'blink-cursor-suspend)
+ (add-function :after after-focus-change-function #'blink-cursor--rescan-frames)
+ (add-hook 'after-delete-frame-functions #'blink-cursor--rescan-frames)
(blink-cursor--start-idle-timer)))
-
;; Frame maximization/fullscreen
-(defun toggle-frame-maximized ()
- "Toggle maximization state of selected frame.
+(defun toggle-frame-maximized (&optional frame)
+ "Toggle maximization state of FRAME.
Maximize selected frame or un-maximize if it is already maximized.
If the frame is in fullscreen state, don't change its state, but
@@ -2404,19 +2484,19 @@ transitions from one fullscreen state to another.
See also `toggle-frame-fullscreen'."
(interactive)
- (let ((fullscreen (frame-parameter nil 'fullscreen)))
+ (let ((fullscreen (frame-parameter frame 'fullscreen)))
(cond
((memq fullscreen '(fullscreen fullboth))
- (set-frame-parameter nil 'fullscreen-restore 'maximized))
+ (set-frame-parameter frame 'fullscreen-restore 'maximized))
((eq fullscreen 'maximized)
- (set-frame-parameter nil 'fullscreen nil))
+ (set-frame-parameter frame 'fullscreen nil))
(t
- (set-frame-parameter nil 'fullscreen 'maximized)))))
+ (set-frame-parameter frame 'fullscreen 'maximized)))))
-(defun toggle-frame-fullscreen ()
- "Toggle fullscreen state of selected frame.
-Make selected frame fullscreen or restore its previous size if it
-is already fullscreen.
+(defun toggle-frame-fullscreen (&optional frame)
+ "Toggle fullscreen state of FRAME.
+Make selected frame fullscreen or restore its previous size
+if it is already fullscreen.
Before making the frame fullscreen remember the current value of
the frame's `fullscreen' parameter in the `fullscreen-restore'
@@ -2431,18 +2511,19 @@ transitions from one fullscreen state to another.
See also `toggle-frame-maximized'."
(interactive)
- (let ((fullscreen (frame-parameter nil 'fullscreen)))
+ (let ((fullscreen (frame-parameter frame 'fullscreen)))
(if (memq fullscreen '(fullscreen fullboth))
- (let ((fullscreen-restore (frame-parameter nil 'fullscreen-restore)))
+ (let ((fullscreen-restore (frame-parameter frame 'fullscreen-restore)))
(if (memq fullscreen-restore '(maximized fullheight fullwidth))
- (set-frame-parameter nil 'fullscreen fullscreen-restore)
- (set-frame-parameter nil 'fullscreen nil)))
+ (set-frame-parameter frame 'fullscreen fullscreen-restore)
+ (set-frame-parameter frame 'fullscreen nil)))
(modify-frame-parameters
- nil `((fullscreen . fullboth) (fullscreen-restore . ,fullscreen))))
+ frame `((fullscreen . fullboth) (fullscreen-restore . ,fullscreen))))
;; Manipulating a frame without waiting for the fullscreen
;; animation to complete can cause a crash, or other unexpected
;; behavior, on macOS (bug#28496).
(when (featurep 'cocoa) (sleep-for 0.5))))
+
;;;; Key bindings