commit 57e78f2d49a5b1a2337a15254e7b8c87aa63c83a (HEAD, refs/remotes/origin/master) Author: Juri Linkov Date: Thu Apr 4 09:21:01 2024 +0300 Disobey display actions while using switch-to-buffer on the tab-line * lisp/tab-line.el (tab-line-select-tab-buffer) (tab-line-switch-to-prev-tab, tab-line-switch-to-next-tab): Let-bind switch-to-buffer-obey-display-actions to nil around the call to switch-to-buffer to restrain buffer switching in bounds of the same window only (bug#69993). diff --git a/lisp/tab-line.el b/lisp/tab-line.el index 8c59f7b2e6d..fd18e7b7909 100644 --- a/lisp/tab-line.el +++ b/lisp/tab-line.el @@ -832,7 +832,8 @@ using the `previous-buffer' command." (switch-to-prev-buffer window))) (t (with-selected-window window - (switch-to-buffer buffer)))))) + (let ((switch-to-buffer-obey-display-actions nil)) + (switch-to-buffer buffer))))))) (defcustom tab-line-switch-cycling nil "Enable cycling tab switch. @@ -863,7 +864,8 @@ Its effect is the same as using the `previous-buffer' command (nth (1- (length buffers)) buffers) (nth (1- pos) buffers))))) (when (bufferp buffer) - (switch-to-buffer buffer))))))) + (let ((switch-to-buffer-obey-display-actions nil)) + (switch-to-buffer buffer)))))))) (defun tab-line-switch-to-next-tab (&optional event) "Switch to the next tab's buffer. @@ -885,7 +887,8 @@ Its effect is the same as using the `next-buffer' command (car buffers) (nth (1+ pos) buffers))))) (when (bufferp buffer) - (switch-to-buffer buffer))))))) + (let ((switch-to-buffer-obey-display-actions nil)) + (switch-to-buffer buffer)))))))) (defcustom tab-line-close-tab-function 'bury-buffer commit daefd6771a4879bb8e71ea67f69522700155df01 Author: Po Lu Date: Thu Apr 4 13:52:52 2024 +0800 Remove redundant byte-swapping boundary * src/sfnt.c (sfnt_read_OS_2_table): * src/sfnt.h (struct sfnt_OS_2_table): Don't redundantly realign after s_family_class. diff --git a/src/sfnt.c b/src/sfnt.c index 8598b052044..d909fba7677 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -16650,10 +16650,10 @@ sfnt_read_OS_2_table (int fd, struct sfnt_offset_subtable *subtable) OS_2 = xmalloc (sizeof *OS_2); - /* Read data up to the end of `panose'. */ + /* Read data into the structure. */ - wanted = SFNT_ENDOF (struct sfnt_OS_2_table, panose, - unsigned char[10]); + wanted = SFNT_ENDOF (struct sfnt_OS_2_table, fs_last_char_index, + uint16_t); rc = read (fd, OS_2, wanted); if (rc == -1 || rc != wanted) @@ -16680,20 +16680,6 @@ sfnt_read_OS_2_table (int fd, struct sfnt_offset_subtable *subtable) sfnt_swap16 (&OS_2->y_strikeout_size); sfnt_swap16 (&OS_2->y_strikeout_position); sfnt_swap16 (&OS_2->s_family_class); - - /* Read fields between ul_unicode_range and fs_last_char_index. */ - wanted = (SFNT_ENDOF (struct sfnt_OS_2_table, fs_last_char_index, - uint16_t) - - offsetof (struct sfnt_OS_2_table, ul_unicode_range)); - rc = read (fd, &OS_2->ul_unicode_range, wanted); - - if (rc == -1 || rc != wanted) - { - xfree (OS_2); - return NULL; - } - - /* Swap the remainder and return the table. */ sfnt_swap32 (&OS_2->ul_unicode_range[0]); sfnt_swap32 (&OS_2->ul_unicode_range[1]); sfnt_swap32 (&OS_2->ul_unicode_range[2]); diff --git a/src/sfnt.h b/src/sfnt.h index 444b1dfe427..ecc3876b394 100644 --- a/src/sfnt.h +++ b/src/sfnt.h @@ -1395,8 +1395,6 @@ struct sfnt_OS_2_table /* Microsoft ``panose'' classification. */ unsigned char panose[10]; - /* Alignment boundary! */ - /* Unicode range specification. */ uint32_t ul_unicode_range[4]; commit 42c0603c7aab191c9cc15a7eca1253060ff5b71a Author: Po Lu Date: Thu Apr 4 09:53:07 2024 +0800 Avoid destroying windows after they are unmapped * java/org/gnu/emacs/EmacsActivity.java (destroy): Detach from current window before calling finish. * java/org/gnu/emacs/EmacsWindow.java (reparentTo): Don't clear attachment state here... * java/org/gnu/emacs/EmacsWindowManager.java (detachWindow): ...but do so here instead. diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java index a939641a752..28bb6e4c065 100644 --- a/java/org/gnu/emacs/EmacsActivity.java +++ b/java/org/gnu/emacs/EmacsActivity.java @@ -207,6 +207,18 @@ children and RESETWHENCHILDLESS is set (implying it is a public final void destroy () { + if (window != null) + { + /* Clear the window's pointer to this activity and remove the + window's view. */ + window.setConsumer (null); + + /* The window can't be iconified any longer. */ + window.noticeDeiconified (); + layout.removeView (window.view); + window = null; + } + finish (); } diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index b085614de23..91e97fa8b61 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -1322,10 +1322,6 @@ private static class Coordinate manager = EmacsWindowManager.MANAGER; manager.detachWindow (EmacsWindow.this); - /* Reset window management state. */ - previouslyAttached = false; - attachmentToken = 0; - /* Also unparent this view. */ /* If the window manager is set, use that instead. */ diff --git a/java/org/gnu/emacs/EmacsWindowManager.java b/java/org/gnu/emacs/EmacsWindowManager.java index 21df77587b0..a239fdc8ac2 100644 --- a/java/org/gnu/emacs/EmacsWindowManager.java +++ b/java/org/gnu/emacs/EmacsWindowManager.java @@ -238,15 +238,19 @@ && isWindowEligible (consumer, window)) { WindowConsumer consumer; - if (window.getAttachedConsumer () != null) - { - consumer = window.getAttachedConsumer (); + /* Reset window management state. */ + window.previouslyAttached = false; + window.attachmentToken = 0; + + /* Remove WINDOW from the list of active windows. */ + windows.remove (window); + if ((consumer = window.getAttachedConsumer ()) != null) + { consumers.remove (consumer); consumer.destroy (); } - windows.remove (window); pruneWindows (); } commit 3608c1399ffdb130311e2d3674ff4037188d79d8 Author: Po Lu Date: Thu Apr 4 08:32:49 2024 +0800 ; * src/haikufns.c (syms_of_haikufns): Fix indentation. diff --git a/src/haikufns.c b/src/haikufns.c index 9b50099d6ac..870b6f58f02 100644 --- a/src/haikufns.c +++ b/src/haikufns.c @@ -3309,8 +3309,8 @@ syms_of_haikufns (void) Vx_sensitive_text_pointer_shape = Qnil; DEFVAR_BOOL ("haiku-pass-control-tab-to-system", - haiku_pass_control_tab_to_system, - doc: /* Whether or not to pass C-TAB to the system. + haiku_pass_control_tab_to_system, + doc: /* Whether or not to pass C-TAB to the system. Setting this variable will cause Emacs to pass C-TAB to the system (allowing window switching on the Haiku operating system), rather than intercepting it. */); commit ba3bf69f84e5c1143a6ff3b1e7031a10e0beed89 Author: Tor-björn Claesson Date: Wed Apr 3 10:25:59 2024 +0300 New variable haiku-pass-control-tab-to-system Allow passing C-TAB on to the Haiku operating system, fixing window switching when an Emacs frame has focus (Bug#70138). * src/haiku_support.cc (DispatchMessage): Conditionally pass message to BWindow. * src/haiku_support.h: * src/haikufns.c (haiku_should_pass_control_tab_to_system): Add variable haiku-pass-control-tab-to-system, and C function haiku_should_pass_control_tab_to_system. Co-authored-by: Po Lu Copyright-paperwork-exempt: yes diff --git a/etc/NEWS b/etc/NEWS index 2654d9d7995..32cec82f970 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1916,6 +1916,11 @@ The new function 'haiku-notifications-notify' provides a subset of the capabilities of the 'notifications-notify' function in a manner analogous to 'w32-notification-notify'. +** New variable 'haiku-pass-control-tab-to-system'. +This sets whether Emacs should pass C-TAB on to the system instead of +handling it, fixing a problem where window switching would not activate +if an Emacs frame had focus on the Haiku operation system. + +++ ** New value 'if-regular' for the REPLACE argument to 'insert-file-contents'. It results in 'insert-file-contents' erasing the buffer instead of diff --git a/src/haiku_support.cc b/src/haiku_support.cc index 1b9c5acdf14..08e7f29685a 100644 --- a/src/haiku_support.cc +++ b/src/haiku_support.cc @@ -1063,6 +1063,13 @@ class EmacsWindow : public BWindow uint32_t mods = modifiers (); + if (haiku_should_pass_control_tab_to_system () + && (mods & B_CONTROL_KEY) && key == 38) + { + BWindow::DispatchMessage (msg, handler); + return; + } + if (mods & B_SHIFT_KEY) rq.modifiers |= HAIKU_MODIFIER_SHIFT; diff --git a/src/haiku_support.h b/src/haiku_support.h index e9ac7005d75..6c0e5fa7acd 100644 --- a/src/haiku_support.h +++ b/src/haiku_support.h @@ -691,6 +691,8 @@ extern int be_get_display_color_cells (void); extern bool be_is_display_grayscale (void); extern void be_warp_pointer (int, int); +extern bool haiku_should_pass_control_tab_to_system (void); + extern void EmacsView_set_up_double_buffering (void *); extern void EmacsView_disable_double_buffering (void *); extern void EmacsView_flip_and_blit (void *); diff --git a/src/haikufns.c b/src/haikufns.c index 173c1e369df..9b50099d6ac 100644 --- a/src/haikufns.c +++ b/src/haikufns.c @@ -2194,6 +2194,12 @@ haiku_set_use_frame_synchronization (struct frame *f, Lisp_Object arg, be_set_use_frame_synchronization (FRAME_HAIKU_VIEW (f), !NILP (arg)); } +bool +haiku_should_pass_control_tab_to_system (void) +{ + return haiku_pass_control_tab_to_system; +} + DEFUN ("haiku-set-mouse-absolute-pixel-position", @@ -3302,6 +3308,14 @@ syms_of_haikufns (void) doc: /* SKIP: real doc in xfns.c. */); Vx_sensitive_text_pointer_shape = Qnil; + DEFVAR_BOOL ("haiku-pass-control-tab-to-system", + haiku_pass_control_tab_to_system, + doc: /* Whether or not to pass C-TAB to the system. +Setting this variable will cause Emacs to pass C-TAB to the system +(allowing window switching on the Haiku operating system), rather than +intercepting it. */); + haiku_pass_control_tab_to_system = true; + DEFVAR_LISP ("haiku-allowed-ui-colors", Vhaiku_allowed_ui_colors, doc: /* Vector of UI colors that Emacs can look up from the system. If this is set up incorrectly, Emacs can crash when encountering an commit 7a41de3d515077e3895fe9201d7786134c82ca26 Author: Juri Linkov Date: Wed Apr 3 20:42:11 2024 +0300 Simplify tab-line-switch-to-prev-tab and tab-line-switch-to-next-tab. * lisp/tab-line.el (tab-line-switch-to-prev-tab) (tab-line-switch-to-next-tab): Use buffers instead of tabs. diff --git a/lisp/tab-line.el b/lisp/tab-line.el index cc60f94c9c5..8c59f7b2e6d 100644 --- a/lisp/tab-line.el +++ b/lisp/tab-line.el @@ -853,20 +853,15 @@ Its effect is the same as using the `previous-buffer' command (if (eq tab-line-tabs-function #'tab-line-tabs-window-buffers) (switch-to-prev-buffer window) (with-selected-window (or window (selected-window)) - (let* ((tabs (seq-filter - (lambda (tab) (or (bufferp tab) (assq 'buffer tab))) - (funcall tab-line-tabs-function))) - (pos (seq-position - tabs (current-buffer) - (lambda (tab buffer) - (if (bufferp tab) - (eq buffer tab) - (eq buffer (cdr (assq 'buffer tab))))))) - (tab (if pos - (if (and tab-line-switch-cycling (<= pos 0)) - (nth (1- (length tabs)) tabs) - (nth (1- pos) tabs)))) - (buffer (if (bufferp tab) tab (cdr (assq 'buffer tab))))) + (let* ((buffers (seq-keep + (lambda (tab) (or (and (bufferp tab) tab) + (alist-get 'buffer tab))) + (funcall tab-line-tabs-function))) + (pos (seq-position buffers (current-buffer))) + (buffer (when pos + (if (and tab-line-switch-cycling (<= pos 0)) + (nth (1- (length buffers)) buffers) + (nth (1- pos) buffers))))) (when (bufferp buffer) (switch-to-buffer buffer))))))) @@ -879,20 +874,16 @@ Its effect is the same as using the `next-buffer' command (if (eq tab-line-tabs-function #'tab-line-tabs-window-buffers) (switch-to-next-buffer window) (with-selected-window (or window (selected-window)) - (let* ((tabs (seq-filter - (lambda (tab) (or (bufferp tab) (assq 'buffer tab))) - (funcall tab-line-tabs-function))) - (pos (seq-position - tabs (current-buffer) - (lambda (tab buffer) - (if (bufferp tab) - (eq buffer tab) - (eq buffer (cdr (assq 'buffer tab))))))) - (tab (if pos - (if (and tab-line-switch-cycling (<= (length tabs) (1+ pos))) - (car tabs) - (nth (1+ pos) tabs)))) - (buffer (if (bufferp tab) tab (cdr (assq 'buffer tab))))) + (let* ((buffers (seq-keep + (lambda (tab) (or (and (bufferp tab) tab) + (alist-get 'buffer tab))) + (funcall tab-line-tabs-function))) + (pos (seq-position buffers (current-buffer))) + (buffer (when pos + (if (and tab-line-switch-cycling + (<= (length buffers) (1+ pos))) + (car buffers) + (nth (1+ pos) buffers))))) (when (bufferp buffer) (switch-to-buffer buffer))))))) commit 08c80c45ddea17df87fc768a39dff353ccc13d3b Author: Theodor Thornhill Date: Sat Mar 30 20:52:41 2024 +0100 Don't use file-truepath in Eglot (bug#70036) `file-truepath' is slow because of recursive calls and being implemented in lisp. It seems to not be needed in eglot, but it is used behind the scenes in `find-buffer-visiting', thus appearing in profiles. Moving the implementation to a hash map will yield similar performance benefits, but wouldn't require us to rewrite `file-truename' in C. * lisp/progmodes/eglot.el (eglot-lsp-server): Convert 'managed-buffers' to a hashmap. (eglot-uri-to-path): Don't use file-truepath, as it is too slow to be included in the hot path. (eglot--on-shutdown): Use buffers from buffer map. (eglot--managed-mode): Add buffer to map, rather than list. Also remove it from the map on deactivation. (eglot-handle-notification): Expose server and get buffer from the buffer map. diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index f247c43203c..7f4284bf09d 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -1053,8 +1053,8 @@ ACTION is an LSP object of either `CodeAction' or `Command' type." :documentation "Map (DIR -> (WATCH ID1 ID2...)) for `didChangeWatchedFiles'." :initform (make-hash-table :test #'equal) :accessor eglot--file-watches) (managed-buffers - :initform nil - :documentation "List of buffers managed by server." + :documentation "Map (PATH -> BUFFER) for buffers managed by server." + :initform (make-hash-table :test #'equal) :accessor eglot--managed-buffers) (saved-initargs :documentation "Saved initargs for reconnection purposes." @@ -1085,12 +1085,12 @@ ACTION is an LSP object of either `CodeAction' or `Command' type." (defun eglot-path-to-uri (path) "Convert PATH, a file name, to LSP URI string and return it." - (let ((truepath (file-truename path))) + (let ((expanded-path (expand-file-name path))) (if (and (url-type (url-generic-parse-url path)) ;; It might be MS Windows path which includes a drive ;; letter that looks like a URL scheme (bug#59338) (not (and (eq system-type 'windows-nt) - (file-name-absolute-p truepath)))) + (file-name-absolute-p expanded-path)))) ;; Path is already a URI, so forward it to the LSP server ;; untouched. The server should be able to handle it, since ;; it provided this URI to clients in the first place. @@ -1098,11 +1098,11 @@ ACTION is an LSP object of either `CodeAction' or `Command' type." (concat "file://" ;; Add a leading "/" for local MS Windows-style paths. (if (and (eq system-type 'windows-nt) - (not (file-remote-p truepath))) + (not (file-remote-p expanded-path))) "/") (url-hexify-string ;; Again watch out for trampy paths. - (directory-file-name (file-local-name truepath)) + (directory-file-name (file-local-name expanded-path)) eglot--uri-path-allowed-chars))))) (defun eglot-range-region (range &optional markers) @@ -1187,7 +1187,7 @@ PRESERVE-BUFFERS as in `eglot-shutdown', which see." (defun eglot--on-shutdown (server) "Called by jsonrpc.el when SERVER is already dead." ;; Turn off `eglot--managed-mode' where appropriate. - (dolist (buffer (eglot--managed-buffers server)) + (dolist (buffer (map-values (eglot--managed-buffers server))) (let (;; Avoid duplicate shutdowns (github#389) (eglot-autoshutdown nil)) (eglot--when-live-buffer buffer (eglot--managed-mode-off)))) @@ -1992,7 +1992,11 @@ Use `eglot-managed-p' to determine if current buffer is managed.") (add-hook 'eldoc-documentation-functions #'eglot-signature-eldoc-function nil t) (eldoc-mode 1)) - (cl-pushnew (current-buffer) (eglot--managed-buffers (eglot-current-server)))) + + (let ((buffer (current-buffer))) + (puthash (expand-file-name (buffer-file-name buffer)) + buffer + (eglot--managed-buffers (eglot-current-server))))) (t (remove-hook 'after-change-functions #'eglot--after-change t) (remove-hook 'before-change-functions #'eglot--before-change t) @@ -2020,10 +2024,10 @@ Use `eglot-managed-p' to determine if current buffer is managed.") (let ((server eglot--cached-server)) (setq eglot--cached-server nil) (when server - (setf (eglot--managed-buffers server) - (delq (current-buffer) (eglot--managed-buffers server))) + (remhash (expand-file-name (buffer-file-name (current-buffer))) + (eglot--managed-buffers server)) (when (and eglot-autoshutdown - (null (eglot--managed-buffers server))) + (null (map-values (eglot--managed-buffers server)))) (eglot-shutdown server))))))) (defun eglot--managed-mode-off () @@ -2346,7 +2350,7 @@ still unanswered LSP requests to the server\n"))) (remhash token (eglot--progress-reporters server)))))))))) (cl-defmethod eglot-handle-notification - (_server (_method (eql textDocument/publishDiagnostics)) &key uri diagnostics + (server (_method (eql textDocument/publishDiagnostics)) &key uri diagnostics &allow-other-keys) ; FIXME: doesn't respect `eglot-strict-mode' "Handle notification publishDiagnostics." (cl-flet ((eglot--diag-type (sev) @@ -2357,7 +2361,7 @@ still unanswered LSP requests to the server\n"))) (mess (source code message) (concat source (and code (format " [%s]" code)) ": " message))) (if-let* ((path (expand-file-name (eglot-uri-to-path uri))) - (buffer (find-buffer-visiting path))) + (buffer (gethash path (eglot--managed-buffers server)))) (with-current-buffer buffer (cl-loop initially @@ -2842,7 +2846,7 @@ may be called multiple times (respecting the protocol of Try to visit the target file for a richer summary line." (pcase-let* ((file (eglot-uri-to-path uri)) - (visiting (or (find-buffer-visiting file) + (visiting (or (gethash file (eglot--managed-buffers (eglot-current-server))) (gethash uri eglot--temp-location-buffers))) (collect (lambda () (eglot--widening @@ -3542,13 +3546,14 @@ list ((FILENAME EDITS VERSION)...)." (with-current-buffer (get-buffer-create "*EGLOT proposed server changes*") (buffer-disable-undo (current-buffer)) (let ((inhibit-read-only t) - (target (current-buffer))) + (target (current-buffer)) + (managed-buffers (eglot--managed-buffers (eglot-current-server)))) (diff-mode) (erase-buffer) (pcase-dolist (`(,path ,edits ,_) prepared) (with-temp-buffer (let* ((diff (current-buffer)) - (existing-buf (find-buffer-visiting path)) + (existing-buf (gethash path (gethash path managed-buffers))) (existing-buf-label (prin1-to-string existing-buf))) (with-temp-buffer (if existing-buf @@ -3583,7 +3588,8 @@ edit proposed by the server." (eglot--dbind ((VersionedTextDocumentIdentifier) uri version) textDocument (list (eglot-uri-to-path uri) edits version))) - documentChanges))) + documentChanges)) + (managed-buffers (eglot--managed-buffers (eglot-current-server)))) (unless (and changes documentChanges) ;; We don't want double edits, and some servers send both ;; changes and documentChanges. This unless ensures that we @@ -3591,7 +3597,7 @@ edit proposed by the server." (cl-loop for (uri edits) on changes by #'cddr do (push (list (eglot-uri-to-path uri) edits) prepared))) (cl-flet ((notevery-visited-p () - (cl-notevery #'find-buffer-visiting + (cl-notevery (lambda (p) (gethash p managed-buffers)) (mapcar #'car prepared))) (accept-p () (y-or-n-p commit 48dc75f2353152158fa325f5fbf154cdc75e8cfa Author: Po Lu Date: Wed Apr 3 20:43:42 2024 +0800 ; * java/org/gnu/emacs/EmacsView.java (onKeyDown): Delete debugging code. diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 37aeded9938..977ad90310d 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -511,8 +511,6 @@ else if (child.getVisibility () != GONE) && !EmacsNative.shouldForwardMultimediaButtons ()) return false; - Log.d (TAG, "onKeyDown: " + event.toString ()); - window.onKeyDown (keyCode, event); return true; } commit 1f5a2c75e60484d9040e5a6e4912bdbed07c9294 Author: Po Lu Date: Wed Apr 3 20:41:08 2024 +0800 Prevent detachWindow from deleting frames * java/org/gnu/emacs/EmacsWindowManager.java (detachWindow): Remove WINDOW from the window list prior to pruning. diff --git a/java/org/gnu/emacs/EmacsWindowManager.java b/java/org/gnu/emacs/EmacsWindowManager.java index fb4ef6344b2..21df77587b0 100644 --- a/java/org/gnu/emacs/EmacsWindowManager.java +++ b/java/org/gnu/emacs/EmacsWindowManager.java @@ -246,8 +246,8 @@ && isWindowEligible (consumer, window)) consumer.destroy (); } - pruneWindows (); windows.remove (window); + pruneWindows (); } public void commit a0da5d04baeca2c159122b11370f828ba8887b4f Author: Po Lu Date: Wed Apr 3 20:33:58 2024 +0800 ; * java/org/gnu/emacs/EmacsWindow.java (reparentTo): Typo. diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 9b444c3f144..b085614de23 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -1324,7 +1324,7 @@ private static class Coordinate /* Reset window management state. */ previouslyAttached = false; - attachmentToken = false; + attachmentToken = 0; /* Also unparent this view. */ commit ad5c6de2fce7b72eb421ae98eff50f29d0723d2e Author: Po Lu Date: Wed Apr 3 20:30:18 2024 +0800 Revise android.texi to agree with last change * doc/emacs/android.texi (Android Windowing): Revise to agree with last change diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index 01732961998..c9f93429deb 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -866,14 +866,16 @@ by the window manager surpasses a specific threshold, and window deletion by this mechanism is indistinguishable from window deletion by the user. Emacs begins to ignore window deletion after two hours less than the default value of this threshold both to err on the side of -caution, in case the system's record of inactivity and Emacs's differ, +caution, in case the system's record of inactivity and Emacs's disagree, and for the reason that this threshold is open to customization by OS distributors. @item -When the user or the system closes any window created by Emacs on -behalf of a specific frame, Emacs deletes the frame displayed within -that window. +When the user or the system closes any window created by Emacs on behalf +of a specific frame, Emacs deletes the frame displayed within that +window, unless the system is Android 10.0 or later, where such windows +are treated identically to the window created at startup, albeit with no +proviso regarding window inactivity. @end itemize When the system predates Android 5.0, the window manager will not commit 7df66b4762ff51e394b8db03dfffe888bdba0a67 Author: Po Lu Date: Wed Apr 3 20:29:10 2024 +0800 Better align Emacs window management with Android task lifecycles * java/org/gnu/emacs/EmacsActivity.java (onCreate): Permit overriding by child classes. (onDestroy): Minor stylistic adjustments. (getAttachmentToken): New function. * java/org/gnu/emacs/EmacsMultitaskActivity.java (onCreate) (getAttachmentToken): New functions. * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow): : New variables. (onActivityDetached): Remove redundant isFinishing argument. (reparentTo): Reset the foregoing fields before registering with the window manager. * java/org/gnu/emacs/EmacsWindowManager.java (EmacsWindowManager): Rename from EmacsWindowAttachmentManager. (WindowConsumer): New function getAttachmentToken. (isWindowEligible): New function. (registerWindowConsumer, registerWindow, removeWindowConsumer) (detachWindow): Implement a new window management strategy on API 29 and subsequent releases where both varieties of toplevel window are permanently, except when reparented, bound to the activities to which they attach, and Emacs establishes at strategic junctures whether those activities remain present. (getTaskToken, pruneWindows): New functions. diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java index e380b7bfc2a..a939641a752 100644 --- a/java/org/gnu/emacs/EmacsActivity.java +++ b/java/org/gnu/emacs/EmacsActivity.java @@ -50,7 +50,7 @@ import android.widget.FrameLayout; public class EmacsActivity extends Activity - implements EmacsWindowAttachmentManager.WindowConsumer, + implements EmacsWindowManager.WindowConsumer, ViewTreeObserver.OnGlobalLayoutListener { public static final String TAG = "EmacsActivity"; @@ -218,7 +218,7 @@ children and RESETWHENCHILDLESS is set (implying it is a } @Override - public final void + public void onCreate (Bundle savedInstanceState) { FrameLayout.LayoutParams params; @@ -249,7 +249,7 @@ children and RESETWHENCHILDLESS is set (implying it is a EmacsService.startEmacsService (this); /* Add this activity to the list of available activities. */ - EmacsWindowAttachmentManager.MANAGER.registerWindowConsumer (this); + EmacsWindowManager.MANAGER.registerWindowConsumer (this); /* Start observing global layout changes between Jelly Bean and Q. This is required to restore the fullscreen state whenever the @@ -326,16 +326,16 @@ children and RESETWHENCHILDLESS is set (implying it is a public final void onDestroy () { - EmacsWindowAttachmentManager manager; - boolean isMultitask; + EmacsWindowManager manager; + boolean isMultitask, reallyFinishing; - manager = EmacsWindowAttachmentManager.MANAGER; + manager = EmacsWindowManager.MANAGER; /* The activity will die shortly hereafter. If there is a window attached, close it now. */ isMultitask = this instanceof EmacsMultitaskActivity; - manager.removeWindowConsumer (this, (isMultitask - || isReallyFinishing ())); + reallyFinishing = isReallyFinishing (); + manager.removeWindowConsumer (this, isMultitask || reallyFinishing); focusedActivities.remove (this); invalidateFocus (2); @@ -383,7 +383,7 @@ children and RESETWHENCHILDLESS is set (implying it is a { isPaused = true; - EmacsWindowAttachmentManager.MANAGER.noticeIconified (this); + EmacsWindowManager.MANAGER.noticeIconified (this); super.onPause (); } @@ -394,7 +394,7 @@ children and RESETWHENCHILDLESS is set (implying it is a isPaused = false; timeOfLastInteraction = 0; - EmacsWindowAttachmentManager.MANAGER.noticeDeiconified (this); + EmacsWindowManager.MANAGER.noticeDeiconified (this); super.onResume (); } @@ -538,6 +538,14 @@ children and RESETWHENCHILDLESS is set (implying it is a EmacsNative.sendNotificationAction (tag, action); } + + @Override + public long + getAttachmentToken () + { + return -1; /* This is overridden by EmacsMultitaskActivity. */ + } + @Override diff --git a/java/org/gnu/emacs/EmacsMultitaskActivity.java b/java/org/gnu/emacs/EmacsMultitaskActivity.java index 7229e34496e..10963ecfd3f 100644 --- a/java/org/gnu/emacs/EmacsMultitaskActivity.java +++ b/java/org/gnu/emacs/EmacsMultitaskActivity.java @@ -19,11 +19,39 @@ package org.gnu.emacs; -/* This class only exists because EmacsActivity is already defined as - an activity, and the system wants a new class in order to define a - new activity. */ +import android.content.Intent; + +import android.os.Bundle; + +/* In large measure, this class only exists because EmacsActivity is + already defined as an activity, and the system requires that every + new activity be defined by a new class. */ public final class EmacsMultitaskActivity extends EmacsActivity { - -} + /* Token provided by the creator. */ + private long activityToken; + + @Override + public final void + onCreate (Bundle savedInstanceState) + { + Intent intent; + String token; + + intent = getIntent (); + token = EmacsWindowManager.ACTIVITY_TOKEN; + + if (intent != null) + activityToken = intent.getLongExtra (token, -2); + + super.onCreate (savedInstanceState); + } + + @Override + public final long + getAttachmentToken () + { + return activityToken; + } +}; diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 446cd26a3dd..171b427b05b 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -494,7 +494,7 @@ invocation of app_process (through android-emacs) can if (window == null) /* Just return all the windows without a parent. */ - windowList = EmacsWindowAttachmentManager.MANAGER.copyWindows (); + windowList = EmacsWindowManager.MANAGER.copyWindows (); else windowList = window.children; diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 109208b2518..37aeded9938 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -511,6 +511,8 @@ else if (child.getVisibility () != GONE) && !EmacsNative.shouldForwardMultimediaButtons ()) return false; + Log.d (TAG, "onKeyDown: " + event.toString ()); + window.onKeyDown (keyCode, event); return true; } @@ -708,12 +710,12 @@ else if (child.getVisibility () != GONE) contextMenu = null; popupActive = false; - /* It is not possible to know with 100% certainty which activity - is currently displaying the context menu. Loop through each - activity and call `closeContextMenu' instead. */ + /* It is not possible to know with 100% certainty which activity is + currently displaying the context menu. Loop over each activity + and call `closeContextMenu' instead. */ - for (EmacsWindowAttachmentManager.WindowConsumer consumer - : EmacsWindowAttachmentManager.MANAGER.consumers) + for (EmacsWindowManager.WindowConsumer consumer + : EmacsWindowManager.MANAGER.consumers) { if (consumer instanceof EmacsActivity) ((EmacsActivity) consumer).closeContextMenu (); diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 2baede1d2d0..9b444c3f144 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -112,7 +112,7 @@ private static class Coordinate private SparseArray pointerMap; /* The window consumer currently attached, if it exists. */ - private EmacsWindowAttachmentManager.WindowConsumer attached; + private EmacsWindowManager.WindowConsumer attached; /* The window background scratch GC. foreground is always the window background. */ @@ -159,6 +159,16 @@ private static class Coordinate values are -1 if no drag and drop operation is under way. */ private int dndXPosition, dndYPosition; + /* Identifier binding this window to the activity created for it, or + -1 if the window should be attached to system-created activities + (i.e. the activity launched by the system at startup). Value is + meaningless under API level 29 and earlier. */ + public long attachmentToken; + + /* Whether this window should be preserved during window pruning, + and whether this window has previously been attached to a task. */ + public boolean preserve, previouslyAttached; + public EmacsWindow (short handle, final EmacsWindow parent, int x, int y, int width, int height, boolean overrideRedirect) @@ -255,12 +265,12 @@ private static class Coordinate run () { ViewManager parent; - EmacsWindowAttachmentManager manager; + EmacsWindowManager manager; if (EmacsActivity.focusedWindow == EmacsWindow.this) EmacsActivity.focusedWindow = null; - manager = EmacsWindowAttachmentManager.MANAGER; + manager = EmacsWindowManager.MANAGER; view.setVisibility (View.GONE); /* If the window manager is set, use that instead. */ @@ -281,12 +291,12 @@ private static class Coordinate } public void - setConsumer (EmacsWindowAttachmentManager.WindowConsumer consumer) + setConsumer (EmacsWindowManager.WindowConsumer consumer) { attached = consumer; } - public EmacsWindowAttachmentManager.WindowConsumer + public EmacsWindowManager.WindowConsumer getAttachedConsumer () { return attached; @@ -420,7 +430,7 @@ private static class Coordinate public void run () { - EmacsWindowAttachmentManager manager; + EmacsWindowManager manager; WindowManager windowManager; Activity ctx; Object tem; @@ -431,7 +441,7 @@ private static class Coordinate if (!overrideRedirect) { - manager = EmacsWindowAttachmentManager.MANAGER; + manager = EmacsWindowManager.MANAGER; /* If parent is the root window, notice that there are new children available for interested activities to pick @@ -527,9 +537,9 @@ private static class Coordinate public void run () { - EmacsWindowAttachmentManager manager; + EmacsWindowManager manager; - manager = EmacsWindowAttachmentManager.MANAGER; + manager = EmacsWindowManager.MANAGER; view.setVisibility (View.GONE); @@ -809,20 +819,13 @@ private static class Coordinate EmacsActivity.invalidateFocus (gainFocus ? 6 : 5); } - /* Notice that the activity has been detached or destroyed. - - ISFINISHING is set if the activity is not the main activity, or - if the activity was not destroyed in response to explicit user - action. */ + /* Notice that the activity (or its task) has been detached or + destroyed by explicit user action. */ public void - onActivityDetached (boolean isFinishing) + onActivityDetached () { - /* Destroy the associated frame when the activity is detached in - response to explicit user action. */ - - if (isFinishing) - EmacsNative.sendWindowAction (this.handle, 0); + EmacsNative.sendWindowAction (this.handle, 0); } @@ -1312,13 +1315,17 @@ private static class Coordinate public void run () { - EmacsWindowAttachmentManager manager; + EmacsWindowManager manager; ViewManager parent; /* First, detach this window if necessary. */ - manager = EmacsWindowAttachmentManager.MANAGER; + manager = EmacsWindowManager.MANAGER; manager.detachWindow (EmacsWindow.this); + /* Reset window management state. */ + previouslyAttached = false; + attachmentToken = false; + /* Also unparent this view. */ /* If the window manager is set, use that instead. */ @@ -1858,7 +1865,7 @@ else if (type.equals (ClipDescription.MIMETYPE_TEXT_URILIST)) public void recreateActivity () { - final EmacsWindowAttachmentManager.WindowConsumer attached; + final EmacsWindowManager.WindowConsumer attached; attached = this.attached; diff --git a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java deleted file mode 100644 index aae4e2ee49b..00000000000 --- a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java +++ /dev/null @@ -1,211 +0,0 @@ -/* Communication module for Android terminals. -*- c-file-style: "GNU" -*- - -Copyright (C) 2023-2024 Free Software Foundation, Inc. - -This file is part of GNU Emacs. - -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 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 -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU Emacs. If not, see . */ - -package org.gnu.emacs; - -import java.util.ArrayList; -import java.util.List; - -import android.app.ActivityOptions; -import android.content.Intent; -import android.os.Build; -import android.util.Log; - -/* Code to paper over the differences in lifecycles between - "activities" and windows. There are four interfaces to an instance - of this class: - - registerWindowConsumer (WindowConsumer) - registerWindow (EmacsWindow) - removeWindowConsumer (WindowConsumer) - removeWindow (EmacsWindow) - - A WindowConsumer is expected to allow an EmacsWindow to be attached - to it, and be created or destroyed. - - Every time a window is created, registerWindow checks the list of - window consumers. If a consumer exists and does not currently have - a window of its own attached, it gets the new window. Otherwise, - the window attachment manager starts a new consumer. - - Every time a consumer is registered, registerWindowConsumer checks - the list of available windows. If a window exists and is not - currently attached to a consumer, then the consumer gets it. - - Finally, every time a window is removed, the consumer is - destroyed. */ - -public final class EmacsWindowAttachmentManager -{ - private final static String TAG = "EmacsWindowAttachmentManager"; - - /* The single window attachment manager ``object''. */ - public static final EmacsWindowAttachmentManager MANAGER; - - static - { - MANAGER = new EmacsWindowAttachmentManager (); - }; - - public interface WindowConsumer - { - public void attachWindow (EmacsWindow window); - public EmacsWindow getAttachedWindow (); - public void detachWindow (); - public void destroy (); - }; - - /* List of currently attached window consumers. */ - public List consumers; - - /* List of currently attached windows. */ - public List windows; - - public - EmacsWindowAttachmentManager () - { - consumers = new ArrayList (); - windows = new ArrayList (); - } - - public void - registerWindowConsumer (WindowConsumer consumer) - { - consumers.add (consumer); - - for (EmacsWindow window : windows) - { - if (window.getAttachedConsumer () == null) - { - consumer.attachWindow (window); - return; - } - } - - EmacsNative.sendWindowAction ((short) 0, 0); - } - - public synchronized void - registerWindow (EmacsWindow window) - { - Intent intent; - ActivityOptions options; - - if (windows.contains (window)) - /* The window is already registered. */ - return; - - windows.add (window); - - for (WindowConsumer consumer : consumers) - { - if (consumer.getAttachedWindow () == null) - { - consumer.attachWindow (window); - return; - } - } - - intent = new Intent (EmacsService.SERVICE, - EmacsMultitaskActivity.class); - - intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); - - /* Intent.FLAG_ACTIVITY_NEW_DOCUMENT is lamentably unavailable on - older systems than Lolipop. */ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) - intent.addFlags (Intent.FLAG_ACTIVITY_NEW_DOCUMENT); - - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) - EmacsService.SERVICE.startActivity (intent); - else - { - /* Specify the desired window size. */ - options = ActivityOptions.makeBasic (); - options.setLaunchBounds (window.getGeometry ()); - EmacsService.SERVICE.startActivity (intent, - options.toBundle ()); - } - } - - public void - removeWindowConsumer (WindowConsumer consumer, boolean isFinishing) - { - EmacsWindow window; - - window = consumer.getAttachedWindow (); - - if (window != null) - { - consumer.detachWindow (); - window.onActivityDetached (isFinishing); - } - - consumers.remove (consumer); - } - - public synchronized void - detachWindow (EmacsWindow window) - { - WindowConsumer consumer; - - if (window.getAttachedConsumer () != null) - { - consumer = window.getAttachedConsumer (); - - consumers.remove (consumer); - consumer.destroy (); - } - - windows.remove (window); - } - - public void - noticeIconified (WindowConsumer consumer) - { - EmacsWindow window; - - /* If a window is attached, send the appropriate iconification - events. */ - window = consumer.getAttachedWindow (); - - if (window != null) - window.noticeIconified (); - } - - public void - noticeDeiconified (WindowConsumer consumer) - { - EmacsWindow window; - - /* If a window is attached, send the appropriate iconification - events. */ - window = consumer.getAttachedWindow (); - - if (window != null) - window.noticeDeiconified (); - } - - public synchronized List - copyWindows () - { - return new ArrayList (windows); - } -}; diff --git a/java/org/gnu/emacs/EmacsWindowManager.java b/java/org/gnu/emacs/EmacsWindowManager.java new file mode 100644 index 00000000000..fb4ef6344b2 --- /dev/null +++ b/java/org/gnu/emacs/EmacsWindowManager.java @@ -0,0 +1,369 @@ +/* Communication module for Android terminals. -*- c-file-style: "GNU" -*- + +Copyright (C) 2023-2024 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +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 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 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs. If not, see . */ + +package org.gnu.emacs; + +import java.util.ArrayList; +import java.util.List; + +import android.app.ActivityManager.AppTask; +import android.app.ActivityManager; +import android.app.ActivityOptions; +import android.app.TaskInfo; + +import android.content.Context; +import android.content.Intent; + +import android.os.Build; + +import android.util.Log; + +/* Code to paper over the differences in lifecycles between + "activities" and windows. + + Four of the five interfaces to be implemented by an instance of this + class are relevant on all versions of Android: + + registerWindowConsumer (WindowConsumer) + registerWindow (EmacsWindow) + removeWindowConsumer (WindowConsumer) + removeWindow (EmacsWindow) + + A WindowConsumer is expected to allow an EmacsWindow to be attached + to it, and be created or destroyed. + + Whenever a window is created, registerWindow examines the list of + window consumers. If a consumer exists and does not currently have a + window of its own attached, it gets the new window, while otherwise, + the window attachment manager starts a new consumer. Whenever a + consumer is registered, registerWindowConsumer checks the list of + available windows. If a window exists and is not currently attached + to a consumer, then the consumer gets it. Finally, every time a + window is removed, the consumer is destroyed. + + getAttachmentToken () + + should return a token uniquely identifying a consumer, which, on API + 29 and up, enables attributing the tasks of activities to the windows + for which they were created, and with that, consistent interaction + between user-visible window state and their underlying frames. */ + +public final class EmacsWindowManager +{ + private static final String TAG = "EmacsWindowManager"; + public final static String ACTIVITY_TOKEN = "emacs:activity_token"; + + /* The single window attachment manager ``object''. */ + public static final EmacsWindowManager MANAGER; + + /* Monotonically increasing counter from which multitasking activity + tokens are produced. */ + private static long nextActivityToken; + + /* The ActivityManager. */ + private ActivityManager activityManager; + + static + { + MANAGER = new EmacsWindowManager (); + }; + + public interface WindowConsumer + { + public void attachWindow (EmacsWindow window); + public EmacsWindow getAttachedWindow (); + public void detachWindow (); + public void destroy (); + public long getAttachmentToken (); + }; + + /* List of currently attached window consumers. */ + public List consumers; + + /* List of currently attached windows. */ + public List windows; + + public + EmacsWindowManager () + { + consumers = new ArrayList (); + windows = new ArrayList (); + } + + + + + /* Return whether the provided WINDOW should be attached to the window + consumer CONSUMER. */ + + public static boolean + isWindowEligible (WindowConsumer consumer, EmacsWindow window) + { + return (/* The window has yet to be bound. */ + window.attachmentToken == 0 + /* Or has already been bound to CONSUMER. */ + || (window.attachmentToken + == consumer.getAttachmentToken ())); + } + + + + public synchronized void + registerWindowConsumer (WindowConsumer consumer) + { + consumers.add (consumer); + pruneWindows (); + + for (EmacsWindow window : windows) + { + if (window.getAttachedConsumer () == null + /* Don't attach this window to CONSUMER if incompatible. */ + && isWindowEligible (consumer, window)) + { + /* Permantly bind this window to the consumer. */ + window.attachmentToken = consumer.getAttachmentToken (); + window.previouslyAttached = true; + consumer.attachWindow (window); + return; + } + } + + EmacsNative.sendWindowAction ((short) 0, 0); + } + + public synchronized void + registerWindow (EmacsWindow window) + { + Intent intent; + ActivityOptions options; + long token; + + if (windows.contains (window)) + /* The window is already registered. */ + return; + + windows.add (window); + + for (WindowConsumer consumer : consumers) + { + if (consumer.getAttachedWindow () == null + && isWindowEligible (consumer, window)) + { + /* Permantly bind this window to the consumer. */ + window.attachmentToken = consumer.getAttachmentToken (); + window.previouslyAttached = true; + consumer.attachWindow (window); + return; + } + } + + intent = new Intent (EmacsService.SERVICE, + EmacsMultitaskActivity.class); + + intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + + /* Intent.FLAG_ACTIVITY_NEW_DOCUMENT is lamentably unavailable on + older systems than Lolipop. */ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + intent.addFlags (Intent.FLAG_ACTIVITY_NEW_DOCUMENT); + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) + EmacsService.SERVICE.startActivity (intent); + else + { + /* Specify the desired window size. */ + options = ActivityOptions.makeBasic (); + options.setLaunchBounds (window.getGeometry ()); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) + /* Bind this window to the activity in advance, i.e., before + its creation, so that its ID will be recorded in the + RecentTasks list. */ + token = ++nextActivityToken; + else + /* APIs required for linking activities to windows are not + available in earlier Android versions. */ + token = -2; + + window.attachmentToken = token; + intent.putExtra (ACTIVITY_TOKEN, token); + EmacsService.SERVICE.startActivity (intent, options.toBundle ()); + } + + pruneWindows (); + } + + public synchronized void + removeWindowConsumer (WindowConsumer consumer, boolean isFinishing) + { + EmacsWindow window; + + window = consumer.getAttachedWindow (); + + if (window != null) + { + consumer.detachWindow (); + + /* Though pruneWindows will likely remove the same windows, call + onActivityDetached anyway if isFinishing is set, as in + obscure circumstances pruneWindows will not remove frames + bound to the system-started task. */ + if (isFinishing) + window.onActivityDetached (); + } + + pruneWindows (); + consumers.remove (consumer); + } + + public synchronized void + detachWindow (EmacsWindow window) + { + WindowConsumer consumer; + + if (window.getAttachedConsumer () != null) + { + consumer = window.getAttachedConsumer (); + + consumers.remove (consumer); + consumer.destroy (); + } + + pruneWindows (); + windows.remove (window); + } + + public void + noticeIconified (WindowConsumer consumer) + { + EmacsWindow window; + + /* If a window is attached, send the appropriate iconification + events. */ + window = consumer.getAttachedWindow (); + + if (window != null) + window.noticeIconified (); + } + + public void + noticeDeiconified (WindowConsumer consumer) + { + EmacsWindow window; + + /* If a window is attached, send the appropriate iconification + events. */ + window = consumer.getAttachedWindow (); + + if (window != null) + window.noticeDeiconified (); + } + + public synchronized List + copyWindows () + { + return new ArrayList (windows); + } + + + + /* Return the activity token specified in the intent giving rise to + TASK, or 0 if absent. */ + + private static long + getTaskToken (AppTask task) + { + TaskInfo info; + + info = (TaskInfo) task.getTaskInfo (); + return (info.baseIntent != null + ? info.baseIntent.getLongExtra (ACTIVITY_TOKEN, + -1l) + : 0); + } + + /* Iterate over each of Emacs's tasks and remove remaining registered + windows whose tasks no longer exist. This function should be + called upon any event that could plausibly indicate changes in the + task list or as to window management. */ + + private synchronized void + pruneWindows () + { + Object object; + List appTasks; + long taskToken; + boolean set; + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q + || EmacsService.SERVICE == null) + return; + + if (activityManager == null) + { + object + = EmacsService.SERVICE.getSystemService (Context.ACTIVITY_SERVICE); + activityManager = (ActivityManager) object; + } + + appTasks = activityManager.getAppTasks (); + + /* Clear the preserve flag on all toplevel windows. */ + + for (EmacsWindow window : windows) + window.preserve = false; + + for (AppTask task : appTasks) + { + taskToken = getTaskToken (task); + set = false; + + if (taskToken == 0) + continue; + + /* Search for a window with this token. */ + for (EmacsWindow window : windows) + { + if (window.attachmentToken == taskToken) + { + window.preserve = true; + set = true; + } + } + + if (!set) + task.finishAndRemoveTask (); + } + + /* Now remove toplevel windows without activity tasks. */ + + for (EmacsWindow window : windows) + { + if (window.preserve + /* This is not the initial window. */ + || (window.attachmentToken < 1) + /* Nor has it never been attached. */ + || !window.previouslyAttached) + continue; + + window.onActivityDetached (); + } + } +}; commit fa9791fe6af2bbedf1f5bb3e7dd8879a0191ebf1 Author: Mattias Engdegård Date: Wed Apr 3 11:28:49 2024 +0200 Revert "Check if lexical-binding is enabled before warning" This reverts commit 7de192680fdac6938f5704aea0310b85b0da9c5e. The logic is wrong (bug#70068). diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el index 3af7ede4c8e..5cff86784f0 100644 --- a/lisp/emacs-lisp/bytecomp.el +++ b/lisp/emacs-lisp/bytecomp.el @@ -2234,8 +2234,7 @@ See also `emacs-lisp-byte-compile-and-load'." (setq buffer-read-only nil filename buffer-file-name)) ;; Don't inherit lexical-binding from caller (bug#12938). - (unless (or (local-variable-p 'lexical-binding) - (null lexical-binding)) + (unless (local-variable-p 'lexical-binding) (let ((byte-compile-current-buffer (current-buffer))) (displaying-byte-compile-warnings (byte-compile-warn-x commit 0c55261971b0fea62a8e94204e544ff8af714a2e Author: Mattias Engdegård Date: Wed Apr 3 11:27:16 2024 +0200 Make lexical-binding cookie warning test more robust * test/lisp/emacs-lisp/bytecomp-tests.el (bytecomp-tests--lexical-binding-cookie): Run with lexical-binding both on and off. diff --git a/test/lisp/emacs-lisp/bytecomp-tests.el b/test/lisp/emacs-lisp/bytecomp-tests.el index 26408e8685a..a943012e5fc 100644 --- a/test/lisp/emacs-lisp/bytecomp-tests.el +++ b/test/lisp/emacs-lisp/bytecomp-tests.el @@ -1348,12 +1348,14 @@ byte-compiled. Run with dynamic binding." (string-search "file has no `lexical-binding' directive on its first line" (bytecomp-tests--log-from-compilation source)))) - (let ((some-code "(defun my-fun () 12)\n")) - (should-not (cookie-warning - (concat ";;; -*-lexical-binding:t-*-\n" some-code))) - (should-not (cookie-warning - (concat ";;; -*-lexical-binding:nil-*-\n" some-code))) - (should (cookie-warning some-code))))) + (dolist (lb '(t nil)) + (let ((lexical-binding lb) + (some-code "(defun my-fun () 12)\n")) + (should-not (cookie-warning + (concat ";;; -*-lexical-binding:t-*-\n" some-code))) + (should-not (cookie-warning + (concat ";;; -*-lexical-binding:nil-*-\n" some-code))) + (should (cookie-warning some-code)))))) (ert-deftest bytecomp-tests--unescaped-char-literals () "Check that byte compiling warns about unescaped character commit 7de192680fdac6938f5704aea0310b85b0da9c5e Author: Philip Kaludercic Date: Tue Apr 2 18:01:27 2024 +0200 Check if lexical-binding is enabled before warning * lisp/emacs-lisp/bytecomp.el (byte-compile-file): Suppress "file has no `lexical-binding' directive" is the variable is non-nil, as is the case with 'lisp-interaction-mode'. (Bug#70068) diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el index 5cff86784f0..3af7ede4c8e 100644 --- a/lisp/emacs-lisp/bytecomp.el +++ b/lisp/emacs-lisp/bytecomp.el @@ -2234,7 +2234,8 @@ See also `emacs-lisp-byte-compile-and-load'." (setq buffer-read-only nil filename buffer-file-name)) ;; Don't inherit lexical-binding from caller (bug#12938). - (unless (local-variable-p 'lexical-binding) + (unless (or (local-variable-p 'lexical-binding) + (null lexical-binding)) (let ((byte-compile-current-buffer (current-buffer))) (displaying-byte-compile-warnings (byte-compile-warn-x commit 375ba7c8ea4e8c8bc7f08dedfc46884ffb908622 Author: Mattias Engdegård Date: Wed Apr 3 09:52:55 2024 +0200 ; * src/json.c (json_parse_array): Remove unused variable. diff --git a/src/json.c b/src/json.c index 3c4d90d8d0a..33c3289b394 100644 --- a/src/json.c +++ b/src/json.c @@ -1415,7 +1415,6 @@ json_parse_array (struct json_parser *parser) if (parser->available_depth < 0) json_signal_error (parser, Qjson_object_too_deep); - size_t number_of_elements = 0; Lisp_Object *cdr = &result; /* This loop collects the array elements in the object workspace */ @@ -1442,8 +1441,6 @@ json_parse_array (struct json_parser *parser) } c = json_skip_whitespace (parser); - - number_of_elements++; if (c == ']') { parser->available_depth++;