patches: fix C-n/C-p VoiceOver regression - exclude isCtrlNP from re-anchor

When Emacs moves the cursor (emacsMovedCursor=YES), we post
FocusedUIElementChanged on the NSWindow to re-anchor VoiceOver's
browse cursor.  For C-n/C-p this notification races with
AXSelectedTextChanged(granularity=line) and causes VoiceOver to
drop the line-read speech.

Arrow key movement works because VoiceOver intercepts those as AX
selection changes (setAccessibilitySelectedTextRange:), making
voiceoverSetPoint=YES and emacsMovedCursor=NO, so no
FocusedUIElementChanged is posted.

Fix: skip FocusedUIElementChanged for sequential C-n/C-p moves
(isCtrlNP).  AXSelectedTextChanged with direction=next/previous +
granularity=line is sufficient for VoiceOver to read the new line.
FocusedUIElementChanged is only needed for discontiguous jumps
(]], M-<, isearch, xref etc.) where VoiceOver must re-anchor.

Also merge duplicate comment blocks and fix two compile errors
from a64d24c that Martin caught during testing.
This commit is contained in:
2026-03-02 20:48:57 +01:00
parent a64d24cbd9
commit 7a0b4f6cf2
9 changed files with 84 additions and 92 deletions

View File

@@ -1,4 +1,4 @@
From cac0509dbcd958d3bc71cac18c414af1543ca993 Mon Sep 17 00:00:00 2001 From 3f97f3b69fdb10c1781ded98292434525838a369 Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 22:39:35 +0100 Date: Sat, 28 Feb 2026 22:39:35 +0100
Subject: [PATCH 0/8] ns: integrate with macOS Zoom for cursor tracking Subject: [PATCH 0/8] ns: integrate with macOS Zoom for cursor tracking

View File

@@ -1,4 +1,4 @@
From af06bbe06123968e08a2ea81ec2c24995f0a91f6 Mon Sep 17 00:00:00 2001 From 234da3ced54798fe9e4bafb0eae08d571a4ffcfc Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 12:58:11 +0100 Date: Sat, 28 Feb 2026 12:58:11 +0100
Subject: [PATCH 1/8] ns: add accessibility base classes and text extraction Subject: [PATCH 1/8] ns: add accessibility base classes and text extraction

View File

