diff options
| author | Daniel Colascione | 2018-06-11 14:58:09 -0700 |
|---|---|---|
| committer | Daniel Colascione | 2018-06-11 16:10:34 -0700 |
| commit | 2f6c682061a281dc3e397ff4727a164880e86e7b (patch) | |
| tree | f5990303d483f7d80e1aa1e80a19dc64a6325b66 /lisp | |
| parent | a20fe5a7e3577f9b9ad5e88006962966240d9b0c (diff) | |
| download | emacs-2f6c682061a281dc3e397ff4727a164880e86e7b.tar.gz emacs-2f6c682061a281dc3e397ff4727a164880e86e7b.zip | |
New focus management interface
focus-in-hook and focus-out-hook don't accurately reflect actual
user-visible focus states. Add a new focus interface and mark the old
one obsolete.
* doc/lispref/frames.texi (Input Focus): Document new focus
functions. Remove references to the now-obsolete focus hooks.
* lisp/frame.el (frame-focus-state): New function.
(after-focus-change-function): New variable.
(focus-in-hook, focus-out-hook): Move to lisp from C;
mark obsolete.
* lisp/term/xterm.el (xterm-translate-focus-in)
(xterm-translate-focus-out): Track tty focus in `tty-focus-state'
terminal parameter; call `after-focus-change-function'.
(xterm--suspend-tty-function): New function.
* src/frame.c (Fhandle_switch_frame): Update docstring; don't call
focus hooks.
(focus-in-hook, focus-out-hook): Remove: moved to lisp.
(syms_of_frame): Remove unread_switch_frame; add
Vunread_switch_frame.
* src/keyboard.c:
(Finternal_handle_focus_in): New function.
(make_lispy_event): Always report focus events to lisp; don't
translate them to switch events sometimes. Lisp can take care of
creating synthetic switch-frame events via
`internal-handle-focus-in'.
* src/w32term.c (x_focus_changed): Remove switch-avoidance logic:
just directly report focus changes to lisp.
* src/xterm.c (x_focus_changed): Remove switch-avoidance logic:
just directly report focus changes to lisp.
Diffstat (limited to 'lisp')
| -rw-r--r-- | lisp/frame.el | 106 | ||||
| -rw-r--r-- | lisp/term/xterm.el | 11 |
2 files changed, 103 insertions, 14 deletions
diff --git a/lisp/frame.el b/lisp/frame.el index c3daff44406..2a2391e8a53 100644 --- a/lisp/frame.el +++ b/lisp/frame.el | |||
| @@ -129,22 +129,104 @@ appended when the minibuffer frame is created." | |||
| 129 | ;; Gildea@x.org says it is ok to ask questions before terminating. | 129 | ;; Gildea@x.org says it is ok to ask questions before terminating. |
| 130 | (save-buffers-kill-emacs)))) | 130 | (save-buffers-kill-emacs)))) |
| 131 | 131 | ||
| 132 | (defun handle-focus-in (&optional _event) | 132 | (defun frame-focus-state (&optional frame) |
| 133 | "Return FRAME's last known focus state. | ||
| 134 | Return nil if the frame is definitely known not be focused, t if | ||
| 135 | the frame is known to be focused, and 'unknown if we don't know. If | ||
| 136 | FRAME is nil, query the selected frame." | ||
| 137 | (let* ((frame (or frame (selected-frame))) | ||
| 138 | (tty-top-frame (tty-top-frame frame))) | ||
| 139 | (if (not tty-top-frame) | ||
| 140 | (frame-parameter frame 'last-focus-update) | ||
| 141 | ;; All tty frames are frame-visible-p if the terminal is | ||
| 142 | ;; visible, so check whether the frame is the top tty frame | ||
| 143 | ;; before checking visibility. | ||
| 144 | (cond ((not (eq tty-top-frame frame)) nil) | ||
| 145 | ((not (frame-visible-p frame)) nil) | ||
| 146 | (t (let ((tty-focus-state | ||
| 147 | (terminal-parameter frame 'tty-focus-state))) | ||
| 148 | (cond ((eq tty-focus-state 'focused) t) | ||
| 149 | ((eq tty-focus-state 'defocused) nil) | ||
| 150 | (t 'unknown)))))))) | ||
| 151 | |||
| 152 | (defvar after-focus-change-function #'ignore | ||
| 153 | "Function called after frame focus may have changed. | ||
| 154 | |||
| 155 | This function is called with no arguments when Emacs notices that | ||
| 156 | the set of focused frames may have changed. Code wanting to do | ||
| 157 | something when frame focus changes should use `add-function' to | ||
| 158 | add a function to this one, and in this added function, re-scan | ||
| 159 | the set of focused frames, calling `frame-focus-state' to | ||
| 160 | retrieve the last known focus state of each frame. Focus events | ||
| 161 | are delivered asynchronously, and frame input focus according to | ||
| 162 | an external system may not correspond to the notion of the Emacs | ||
| 163 | selected frame. Multiple frames may appear to have input focus | ||
| 164 | simultaneously due to focus event delivery differences, the | ||
| 165 | presence of multiple Emacs terminals, and other factors, and code | ||
| 166 | should be robust in the face of this situation. | ||
| 167 | |||
| 168 | Depending on window system, focus events may also be delivered | ||
| 169 | repeatedly and with different focus states before settling to the | ||
| 170 | expected values. Code relying on focus notifications should | ||
| 171 | \"debounce\" any user-visible updates arising from focus changes, | ||
| 172 | perhaps by deferring work until redisplay. | ||
| 173 | |||
| 174 | This function may be called in arbitrary contexts, including from | ||
| 175 | inside `read-event', so take the same care as you might when | ||
| 176 | writing a process filter.") | ||
| 177 | |||
| 178 | (defvar focus-in-hook nil | ||
| 179 | "Normal hook run when a frame gains focus. | ||
| 180 | The frame gaining focus is selected at the time this hook is run. | ||
| 181 | |||
| 182 | This hook is obsolete. Despite its name, this hook may be run in | ||
| 183 | situations other than when a frame obtains input focus: for | ||
| 184 | example, we also run this hook when switching the selected frame | ||
| 185 | internally to handle certain input events (like mouse wheel | ||
| 186 | scrolling) even when the user's notion of input focus | ||
| 187 | hasn't changed. | ||
| 188 | |||
| 189 | Prefer using `after-focus-change-function'.") | ||
| 190 | (make-obsolete-variable | ||
| 191 | 'focus-in-hook "after-focus-change-function" "27.1" 'set) | ||
| 192 | |||
| 193 | (defvar focus-out-hook nil | ||
| 194 | "Normal hook run when all frames lost input focus. | ||
| 195 | |||
| 196 | This hook is obsolete; see `focus-in-hook'. Depending on timing, | ||
| 197 | this hook may be delivered when a frame does in fact have focus. | ||
| 198 | Prefer `after-focus-change-function'.") | ||
| 199 | (make-obsolete-variable | ||
| 200 | 'focus-out-hook "after-focus-change-function" "27.1" 'set) | ||
| 201 | |||
| 202 | (defun handle-focus-in (event) | ||
| 133 | "Handle a focus-in event. | 203 | "Handle a focus-in event. |
| 134 | Focus-in events are usually bound to this function. | 204 | Focus-in events are bound to this function; do not change this |
| 135 | Focus-in events occur when a frame has focus, but a switch-frame event | 205 | binding. Focus-in events occur when a frame receives focus from |
| 136 | is not generated. | 206 | the window system." |
| 137 | This function runs the hook `focus-in-hook'." | 207 | ;; N.B. tty focus goes down a different path; see xterm.el. |
| 138 | (interactive "e") | 208 | (interactive "e") |
| 139 | (run-hooks 'focus-in-hook)) | 209 | (unless (eq (car-safe event) 'focus-in) |
| 140 | 210 | (error "handle-focus-in should handle focus-in events")) | |
| 141 | (defun handle-focus-out (&optional _event) | 211 | (internal-handle-focus-in event) |
| 212 | (let ((frame (nth 1 event))) | ||
| 213 | (setf (frame-parameter frame 'last-focus-update) t) | ||
| 214 | (run-hooks 'focus-in-hook) | ||
| 215 | (funcall after-focus-change-function))) | ||
| 216 | |||
| 217 | (defun handle-focus-out (event) | ||
| 142 | "Handle a focus-out event. | 218 | "Handle a focus-out event. |
| 143 | Focus-out events are usually bound to this function. | 219 | Focus-out events are bound to this function; do not change this |
| 144 | Focus-out events occur when no frame has focus. | 220 | binding. Focus-out events occur when a frame loses focus, but |
| 145 | This function runs the hook `focus-out-hook'." | 221 | that's not the whole story: see `after-focus-change-function'." |
| 222 | ;; N.B. tty focus goes down a different path; see xterm.el. | ||
| 146 | (interactive "e") | 223 | (interactive "e") |
| 147 | (run-hooks 'focus-out-hook)) | 224 | (unless (eq (car event) 'focus-out) |
| 225 | (error "handle-focus-out should handle focus-out events")) | ||
| 226 | (let ((frame (nth 1 event))) | ||
| 227 | (setf (frame-parameter frame 'last-focus-update) nil) | ||
| 228 | (run-hooks 'focus-out-hook) | ||
| 229 | (funcall after-focus-change-function))) | ||
| 148 | 230 | ||
| 149 | (defun handle-move-frame (event) | 231 | (defun handle-move-frame (event) |
| 150 | "Handle a move-frame event. | 232 | "Handle a move-frame event. |
diff --git a/lisp/term/xterm.el b/lisp/term/xterm.el index b3b7a216352..ce4e18efff8 100644 --- a/lisp/term/xterm.el +++ b/lisp/term/xterm.el | |||
| @@ -115,13 +115,20 @@ Return the pasted text as a string." | |||
| 115 | ;; notifications) instead of read-event (which can't). | 115 | ;; notifications) instead of read-event (which can't). |
| 116 | 116 | ||
| 117 | (defun xterm-translate-focus-in (_prompt) | 117 | (defun xterm-translate-focus-in (_prompt) |
| 118 | (handle-focus-in) | 118 | (setf (terminal-parameter nil 'tty-focus-state) 'focused) |
| 119 | (funcall after-focus-change-function) | ||
| 119 | []) | 120 | []) |
| 120 | 121 | ||
| 121 | (defun xterm-translate-focus-out (_prompt) | 122 | (defun xterm-translate-focus-out (_prompt) |
| 122 | (handle-focus-out) | 123 | (setf (terminal-parameter nil 'tty-focus-state) 'defocused) |
| 124 | (funcall after-focus-change-function) | ||
| 123 | []) | 125 | []) |
| 124 | 126 | ||
| 127 | (defun xterm--suspend-tty-function (_tty) | ||
| 128 | ;; We can't know what happens to the tty after we're suspended | ||
| 129 | (setf (terminal-parameter nil 'tty-focus-state) nil) | ||
| 130 | (funcall after-focus-change-function)) | ||
| 131 | |||
| 125 | ;; Similarly, we want to transparently slurp the entirety of a | 132 | ;; Similarly, we want to transparently slurp the entirety of a |
| 126 | ;; bracketed paste and encapsulate it into a single event. We used to | 133 | ;; bracketed paste and encapsulate it into a single event. We used to |
| 127 | ;; just slurp up the bracketed paste content in the event handler, but | 134 | ;; just slurp up the bracketed paste content in the event handler, but |