@@ -1,4 +1,4 @@
From 60f0e223190b158679322411b4186b7a378114e7 Mon Sep 17 00:00:00 2001 From 5a786a29a4d4067ce7a75994136f945c49e4624e Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 12:58:11 +0100 Date: Sat, 28 Feb 2026 12:58:11 +0100
Subject: [PATCH 2/8] ns: implement buffer accessibility element (core Subject: [PATCH 2/8] ns: implement buffer accessibility element (core

View File

@@ -1,4 +1,4 @@
From 7bd0a761b78c1c05fe74e150d841bf15686efbb9 Mon Sep 17 00:00:00 2001 From 1dd6de1a46d86a87640129745bb0db01618b2879 Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 12:58:11 +0100 Date: Sat, 28 Feb 2026 12:58:11 +0100
Subject: [PATCH 3/8] ns: add buffer notification dispatch and mode-line Subject: [PATCH 3/8] ns: add buffer notification dispatch and mode-line

View File

@@ -1,4 +1,4 @@
From e4d2760f5e99cb6015fb85e68db37fa766a4ab53 Mon Sep 17 00:00:00 2001 From 3b8838647b39912753157d76b2aa4d8d0da0c55c Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 12:58:11 +0100 Date: Sat, 28 Feb 2026 12:58:11 +0100
Subject: [PATCH 4/8] ns: add interactive span elements for Tab navigation Subject: [PATCH 4/8] ns: add interactive span elements for Tab navigation

View File

@@ -1,4 +1,4 @@
From 7c042d446bddee16a83e4cd8f0050e24e262ef77 Mon Sep 17 00:00:00 2001 From d4cda4bda0bee73c14946f20322975edd1580d46 Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 12:58:11 +0100 Date: Sat, 28 Feb 2026 12:58:11 +0100
Subject: [PATCH 5/8] ns: integrate accessibility with EmacsView and redisplay Subject: [PATCH 5/8] ns: integrate accessibility with EmacsView and redisplay
@@ -23,8 +23,8 @@ com.apple.accessibility.api distributed notification.
(accessibilityAttributeValue:forParameter:): New methods. (accessibilityAttributeValue:forParameter:): New methods.
--- ---
etc/NEWS | 13 ++ etc/NEWS | 13 ++
src/nsterm.m | 430 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/nsterm.m | 474 +++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 431 insertions(+), 12 deletions(-) 2 files changed, 475 insertions(+), 12 deletions(-)
diff --git a/etc/NEWS b/etc/NEWS diff --git a/etc/NEWS b/etc/NEWS
index 4c149e41d6..7f917f93b2 100644 index 4c149e41d6..7f917f93b2 100644
@@ -51,7 +51,7 @@ index 4c149e41d6..7f917f93b2 100644
** Re-introduced dictation, lost in Emacs v30 (macOS). ** Re-introduced dictation, lost in Emacs v30 (macOS).
We lost macOS dictation in v30 when migrating to NSTextInputClient. We lost macOS dictation in v30 when migrating to NSTextInputClient.
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index b460beb00c..95a5b378c1 100644 index b460beb00c..7c118045bd 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -1275,7 +1275,7 @@ If a completion candidate is selected (overlay or child frame), @@ -1275,7 +1275,7 @@ If a completion candidate is selected (overlay or child frame),
@@ -165,7 +165,58 @@ index b460beb00c..95a5b378c1 100644
/* =================================================================== /* ===================================================================
EmacsAccessibilityBuffer (Notifications) — AX event dispatch EmacsAccessibilityBuffer (Notifications) — AX event dispatch
@@ -9347,7 +9396,6 @@ - (NSRect)accessibilityFrame @@ -9235,6 +9284,50 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f
granularity = ns_ax_text_selection_granularity_line;
}
+ /* Programmatic jumps that cross a line boundary (]], [[, M-<,
+ xref, imenu, …) are discontiguous: the cursor teleported to an
+ arbitrary position, not one sequential step forward/backward.
+ Reporting AXTextSelectionDirectionDiscontiguous causes VoiceOver
+ to re-anchor its rotor browse cursor at the new
+ accessibilitySelectedTextRange rather than advancing linearly
+ from its previous internal position. */
+ if (!isCtrlNP && granularity == ns_ax_text_selection_granularity_line)
+ direction = ns_ax_text_selection_direction_discontiguous;
+
+ /* If Emacs moved the cursor (not VoiceOver), force discontiguous
+ so VoiceOver re-anchors its browse cursor to the current
+ accessibilitySelectedTextRange. This covers all Emacs-initiated
+ moves: editing commands, ELisp, isearch, etc.
+ Exception: C-n/C-p (isCtrlNP) already uses next/previous with
+ line granularity; those are already sequential and VoiceOver
+ handles them correctly. */
+ if (emacsMovedCursor && !isCtrlNP)
+ direction = ns_ax_text_selection_direction_discontiguous;
+
+ /* Re-anchor VoiceOver's browse cursor for discontiguous (teleport)
+ moves only. For sequential C-n/C-p (isCtrlNP), posting
+ FocusedUIElementChanged on the window races with the
+ AXSelectedTextChanged(granularity=line) notification and
+ causes VoiceOver to drop the line-read speech. Sequential
+ moves are already handled correctly by AXSelectedTextChanged
+ with direction=next/previous + granularity=line. */
+ if (emacsMovedCursor && !isCtrlNP && [self isAccessibilityFocused])
+ {
+ NSWindow *win = [self.emacsView window];
+ if (win)
+ ns_ax_post_notification (
+ win,
+ NSAccessibilityFocusedUIElementChangedNotification);
+
+ NSDictionary *layoutInfo = @{
+ NSAccessibilityUIElementsKey: @[self]
+ };
+ ns_ax_post_notification_with_info (
+ self.emacsView,
+ NSAccessibilityLayoutChangedNotification,
+ layoutInfo);
+ }
+
/* Post notifications for focused and non-focused elements. */
if ([self isAccessibilityFocused])
[self postFocusedCursorNotification:point
@@ -9347,7 +9440,6 @@ - (NSRect)accessibilityFrame
@end @end
@@ -173,7 +224,7 @@ index b460beb00c..95a5b378c1 100644
/* =================================================================== /* ===================================================================
EmacsAccessibilityInteractiveSpan --- helpers and implementation EmacsAccessibilityInteractiveSpan --- helpers and implementation
=================================================================== */ =================================================================== */
@@ -9682,6 +9730,7 @@ - (void)dealloc @@ -9682,6 +9774,7 @@ - (void)dealloc
[layer release]; [layer release];
#endif #endif
@@ -181,7 +232,7 @@ index b460beb00c..95a5b378c1 100644
[[self menu] release]; [[self menu] release];
[super dealloc]; [super dealloc];
} }
@@ -11030,6 +11079,32 @@ - (void)windowDidBecomeKey /* for direct calls */ @@ -11030,6 +11123,32 @@ - (void)windowDidBecomeKey /* for direct calls */
XSETFRAME (event.frame_or_window, emacsframe); XSETFRAME (event.frame_or_window, emacsframe);
kbd_buffer_store_event (&event); kbd_buffer_store_event (&event);
ns_send_appdefined (-1); // Kick main loop ns_send_appdefined (-1); // Kick main loop
@@ -214,7 +265,7 @@ index b460beb00c..95a5b378c1 100644
} }
@@ -12267,6 +12342,332 @@ - (int) fullscreenState @@ -12267,6 +12386,332 @@ - (int) fullscreenState
return fs_state; return fs_state;
} }
@@ -547,7 +598,7 @@ index b460beb00c..95a5b378c1 100644
@end /* EmacsView */ @end /* EmacsView */
@@ -14263,12 +14664,17 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with @@ -14263,12 +14708,17 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with
ns_use_srgb_colorspace = YES; ns_use_srgb_colorspace = YES;
DEFVAR_BOOL ("ns-accessibility-enabled", ns_accessibility_enabled, DEFVAR_BOOL ("ns-accessibility-enabled", ns_accessibility_enabled,

View File

@@ -1,4 +1,4 @@
From b33ad461f56034270d8461ad48e838f5f36a98da Mon Sep 17 00:00:00 2001 From 33ed790921c1d78dec79f803807deae65fff365e Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 12:58:11 +0100 Date: Sat, 28 Feb 2026 12:58:11 +0100
Subject: [PATCH 6/8] doc: add VoiceOver accessibility section to macOS Subject: [PATCH 6/8] doc: add VoiceOver accessibility section to macOS
@@ -110,10 +110,10 @@ index 6bd334f48e..8d4a7825d8 100644
@section GNUstep Support @section GNUstep Support
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 95a5b378c1..8a1bfd9eb1 100644 index 7c118045bd..a0598a73c2 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -14665,9 +14665,13 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with @@ -14709,9 +14709,13 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with
DEFVAR_BOOL ("ns-accessibility-enabled", ns_accessibility_enabled, DEFVAR_BOOL ("ns-accessibility-enabled", ns_accessibility_enabled,
doc: /* Non-nil enables Zoom cursor tracking and VoiceOver support. doc: /* Non-nil enables Zoom cursor tracking and VoiceOver support.

View File

@@ -1,4 +1,4 @@
From dd8b0911c8a690aa7110b881b6a88977e2fc67f1 Mon Sep 17 00:00:00 2001 From 8c99359156443223d13905de4cfbca58fb3e1177 Mon Sep 17 00:00:00 2001
From: Daneel <daneel@sukany.cz> From: Daneel <daneel@sukany.cz>
Date: Mon, 2 Mar 2026 18:39:46 +0100 Date: Mon, 2 Mar 2026 18:39:46 +0100
Subject: [PATCH 7/8] ns: announce overlay completion candidates for VoiceOver Subject: [PATCH 7/8] ns: announce overlay completion candidates for VoiceOver
@@ -34,7 +34,7 @@ index 5746e9e9bd..21a93bc799 100644
@property (nonatomic, assign) BOOL cachedMarkActive; @property (nonatomic, assign) BOOL cachedMarkActive;
@property (nonatomic, copy) NSString *cachedCompletionAnnouncement; @property (nonatomic, copy) NSString *cachedCompletionAnnouncement;
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 8a1bfd9eb1..523f79d235 100644 index a0598a73c2..3d8a5dd0fc 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -7263,11 +7263,154 @@ Accessibility virtual elements (macOS / Cocoa only) @@ -7263,11 +7263,154 @@ Accessibility virtual elements (macOS / Cocoa only)
@@ -465,7 +465,7 @@ index 8a1bfd9eb1..523f79d235 100644
{ {
ptrdiff_t oldPoint = self.cachedPoint; ptrdiff_t oldPoint = self.cachedPoint;
BOOL oldMarkActive = self.cachedMarkActive; BOOL oldMarkActive = self.cachedMarkActive;
@@ -12358,7 +12604,7 @@ - (int) fullscreenState @@ -12402,7 +12648,7 @@ - (int) fullscreenState
if (WINDOW_LEAF_P (w)) if (WINDOW_LEAF_P (w))
{ {
@@ -474,7 +474,7 @@ index 8a1bfd9eb1..523f79d235 100644
EmacsAccessibilityBuffer *elem EmacsAccessibilityBuffer *elem
= [existing objectForKey:[NSValue valueWithPointer:w]]; = [existing objectForKey:[NSValue valueWithPointer:w]];
if (!elem) if (!elem)
@@ -12392,7 +12638,7 @@ - (int) fullscreenState @@ -12436,7 +12682,7 @@ - (int) fullscreenState
} }
else else
{ {
@@ -483,7 +483,7 @@ index 8a1bfd9eb1..523f79d235 100644
Lisp_Object child = w->contents; Lisp_Object child = w->contents;
while (!NILP (child)) while (!NILP (child))
{ {
@@ -12504,7 +12750,7 @@ - (void)postAccessibilityUpdates @@ -12548,7 +12794,7 @@ - (void)postAccessibilityUpdates
accessibilityUpdating = YES; accessibilityUpdating = YES;
/* Detect window tree change (split, delete, new buffer). Compare /* Detect window tree change (split, delete, new buffer). Compare
@@ -492,7 +492,7 @@ index 8a1bfd9eb1..523f79d235 100644
Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe); Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe);
if (!EQ (curRoot, lastRootWindow)) if (!EQ (curRoot, lastRootWindow))
{ {
@@ -12513,12 +12759,12 @@ - (void)postAccessibilityUpdates @@ -12557,12 +12803,12 @@ - (void)postAccessibilityUpdates
} }
/* If tree is stale, rebuild FIRST so we don't iterate freed /* If tree is stale, rebuild FIRST so we don't iterate freed

View File

@@ -1,4 +1,4 @@
From 13da736610fc631a9ea420d6918eefd2791940d3 Mon Sep 17 00:00:00 2001 From 33333f637c51c1ee2080c780fd623e67d3a85545 Mon Sep 17 00:00:00 2001
From: Daneel <daneel@sukany.cz> From: Daneel <daneel@sukany.cz>
Date: Mon, 2 Mar 2026 18:49:13 +0100 Date: Mon, 2 Mar 2026 18:49:13 +0100
Subject: [PATCH 8/8] ns: announce child frame completion candidates for Subject: [PATCH 8/8] ns: announce child frame completion candidates for
@@ -33,8 +33,8 @@ area announcements.
doc/emacs/macos.texi | 14 +- doc/emacs/macos.texi | 14 +-
etc/NEWS | 18 +- etc/NEWS | 18 +-
src/nsterm.h | 20 ++ src/nsterm.h | 20 ++
src/nsterm.m | 508 ++++++++++++++++++++++++++++++++++++++++--- src/nsterm.m | 456 +++++++++++++++++++++++++++++++++++++++----
4 files changed, 512 insertions(+), 48 deletions(-) 4 files changed, 460 insertions(+), 48 deletions(-)
diff --git a/doc/emacs/macos.texi b/doc/emacs/macos.texi diff --git a/doc/emacs/macos.texi b/doc/emacs/macos.texi
index 8d4a7825d8..03a657f970 100644 index 8d4a7825d8..03a657f970 100644
@@ -149,7 +149,7 @@ index 21a93bc799..bdd40b8eb7 100644
@end @end
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 523f79d235..45f83f3ac6 100644 index 3d8a5dd0fc..7555ae3e95 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -1126,24 +1126,19 @@ Uses CFAbsoluteTimeGetCurrent() (~5 ns, a VDSO read) for timing. */ @@ -1126,24 +1126,19 @@ Uses CFAbsoluteTimeGetCurrent() (~5 ns, a VDSO read) for timing. */
@@ -483,66 +483,7 @@ index 523f79d235..45f83f3ac6 100644
NSInteger direction = ns_ax_text_selection_direction_discontiguous; NSInteger direction = ns_ax_text_selection_direction_discontiguous;
if (point > oldPoint) if (point > oldPoint)
direction = ns_ax_text_selection_direction_next; direction = ns_ax_text_selection_direction_next;
@@ -9530,6 +9698,58 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property @@ -9716,6 +9884,13 @@ - (NSRect)accessibilityFrame
granularity = ns_ax_text_selection_granularity_line;
}
+ /* Programmatic jumps that cross a line boundary (]], [[, M-<,
+ xref, imenu, …) are discontiguous: the cursor teleported to an
+ arbitrary position, not one sequential step forward/backward.
+ Reporting AXTextSelectionDirectionDiscontiguous causes VoiceOver
+ to re-anchor its rotor browse cursor at the new
+ accessibilitySelectedTextRange rather than advancing linearly
+ from its previous internal position. */
+ if (!isCtrlNP && granularity == ns_ax_text_selection_granularity_line)
+ direction = ns_ax_text_selection_direction_discontiguous;
+
+ /* If Emacs moved the cursor (not VoiceOver), force discontiguous
+ so VoiceOver re-anchors its browse cursor to the current
+ accessibilitySelectedTextRange. This covers all Emacs-initiated
+ moves: editing commands, ELisp, isearch, etc.
+ Exception: C-n/C-p (isCtrlNP) already uses next/previous with
+ line granularity; those are already sequential and VoiceOver
+ handles them correctly. */
+ if (emacsMovedCursor && !isCtrlNP)
+ direction = ns_ax_text_selection_direction_discontiguous;
+
+ /* When Emacs moves the cursor (not VoiceOver-initiated),
+ VoiceOver's browse cursor must re-anchor to the new
+ insertion point. Posting FocusedUIElementChanged on self
+ (a custom NSObject-based element, not an NSView) is
+ insufficient \u2014 VoiceOver only re-anchors its browse cursor
+ when it receives FocusedUIElementChanged from the NSWindow,
+ which triggers accessibilityFocusedUIElement to walk the
+ hierarchy and re-anchor at the returned element.
+
+ Post on the window so VoiceOver calls
+ accessibilityFocusedUIElement on it, receives our buffer
+ element, and re-anchors its rotor browse cursor. Also
+ post LayoutChanged with UIElementsKey on the parent view
+ so VoiceOver re-examines our element's properties
+ (including accessibilitySelectedTextRange). */
+ if (emacsMovedCursor && [self isAccessibilityFocused])
+ {
+ NSWindow *win = [self.emacsView window];
+ if (win)
+ ns_ax_post_notification (
+ win,
+ NSAccessibilityFocusedUIElementChangedNotification);
+
+ NSDictionary *layoutInfo = @{
+ NSAccessibilityUIElementsKey: @[self]
+ };
+ ns_ax_post_notification_with_info (
+ self.emacsView,
+ NSAccessibilityLayoutChangedNotification,
+ layoutInfo);
+ }
+
/* Post notifications for focused and non-focused elements. */
if ([self isAccessibilityFocused])
[self postFocusedCursorNotification:point
@@ -9672,6 +9892,13 @@ - (NSRect)accessibilityFrame
if (vis_start >= vis_end) if (vis_start >= vis_end)
return @[]; return @[];
@@ -556,7 +497,7 @@ index 523f79d235..45f83f3ac6 100644
block_input (); block_input ();
specpdl_ref blk_count = SPECPDL_INDEX (); specpdl_ref blk_count = SPECPDL_INDEX ();
record_unwind_protect_void (unblock_input); record_unwind_protect_void (unblock_input);
@@ -9796,6 +10023,7 @@ than O(chars). Fall back to pos+1 as safety net. */ @@ -9840,6 +10015,7 @@ than O(chars). Fall back to pos+1 as safety net. */
pos = span_end; pos = span_end;
} }
@@ -564,7 +505,7 @@ index 523f79d235..45f83f3ac6 100644
return [[spans copy] autorelease]; return [[spans copy] autorelease];
} }
@@ -9977,6 +10205,10 @@ - (void)dealloc @@ -10021,6 +10197,10 @@ - (void)dealloc
#endif #endif
[accessibilityElements release]; [accessibilityElements release];
@@ -575,7 +516,7 @@ index 523f79d235..45f83f3ac6 100644
[[self menu] release]; [[self menu] release];
[super dealloc]; [super dealloc];
} }
@@ -11426,6 +11658,9 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f @@ -11470,6 +11650,9 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f
windowClosing = NO; windowClosing = NO;
processingCompose = NO; processingCompose = NO;
@@ -585,7 +526,7 @@ index 523f79d235..45f83f3ac6 100644
scrollbarsNeedingUpdate = 0; scrollbarsNeedingUpdate = 0;
fs_state = FULLSCREEN_NONE; fs_state = FULLSCREEN_NONE;
fs_before_fs = next_maximized = -1; fs_before_fs = next_maximized = -1;
@@ -12734,6 +12969,154 @@ - (id)accessibilityFocusedUIElement @@ -12778,6 +12961,154 @@ - (id)accessibilityFocusedUIElement
The existing elements carry cached state (modiff, point) from the The existing elements carry cached state (modiff, point) from the
previous redisplay cycle. Rebuilding first would create fresh previous redisplay cycle. Rebuilding first would create fresh
elements with current values, making change detection impossible. */ elements with current values, making change detection impossible. */
@@ -740,7 +681,7 @@ index 523f79d235..45f83f3ac6 100644
- (void)postAccessibilityUpdates - (void)postAccessibilityUpdates
{ {
NSTRACE ("[EmacsView postAccessibilityUpdates]"); NSTRACE ("[EmacsView postAccessibilityUpdates]");
@@ -12744,11 +13127,64 @@ - (void)postAccessibilityUpdates @@ -12788,11 +13119,64 @@ - (void)postAccessibilityUpdates
/* Re-entrance guard: VoiceOver callbacks during notification posting /* Re-entrance guard: VoiceOver callbacks during notification posting
can trigger redisplay, which calls ns_update_end, which calls us can trigger redisplay, which calls ns_update_end, which calls us