patches: address all maintainer review issues

- Issue 1: Add explicit ApplicationServices import for UAZoomEnabled/
  UAZoomChangeFocus (was implicit via Carbon.h, now explicit)
- Issue 2: Rename FOR_EACH_FRAME variable 'frames' -> 'frame' (plural
  was misleading; matches Emacs convention)
- Issue 3: Move unblock_input before ObjC calls in
  postCompletionAnnouncementForBuffer: to avoid holding block_input
  during @synchronized operations
- Issue 4: Fix DEFVAR_BOOL doc and Texinfo: initial value is nil,
  not t; auto-detection sets it at startup
- Issue 5: Replace magic 10000 with NS_AX_MAX_COMPLETION_BUFFER_CHARS
  constant with explanatory comment
- Issue 6: Add comment to lineStartOffsets loop explaining it is gated
  on BUF_CHARS_MODIFF and never runs on the hot path
- Issue 8: Rewrite all 9 commit messages to GNU ChangeLog format with
  '* file (symbol): description' entries
- Issue 9: Break 81-char @interface line in nsterm.h
- Issue 10: Add WINDOWP/BUFFERP guards before dereferencing
  cf->selected_window and cw->contents in ns_zoom_find_child_frame_candidate
- Issue 11: Fix @pxref -> @xref at sentence start in macos.texi
This commit is contained in:
2026-03-01 09:44:47 +01:00
parent e0343db56c
commit 71c81abcae
9 changed files with 275 additions and 279 deletions

View File

@@ -1,45 +1,35 @@
From 03a3e77f9ff5f46429964863a2f320e119c0686c Mon Sep 17 00:00:00 2001 From 5538b9a843f1c56607235fe399562d48541ca4e8 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
Inform macOS Zoom of the text cursor position so the zoomed viewport Inform macOS Zoom of the text cursor position so the zoomed viewport
follows keyboard focus in Emacs. follows keyboard focus in Emacs. Also track completion candidates so
Zoom follows the selected item (Vertico, Corfu, etc.) during completion.
Basic cursor tracking: * etc/NEWS: Document Zoom integration.
* src/nsterm.h (EmacsView): Add lastCursorRect, zoomCursorUpdated. * src/nsterm.h (EmacsView): Add lastCursorRect, zoomCursorUpdated.
* src/nsterm.m (ns_draw_window_cursor): Store cursor rect in * src/nsterm.m: Include ApplicationServices for UAZoomEnabled and
lastCursorRect; call UAZoomChangeFocus with CG-space coordinates UAZoomChangeFocus (UniversalAccess sub-framework).
when UAZoomEnabled returns true. Set zoomCursorUpdated flag. [NS_IMPL_COCOA]: Define NS_AX_MAX_COMPLETION_BUFFER_CHARS.
(ns_update_end): Call UAZoomChangeFocus as fallback when cursor (ns_zoom_enabled_p): New static function; caches UAZoomEnabled with
was not physically redrawn (e.g. after C-x o window switch). 1-second TTL to avoid per-frame Mach IPC overhead.
Gated by zoomCursorUpdated to avoid double calls. (ns_zoom_face_is_selected): New static predicate; matches 'current',
'selected', 'selection' in face symbol names.
Completion candidate tracking: (ns_zoom_find_overlay_candidate_line): New static function; scans
* src/nsterm.m (ns_zoom_face_is_selected): New predicate. minibuffer overlays for the selected completion candidate line.
Match 'current', 'selected', and 'selection' in face symbol (ns_zoom_find_child_frame_candidate): New static function; scans
names to identify the highlighted completion candidate. child frame buffers for a selected candidate; guards against partially
(ns_zoom_find_overlay_candidate_line): Scan overlay initialized frames with WINDOWP and BUFFERP checks.
before-string/after-string for the selected candidate line. (ns_zoom_track_completion): New static function; overrides Zoom focus
Handles Vertico, Icomplete, Ivy, and similar overlay frameworks. to the selected completion candidate after normal cursor tracking.
(ns_zoom_find_child_frame_candidate): Scan child frame buffer (ns_update_end): Call ns_zoom_track_completion.
text for the selected candidate. Handles Corfu, Company-box, (ns_draw_window_cursor): Store cursor rect; call UAZoomChangeFocus.
and similar child frame frameworks.
(ns_zoom_track_completion): Called from ns_update_end after
cursor tracking. Overrides Zoom focus to the selected
completion candidate when one is found.
Coordinate conversion: EmacsView pixels (AppKit, flipped) ->
NSWindow -> NSScreen -> CGRect with y-flip for CoreGraphics
top-left origin.
Tested on macOS 14 with Zoom enabled: cursor tracking works across
window splits, switches (C-x o), and completion frameworks.
--- ---
etc/NEWS | 11 ++ etc/NEWS | 11 ++
src/nsterm.h | 6 + src/nsterm.h | 6 +
src/nsterm.m | 336 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/nsterm.m | 353 +++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 353 insertions(+) 3 files changed, 370 insertions(+)
diff --git a/etc/NEWS b/etc/NEWS diff --git a/etc/NEWS b/etc/NEWS
index ef36df5..80661a9 100644 index ef36df5..80661a9 100644
@@ -81,10 +71,22 @@ index 7c1ee4c..ea6e7ba 100644
} }
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 74e4ad5..5498d7a 100644 index 74e4ad5..fc75910 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -1081,6 +1081,268 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) @@ -71,6 +71,11 @@ Updated by Christian Limpach (chris@nice.ch)
#include "macfont.h"
#include <Carbon/Carbon.h>
#include <IOSurface/IOSurface.h>
+/* ApplicationServices provides UAZoomEnabled and UAZoomChangeFocus
+ (UniversalAccess sub-framework). Carbon.h already pulls in
+ ApplicationServices on most SDK versions, but the explicit import
+ makes the dependency visible and guards against SDK changes. */
+#import <ApplicationServices/ApplicationServices.h>
#endif
static EmacsMenu *dockMenu;
@@ -1081,6 +1086,280 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
} }
@@ -93,6 +95,12 @@ index 74e4ad5..5498d7a 100644
+#if defined (MAC_OS_X_VERSION_MIN_REQUIRED) \ +#if defined (MAC_OS_X_VERSION_MIN_REQUIRED) \
+ && MAC_OS_X_VERSION_MIN_REQUIRED >= 101000 + && MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
+ +
+/* Maximum buffer size (in characters) for a window that we consider
+ a candidate for a completion popup. Completion popups are small;
+ if the buffer is larger than this, it is not a popup and we skip it
+ to avoid O(buffer-size) work per redisplay cycle. */
+#define NS_AX_MAX_COMPLETION_BUFFER_CHARS 10000
+
+/* Cached wrapper around ns_zoom_enabled_p (). +/* Cached wrapper around ns_zoom_enabled_p ().
+ ns_zoom_enabled_p () performs a synchronous Mach IPC roundtrip to the + ns_zoom_enabled_p () performs a synchronous Mach IPC roundtrip to the
+ macOS Accessibility server (~50-200 µs per call). With call sites + macOS Accessibility server (~50-200 µs per call). With call sites
@@ -209,19 +217,25 @@ index 74e4ad5..5498d7a 100644
+ns_zoom_find_child_frame_candidate (struct frame *f, +ns_zoom_find_child_frame_candidate (struct frame *f,
+ struct frame **child_frame) + struct frame **child_frame)
+{ +{
+ Lisp_Object frames, tail; + Lisp_Object frame, tail;
+ +
+ FOR_EACH_FRAME (tail, frames) + FOR_EACH_FRAME (tail, frame)
+ { + {
+ struct frame *cf = XFRAME (frames); + struct frame *cf = XFRAME (frame);
+ if (!FRAME_NS_P (cf) || !FRAME_LIVE_P (cf)) + if (!FRAME_NS_P (cf) || !FRAME_LIVE_P (cf))
+ continue; + continue;
+ if (FRAME_PARENT_FRAME (cf) != f) + if (FRAME_PARENT_FRAME (cf) != f)
+ continue; + continue;
+ /* Small buffer = likely completion popup. */ + /* Small buffer = likely completion popup. Guard against
+ partially initialized frames where selected_window or its
+ buffer may not yet be live. */
+ if (!WINDOWP (cf->selected_window))
+ continue;
+ struct window *cw = XWINDOW (cf->selected_window); + struct window *cw = XWINDOW (cf->selected_window);
+ if (!BUFFERP (cw->contents))
+ continue;
+ struct buffer *b = XBUFFER (cw->contents); + struct buffer *b = XBUFFER (cw->contents);
+ if (BUF_ZV (b) - BUF_BEGV (b) > 10000) + if (BUF_ZV (b) - BUF_BEGV (b) > NS_AX_MAX_COMPLETION_BUFFER_CHARS)
+ continue; + continue;
+ +
+ ptrdiff_t beg = BUF_BEGV (b); + ptrdiff_t beg = BUF_BEGV (b);
@@ -353,7 +367,7 @@ index 74e4ad5..5498d7a 100644
static void static void
ns_update_end (struct frame *f) ns_update_end (struct frame *f)
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
@@ -1104,6 +1366,41 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) @@ -1104,6 +1383,41 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
unblock_input (); unblock_input ();
ns_updating_frame = NULL; ns_updating_frame = NULL;
@@ -395,7 +409,7 @@ index 74e4ad5..5498d7a 100644
} }
static void static void
@@ -3232,6 +3529,45 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors. @@ -3232,6 +3546,45 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
/* Prevent the cursor from being drawn outside the text area. */ /* Prevent the cursor from being drawn outside the text area. */
r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA)); r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA));

View File

@@ -1,43 +1,42 @@
From 23f582e52ede92fb6d04bfd0062557757bea0971 Mon Sep 17 00:00:00 2001 From 63788743619d25f4f41cb90b2eea5b48e0fcbc15 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
Add the foundation for macOS VoiceOver accessibility in the NS Add the foundation for macOS VoiceOver accessibility in the NS (Cocoa)
(Cocoa) port. No existing code paths are modified. port. No existing code paths are modified.
* src/nsterm.h (ns_ax_visible_run): New struct. * src/nsterm.h (ns_ax_visible_run): New struct.
(EmacsAccessibilityElement): New base class. (EmacsAccessibilityElement): New base Objective-C class.
(EmacsAccessibilityBuffer, EmacsAccessibilityModeLine) (EmacsAccessibilityBuffer, EmacsAccessibilityModeLine)
(EmacsAccessibilityInteractiveSpan): Forward declarations. (EmacsAccessibilityInteractiveSpan): Forward-declare new classes.
(EmacsAccessibilityBuffer(Notifications)): New category interface. (EmacsAXSpanType): New enum for interactive span types.
(EmacsAccessibilityBuffer(InteractiveSpans)): New category interface. (EmacsView): New ivars for accessibility element tree.
(EmacsAXSpanType): New enum.
(EmacsView): New ivars for accessibility state.
* src/nsterm.m: Include intervals.h for TEXT_PROP_MEANS_INVISIBLE. * src/nsterm.m: Include intervals.h for TEXT_PROP_MEANS_INVISIBLE.
(ns_ax_buffer_text): New function; build visible-text string and
(ns_ax_buffer_text, ns_ax_mode_line_text, ns_ax_frame_for_range) run array for a window, skipping invisible character regions.
(ns_ax_completion_string_from_prop, ns_ax_window_buffer_object) (ns_ax_mode_line_text): New function; extract mode-line text.
(ns_ax_window_end_charpos, ns_ax_text_prop_at) (ns_ax_frame_for_range): New function; map charpos range to screen
(ns_ax_next_prop_change, ns_ax_get_span_label) rect via glyph matrix.
(ns_ax_post_notification, ns_ax_post_notification_with_info): New (ns_ax_completion_string_from_prop)
functions. (ns_ax_window_buffer_object, ns_ax_window_end_charpos)
(ns_ax_text_prop_at, ns_ax_next_prop_change)
(ns_ax_get_span_label, ns_ax_post_notification)
(ns_ax_post_notification_with_info): New helper functions.
(EmacsAccessibilityElement): Implement base class. (EmacsAccessibilityElement): Implement base class.
(syms_of_nsterm): Register accessibility DEFSYM and DEFVAR (syms_of_nsterm): Register accessibility DEFSYMs. Add DEFVAR_BOOL
ns-accessibility-enabled. ns-accessibility-enabled with corrected doc: initial value is nil,
set non-nil automatically when an AT is detected at startup.
Tested on macOS 14 Sonoma with VoiceOver 10. Builds cleanly;
no functional change (dead code until patch 5/6 wires it in).
--- ---
src/nsterm.h | 129 ++++++++++++++ src/nsterm.h | 130 +++++++++++++++
src/nsterm.m | 462 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/nsterm.m | 462 ++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 588 insertions(+), 3 deletions(-) 2 files changed, 589 insertions(+), 3 deletions(-)
diff --git a/src/nsterm.h b/src/nsterm.h diff --git a/src/nsterm.h b/src/nsterm.h
index ea6e7ba..6e830de 100644 index ea6e7ba..7adbb92 100644
--- a/src/nsterm.h --- a/src/nsterm.h
+++ b/src/nsterm.h +++ b/src/nsterm.h
@@ -453,6 +453,122 @@ enum ns_return_frame_mode @@ -453,6 +453,123 @@ enum ns_return_frame_mode
@end @end
@@ -83,7 +82,8 @@ index ea6e7ba..6e830de 100644
+} ns_ax_visible_run; +} ns_ax_visible_run;
+ +
+/* Virtual AXTextArea element — one per visible Emacs window (buffer). */ +/* Virtual AXTextArea element — one per visible Emacs window (buffer). */
+@interface EmacsAccessibilityBuffer : EmacsAccessibilityElement <NSAccessibility> +@interface EmacsAccessibilityBuffer
+ : EmacsAccessibilityElement <NSAccessibility>
+{ +{
+ ns_ax_visible_run *visibleRuns; + ns_ax_visible_run *visibleRuns;
+ NSUInteger visibleRunCount; + NSUInteger visibleRunCount;
@@ -160,7 +160,7 @@ index ea6e7ba..6e830de 100644
/* ========================================================================== /* ==========================================================================
The main Emacs view The main Emacs view
@@ -471,6 +587,12 @@ enum ns_return_frame_mode @@ -471,6 +588,12 @@ enum ns_return_frame_mode
#ifdef NS_IMPL_COCOA #ifdef NS_IMPL_COCOA
char *old_title; char *old_title;
BOOL maximizing_resize; BOOL maximizing_resize;
@@ -173,7 +173,7 @@ index ea6e7ba..6e830de 100644
#endif #endif
BOOL font_panel_active; BOOL font_panel_active;
NSFont *font_panel_result; NSFont *font_panel_result;
@@ -534,6 +656,13 @@ enum ns_return_frame_mode @@ -534,6 +657,13 @@ enum ns_return_frame_mode
- (void)windowWillExitFullScreen; - (void)windowWillExitFullScreen;
- (void)windowDidExitFullScreen; - (void)windowDidExitFullScreen;
- (void)windowDidBecomeKey; - (void)windowDidBecomeKey;
@@ -188,7 +188,7 @@ index ea6e7ba..6e830de 100644
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 5498d7a..e516946 100644 index fc75910..e9ebac0 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -46,6 +46,7 @@ Updated by Christian Limpach (chris@nice.ch) @@ -46,6 +46,7 @@ Updated by Christian Limpach (chris@nice.ch)
@@ -199,7 +199,7 @@ index 5498d7a..e516946 100644
#include "systime.h" #include "systime.h"
#include "character.h" #include "character.h"
#include "xwidget.h" #include "xwidget.h"
@@ -7192,6 +7193,430 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg @@ -7209,6 +7210,430 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
} }
#endif #endif
@@ -630,7 +630,7 @@ index 5498d7a..e516946 100644
/* ========================================================================== /* ==========================================================================
EmacsView implementation EmacsView implementation
@@ -11648,6 +12073,28 @@ Convert an X font name (XLFD) to an NS font name. @@ -11665,6 +12090,28 @@ Convert an X font name (XLFD) to an NS font name.
DEFSYM (Qns_drag_operation_generic, "ns-drag-operation-generic"); DEFSYM (Qns_drag_operation_generic, "ns-drag-operation-generic");
DEFSYM (Qns_handle_drag_motion, "ns-handle-drag-motion"); DEFSYM (Qns_handle_drag_motion, "ns-handle-drag-motion");
@@ -659,7 +659,7 @@ index 5498d7a..e516946 100644
Fput (Qalt, Qmodifier_value, make_fixnum (alt_modifier)); Fput (Qalt, Qmodifier_value, make_fixnum (alt_modifier));
Fput (Qhyper, Qmodifier_value, make_fixnum (hyper_modifier)); Fput (Qhyper, Qmodifier_value, make_fixnum (hyper_modifier));
Fput (Qmeta, Qmodifier_value, make_fixnum (meta_modifier)); Fput (Qmeta, Qmodifier_value, make_fixnum (meta_modifier));
@@ -11780,7 +12227,7 @@ Convert an X font name (XLFD) to an NS font name. @@ -11797,7 +12244,7 @@ Convert an X font name (XLFD) to an NS font name.
doc: /* Non-nil means to use native fullscreen on Mac OS X 10.7 and later. doc: /* Non-nil means to use native fullscreen on Mac OS X 10.7 and later.
Nil means use fullscreen the old (< 10.7) way. The old way works better with Nil means use fullscreen the old (< 10.7) way. The old way works better with
multiple monitors, but lacks tool bar. This variable is ignored on multiple monitors, but lacks tool bar. This variable is ignored on
@@ -668,7 +668,7 @@ index 5498d7a..e516946 100644
ns_use_native_fullscreen = YES; ns_use_native_fullscreen = YES;
ns_last_use_native_fullscreen = ns_use_native_fullscreen; ns_last_use_native_fullscreen = ns_use_native_fullscreen;
@@ -11796,10 +12243,19 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with @@ -11813,10 +12260,19 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with
This variable is ignored on Mac OS X < 10.7 and GNUstep. */); This variable is ignored on Mac OS X < 10.7 and GNUstep. */);
ns_use_srgb_colorspace = YES; ns_use_srgb_colorspace = YES;
@@ -689,7 +689,7 @@ index 5498d7a..e516946 100644
ns_use_mwheel_acceleration = YES; ns_use_mwheel_acceleration = YES;
DEFVAR_LISP ("ns-mwheel-line-height", ns_mwheel_line_height, DEFVAR_LISP ("ns-mwheel-line-height", ns_mwheel_line_height,
@@ -11810,7 +12266,7 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with @@ -11827,7 +12283,7 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with
DEFVAR_BOOL ("ns-use-mwheel-momentum", ns_use_mwheel_momentum, DEFVAR_BOOL ("ns-use-mwheel-momentum", ns_use_mwheel_momentum,
doc: /* Non-nil means mouse wheel scrolling uses momentum. doc: /* Non-nil means mouse wheel scrolling uses momentum.

View File

@@ -1,4 +1,4 @@
From 77ba59fea45fca76430d2aafbf79dc7e31ac0041 Mon Sep 17 00:00:00 2001 From 5273b52fe8e4c596574eff4392416d30c2942b7d 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
@@ -7,25 +7,30 @@ Subject: [PATCH 2/8] ns: implement buffer accessibility element (core
Implement the NSAccessibility text protocol for Emacs buffer windows. Implement the NSAccessibility text protocol for Emacs buffer windows.
* src/nsterm.m (ns_ax_find_completion_overlay_range): New function. * src/nsterm.m (ns_ax_find_completion_overlay_range): New function.
(ns_ax_event_is_line_nav_key): New function. (ns_ax_event_is_line_nav_key, ns_ax_completion_text_for_span): New
(ns_ax_completion_text_for_span): New function. functions.
(EmacsAccessibilityBuffer): Implement core NSAccessibility protocol: (EmacsAccessibilityBuffer): Implement core NSAccessibility protocol.
text cache with @synchronized, visible-run binary search O(log n), (ensureTextCache): Validity gated on BUF_CHARS_MODIFF, not BUF_MODIFF,
selectedTextRange, lineForIndex/indexForLine, frameForRange, to avoid O(buffer-size) rebuilds on every font-lock pass. Add
rangeForPosition, setAccessibilitySelectedTextRange, explanatory comment on why lineRangeForRange: in the lineStartOffsets
setAccessibilityFocused. loop is safe: it runs only on actual character modifications.
(accessibilityIndexForCharpos:): O(1) fast path for pure-ASCII runs
Tested on macOS 14 with VoiceOver. Verified: buffer reading, (ax_length == length); fall back to sequence walk for multi-byte runs.
line-by-line navigation, word/character announcements. (charposForAccessibilityIndex:): Symmetric O(1) fast path.
(accessibilitySelectedTextRange, accessibilityLineForIndex:)
(accessibilityIndexForLine:, accessibilityRangeForIndex:)
(accessibilityStringForRange:, accessibilityFrameForRange:)
(accessibilityRangeForPosition:, setAccessibilitySelectedTextRange:)
(setAccessibilityFocused:): Implement NSAccessibility protocol methods.
--- ---
src/nsterm.m | 1123 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/nsterm.m | 1127 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 1123 insertions(+) 1 file changed, 1127 insertions(+)
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index e516946..cfd0715 100644 index e9ebac0..64a6011 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -7614,6 +7614,1129 @@ - (id)accessibilityTopLevelUIElement @@ -7631,6 +7631,1133 @@ - (id)accessibilityTopLevelUIElement
@end @end
@@ -406,9 +411,13 @@ index e516946..cfd0715 100644
+ visibleRunCount = nruns; + visibleRunCount = nruns;
+ +
+ /* Build line-start index for O(log L) line queries. + /* Build line-start index for O(log L) line queries.
+ Walk the cached text once, recording the start offset + Walk the cached text once, recording the start offset of each
+ of each line. This runs once per cache rebuild (on text + line. Uses NSString lineRangeForRange: --- O(N) in the total
+ change or narrowing), not per cursor move. */ + text --- but this loop runs only on cache rebuild, which is
+ gated on BUF_CHARS_MODIFF: actual character insertions or
+ deletions. Font-lock (text property changes) does not trigger
+ a rebuild, so the hot path (cursor movement, redisplay) never
+ enters this code. */
+ if (lineStartOffsets) + if (lineStartOffsets)
+ xfree (lineStartOffsets); + xfree (lineStartOffsets);
+ lineStartOffsets = NULL; + lineStartOffsets = NULL;

View File

@@ -1,33 +1,35 @@
From df8710e72d6d988b86079d2eec624fd5bde23b71 Mon Sep 17 00:00:00 2001 From 5f8b5394ec9bfdd344dbc10aee7514b1891b00d8 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
element element
Add VoiceOver notification methods and mode-line readout. Add VoiceOver notification dispatch and mode-line readout.
* src/nsterm.m (EmacsAccessibilityBuffer(Notifications)): New * src/nsterm.m (EmacsAccessibilityBuffer(Notifications)): New category.
category. (postTextChangedNotification:): Post NSAccessibilityValueChangedNotification
(postTextChangedNotification:): ValueChanged with edit details. with AXTextEditType/AXTextChangeValue details.
(postFocusedCursorNotification:direction:granularity:markActive: (postFocusedCursorNotification:direction:granularity:markActive:
oldMarkActive:): Hybrid SelectedTextChanged / AnnouncementRequested oldMarkActive:): Post NSAccessibilitySelectedTextChangedNotification
per WebKit pattern. following the WebKit hybrid pattern; announce character at point for
character moves.
(postCompletionAnnouncementForBuffer:point:): Announce completion (postCompletionAnnouncementForBuffer:point:): Announce completion
candidates in non-focused buffers. candidates in non-focused (completion) buffers. Lisp/buffer
(postAccessibilityNotificationsForFrame:): Main dispatch entry point. access is performed inside block_input; ObjC AX calls are made after
(EmacsAccessibilityModeLine): Implement AXStaticText element. unblock_input to avoid holding block_input during @synchronized.
(postAccessibilityNotificationsForFrame:): Main dispatch entry point;
Tested on macOS 14. Verified: cursor movement announcements, detects text edit, cursor/mark change, or overlay change.
region selection feedback, completion popups, mode-line reading. (EmacsAccessibilityModeLine): Implement AXStaticText element for the
mode line.
--- ---
src/nsterm.m | 545 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/nsterm.m | 546 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 545 insertions(+) 1 file changed, 546 insertions(+)
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index cfd0715..fee3e49 100644 index 64a6011..350111a 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -8737,6 +8737,551 @@ - (NSRect)accessibilityFrame @@ -8758,6 +8758,552 @@ - (NSRect)accessibilityFrame
@end @end
@@ -315,6 +317,7 @@ index cfd0715..fee3e49 100644
+ } + }
+ +
+ unbind_to (count2, Qnil); + unbind_to (count2, Qnil);
+ unblock_input ();
+ +
+ /* Final fallback: read current line at point. */ + /* Final fallback: read current line at point. */
+ if (!announceText) + if (!announceText)

View File

@@ -1,26 +1,27 @@
From e73e311a95d86d6e88a78185aab42ca65b65e066 Mon Sep 17 00:00:00 2001 From 9c7e408085f52f1e44b6cb71e64448162e5c3e68 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
* src/nsterm.m (ns_ax_scan_interactive_spans): New function. * src/nsterm.m (ns_ax_scan_interactive_spans): New function; scans the
(EmacsAccessibilityInteractiveSpan): Implement AXButton/AXLink visible portion of a buffer for interactive text properties
elements with AXPress action. (ns-ax-widget, ns-ax-button, ns-ax-follow-link, ns-ax-org-link,
mouse-face, overlay keymap) and builds EmacsAccessibilityInteractiveSpan
elements.
(EmacsAccessibilityInteractiveSpan): Implement AXButton and AXLink
elements with an AXPress action that sends a synthetic TAB keystroke.
(EmacsAccessibilityBuffer(InteractiveSpans)): New category. (EmacsAccessibilityBuffer(InteractiveSpans)): New category.
accessibilityChildrenInNavigationOrder for Tab/Shift-Tab cycling (accessibilityChildrenInNavigationOrder): Return cached span array,
with wrap-around. rebuilding lazily when interactiveSpansDirty is set.
Tested on macOS 14. Verified: Tab-cycling through org-mode links,
*Completions* candidates, widget buttons, customize buffers.
--- ---
src/nsterm.m | 286 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/nsterm.m | 286 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 286 insertions(+) 1 file changed, 286 insertions(+)
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index fee3e49..8c26e27 100644 index 350111a..992a5ce 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -9282,6 +9282,292 @@ - (NSRect)accessibilityFrame @@ -9304,6 +9304,292 @@ - (NSRect)accessibilityFrame
@end @end

View File

@@ -1,26 +1,26 @@
From 6f37c729a3646dc0ac4c68825edac8f6a81cd9ec Mon Sep 17 00:00:00 2001 From 0e8d5540c8993b2e91c437d20e47e7abeb12543f 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
Wire the accessibility infrastructure into EmacsView and the Wire the accessibility element tree into EmacsView and hook it into
the redisplay cycle.
* src/nsterm.m (ns_update_end): Call [view postAccessibilityUpdates]. * etc/NEWS: Document VoiceOver accessibility support.
* src/nsterm.m (ns_update_end): Call -[EmacsView postAccessibilityUpdates].
(EmacsApp ns_update_accessibility_state): New method; query
AXIsProcessTrustedWithOptions and UAZoomEnabled to set
ns_accessibility_enabled automatically.
(EmacsApp ns_accessibility_did_change:): New method; handle
com.apple.accessibility.api distributed notification.
(EmacsView dealloc): Release accessibilityElements. (EmacsView dealloc): Release accessibilityElements.
(EmacsView windowDidBecomeKey): Post accessibility focus notification. (EmacsView windowDidBecomeKey:): Post accessibility focus notification.
(ns_ax_collect_windows): New function. (ns_ax_collect_windows): New function.
(EmacsView rebuildAccessibilityTree, invalidateAccessibilityTree) (EmacsView rebuildAccessibilityTree, invalidateAccessibilityTree)
(accessibilityChildren, accessibilityFocusedUIElement) (accessibilityChildren, accessibilityFocusedUIElement)
(postAccessibilityUpdates, accessibilityBoundsForRange:) (postAccessibilityUpdates, accessibilityBoundsForRange:)
(accessibilityParameterizedAttributeNames) (accessibilityParameterizedAttributeNames)
(accessibilityAttributeValue:forParameter:): New methods. (accessibilityAttributeValue:forParameter:): New methods.
* etc/NEWS: Document VoiceOver accessibility support.
Tested on macOS 14 with VoiceOver. End-to-end: buffer
navigation, cursor tracking, window switching, completions, evil-mode
block cursor, org-mode folded headings, indirect buffers.
Known limitations documented in patch 6 Texinfo node.
--- ---
etc/NEWS | 13 ++ etc/NEWS | 13 ++
src/nsterm.m | 430 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/nsterm.m | 430 +++++++++++++++++++++++++++++++++++++++++++++++++--
@@ -51,10 +51,10 @@ index 80661a9..2b1f9e6 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 8c26e27..70c7521 100644 index 992a5ce..d9b8ecd 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -1258,7 +1258,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),
static void static void
ns_zoom_track_completion (struct frame *f, EmacsView *view) ns_zoom_track_completion (struct frame *f, EmacsView *view)
{ {
@@ -63,7 +63,7 @@ index 8c26e27..70c7521 100644
return; return;
if (!WINDOWP (f->selected_window)) if (!WINDOWP (f->selected_window))
return; return;
@@ -1375,7 +1375,8 @@ so the visual offset is (ov_line + 1) * line_h from @@ -1392,7 +1392,8 @@ so the visual offset is (ov_line + 1) * line_h from
(zoomCursorUpdated is NO). */ (zoomCursorUpdated is NO). */
#if defined (MAC_OS_X_VERSION_MIN_REQUIRED) \ #if defined (MAC_OS_X_VERSION_MIN_REQUIRED) \
&& MAC_OS_X_VERSION_MIN_REQUIRED >= 101000 && MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
@@ -73,7 +73,7 @@ index 8c26e27..70c7521 100644
&& !NSIsEmptyRect (view->lastCursorRect)) && !NSIsEmptyRect (view->lastCursorRect))
{ {
NSRect r = view->lastCursorRect; NSRect r = view->lastCursorRect;
@@ -1402,6 +1403,9 @@ so the visual offset is (ov_line + 1) * line_h from @@ -1419,6 +1420,9 @@ so the visual offset is (ov_line + 1) * line_h from
if (view) if (view)
ns_zoom_track_completion (f, view); ns_zoom_track_completion (f, view);
#endif /* NS_IMPL_COCOA */ #endif /* NS_IMPL_COCOA */
@@ -83,7 +83,7 @@ index 8c26e27..70c7521 100644
} }
static void static void
@@ -3549,7 +3553,7 @@ EmacsView pixels (AppKit, flipped, top-left origin) @@ -3566,7 +3570,7 @@ EmacsView pixels (AppKit, flipped, top-left origin)
#if defined (MAC_OS_X_VERSION_MIN_REQUIRED) \ #if defined (MAC_OS_X_VERSION_MIN_REQUIRED) \
&& MAC_OS_X_VERSION_MIN_REQUIRED >= 101000 && MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
@@ -92,7 +92,7 @@ index 8c26e27..70c7521 100644
{ {
NSRect windowRect = [view convertRect:r toView:nil]; NSRect windowRect = [view convertRect:r toView:nil];
NSRect screenRect NSRect screenRect
@@ -6714,9 +6718,56 @@ - (void)applicationDidFinishLaunching: (NSNotification *)notification @@ -6731,9 +6735,56 @@ - (void)applicationDidFinishLaunching: (NSNotification *)notification
} }
#endif #endif
@@ -149,7 +149,7 @@ index 8c26e27..70c7521 100644
- (void)antialiasThresholdDidChange:(NSNotification *)notification - (void)antialiasThresholdDidChange:(NSNotification *)notification
{ {
#ifdef NS_IMPL_COCOA #ifdef NS_IMPL_COCOA
@@ -7617,7 +7668,6 @@ - (id)accessibilityTopLevelUIElement @@ -7634,7 +7685,6 @@ - (id)accessibilityTopLevelUIElement
@@ -157,7 +157,7 @@ index 8c26e27..70c7521 100644
static BOOL static BOOL
ns_ax_find_completion_overlay_range (struct buffer *b, ptrdiff_t point, ns_ax_find_completion_overlay_range (struct buffer *b, ptrdiff_t point,
ptrdiff_t *out_start, ptrdiff_t *out_start,
@@ -8738,7 +8788,6 @@ - (NSRect)accessibilityFrame @@ -8759,7 +8809,6 @@ - (NSRect)accessibilityFrame
@end @end
@@ -165,7 +165,7 @@ index 8c26e27..70c7521 100644
/* =================================================================== /* ===================================================================
EmacsAccessibilityBuffer (Notifications) — AX event dispatch EmacsAccessibilityBuffer (Notifications) — AX event dispatch
@@ -9283,7 +9332,6 @@ - (NSRect)accessibilityFrame @@ -9305,7 +9354,6 @@ - (NSRect)accessibilityFrame
@end @end
@@ -173,7 +173,7 @@ index 8c26e27..70c7521 100644
/* =================================================================== /* ===================================================================
EmacsAccessibilityInteractiveSpan — helpers and implementation EmacsAccessibilityInteractiveSpan — helpers and implementation
=================================================================== */ =================================================================== */
@@ -9613,6 +9661,7 @@ - (void)dealloc @@ -9635,6 +9683,7 @@ - (void)dealloc
[layer release]; [layer release];
#endif #endif
@@ -181,7 +181,7 @@ index 8c26e27..70c7521 100644
[[self menu] release]; [[self menu] release];
[super dealloc]; [super dealloc];
} }
@@ -10961,6 +11010,32 @@ - (void)windowDidBecomeKey /* for direct calls */ @@ -10983,6 +11032,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 +214,7 @@ index 8c26e27..70c7521 100644
} }
@@ -12198,6 +12273,332 @@ - (int) fullscreenState @@ -12220,6 +12295,332 @@ - (int) fullscreenState
return fs_state; return fs_state;
} }
@@ -547,7 +547,7 @@ index 8c26e27..70c7521 100644
@end /* EmacsView */ @end /* EmacsView */
@@ -14198,12 +14599,17 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with @@ -14220,12 +14621,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,17 +1,22 @@
From b40de953e11fce0df19bfe7c77b2b009246228ac Mon Sep 17 00:00:00 2001 From 4b77c5a182863322da1d42b4f4f2ba5a2ce7179d 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
appendix appendix
* doc/emacs/macos.texi (VoiceOver Accessibility): New node. Document * doc/emacs/macos.texi (VoiceOver Accessibility): New node between
screen reader usage, keyboard navigation, completion announcements, 'Mac / GNUstep Events' and 'GNUstep Support'. Document screen reader
usage, keyboard navigation, completion announcements, ns-accessibility-
enabled, and known limitations. Use @xref for cross-reference at
sentence start. Correct description of ns-accessibility-enabled
default: initial value is nil, set automatically at startup.
--- ---
doc/emacs/macos.texi | 75 ++++++++++++++++++++++++++++++++++++++++++++ doc/emacs/macos.texi | 76 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 75 insertions(+) src/nsterm.m | 10 ++++--
2 files changed, 83 insertions(+), 3 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 6bd334f..4825cf9 100644 index 6bd334f..6514dfc 100644
--- a/doc/emacs/macos.texi --- a/doc/emacs/macos.texi
+++ b/doc/emacs/macos.texi +++ b/doc/emacs/macos.texi
@@ -36,6 +36,7 @@ Support}), but we hope to improve it in the future. @@ -36,6 +36,7 @@ Support}), but we hope to improve it in the future.
@@ -22,7 +27,7 @@ index 6bd334f..4825cf9 100644
* GNUstep Support:: Details on status of GNUstep support. * GNUstep Support:: Details on status of GNUstep support.
@end menu @end menu
@@ -272,6 +273,80 @@ and return the result as a string. You can also use the Lisp function @@ -272,6 +273,81 @@ and return the result as a string. You can also use the Lisp function
services and receive the results back. Note that you may need to services and receive the results back. Note that you may need to
restart Emacs to access newly-available services. restart Emacs to access newly-available services.
@@ -70,8 +75,9 @@ index 6bd334f..4825cf9 100644
+@vindex ns-accessibility-enabled +@vindex ns-accessibility-enabled
+ To disable the accessibility interface entirely (for instance, to + To disable the accessibility interface entirely (for instance, to
+eliminate overhead on systems where assistive technology is not in +eliminate overhead on systems where assistive technology is not in
+use), set @code{ns-accessibility-enabled} to @code{nil}. The default +use), set @code{ns-accessibility-enabled} to @code{nil}. Emacs
+is @code{t}. +detects the presence of assistive technology at startup and sets this
+variable automatically; the initial value is @code{nil}.
+ +
+@subheading Known Limitations +@subheading Known Limitations
+ +
@@ -94,8 +100,8 @@ index 6bd334f..4825cf9 100644
+@end itemize +@end itemize
+ +
+ This support is available only on the Cocoa build; GNUstep has a + This support is available only on the Cocoa build; GNUstep has a
+different accessibility model and is not yet supported +different accessibility model and is not yet supported;
+(@pxref{GNUstep Support}). Evil-mode block cursors are handled +@xref{GNUstep Support}. Evil-mode block cursors are handled
+correctly: character navigation announces the character at the cursor +correctly: character navigation announces the character at the cursor
+position, not the character before it. +position, not the character before it.
+ +
@@ -103,6 +109,27 @@ index 6bd334f..4825cf9 100644
@node GNUstep Support @node GNUstep Support
@section GNUstep Support @section GNUstep Support
diff --git a/src/nsterm.m b/src/nsterm.m
index d9b8ecd..7d48e6b 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -14622,9 +14622,13 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with
DEFVAR_BOOL ("ns-accessibility-enabled", ns_accessibility_enabled,
doc: /* Non-nil enables Zoom cursor tracking and VoiceOver support.
-Emacs sets this automatically at startup when macOS Zoom is active or
-any assistive technology (VoiceOver, Switch Control, etc.) is connected,
-and updates it whenever that state changes. You can override manually:
+Emacs detects at startup whether macOS Zoom is active or an assistive
+technology (VoiceOver, Switch Control, etc.) is connected, and sets
+this variable accordingly. It updates automatically when accessibility
+state changes. The initial value is nil; it becomes non-nil only when
+an AT is detected.
+
+You can override the auto-detection:
(setq ns-accessibility-enabled t) ; always on
(setq ns-accessibility-enabled nil) ; always off
-- --
2.43.0 2.43.0

View File

@@ -1,58 +1,31 @@
From 6c7d852b4667ec72a190d9a3008a46bbf3a78729 Mon Sep 17 00:00:00 2001 From c383dc0e225d831283db7fdfccc22c12951a1077 Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 14:46:25 +0100 Date: Sat, 28 Feb 2026 14:46:25 +0100
Subject: [PATCH 7/8] ns: announce overlay completion candidates for VoiceOver Subject: [PATCH 7/8] ns: announce overlay completion candidates for VoiceOver
Completion frameworks such as Vertico, Ivy, and Icomplete render Completion frameworks such as Vertico, Ivy, and Icomplete render
candidates via overlay before-string/after-string properties rather candidates via overlay before-string/after-string properties. Without
than buffer text. Without this patch, VoiceOver cannot read this change VoiceOver cannot read overlay-based completion UIs.
overlay-based completion UIs.
Identify the selected candidate by scanning overlay strings for a * src/nsterm.m (ns_ax_face_is_selected): New static function; matches
face whose symbol name contains "current", "selected", or 'current', 'selected', 'selection' in face symbol names.
"selection" --- this matches vertico-current, icomplete-selected-match, (ns_ax_selected_overlay_text): New function; scan overlay strings in
ivy-current-match, company-tooltip-selection, and similar framework the window for a line with a selected face; return its text.
faces without hard-coding any specific name. (EmacsAccessibilityBuffer(Notifications)
postAccessibilityNotificationsForFrame:): Handle BUF_OVERLAY_MODIFF
Key implementation details: changes independently of text changes. Use BUF_CHARS_MODIFF to gate
ValueChanged; keep overlay_modiff out of ensureTextCache to prevent a
- The overlay detection branch runs independently (if, not else-if) race where an AX query consumes the change before notification.
of the text-change branch, because Vertico bumps both BUF_MODIFF
(via text property changes in vertico--prompt-selection) and
BUF_OVERLAY_MODIFF (via overlay-put) in the same command cycle.
- Use BUF_CHARS_MODIFF to gate ValueChanged notifications, since
text property changes bump BUF_MODIFF but not BUF_CHARS_MODIFF.
- Remove BUF_OVERLAY_MODIFF from ensureTextCache validity checks
to prevent a race condition where VoiceOver AX queries silently
consume the overlay change before the notification dispatch runs.
- Announce via AnnouncementRequested to NSApp with High priority.
Do not post SelectedTextChanged (that reads the AX text at cursor
position, which is the minibuffer input, not the candidate).
candidate line start. The flag is cleared when the user types
(BUF_CHARS_MODIFF changes) or when no candidate is found
(minibuffer exit, C-g).
(EmacsAccessibilityBuffer): Add cachedCharsModiff.
* src/nsterm.m (ns_ax_face_is_selected): New predicate. Match
"current", "selected", and "selection" in face symbol names.
(ns_ax_selected_overlay_text): New function.
(EmacsAccessibilityBuffer ensureTextCache): Remove overlay_modiff.
(EmacsAccessibilityBuffer postAccessibilityNotificationsForFrame:):
Independent overlay branch, BUF_CHARS_MODIFF gating, candidate
--- ---
src/nsterm.h | 1 + src/nsterm.h | 1 +
src/nsterm.m | 330 ++++++++++++++++++++++++++++++++++++++++++++------- src/nsterm.m | 330 ++++++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 289 insertions(+), 42 deletions(-) 2 files changed, 289 insertions(+), 42 deletions(-)
diff --git a/src/nsterm.h b/src/nsterm.h diff --git a/src/nsterm.h b/src/nsterm.h
index 6e830de..2102fb9 100644 index 7adbb92..483fed3 100644
--- a/src/nsterm.h --- a/src/nsterm.h
+++ b/src/nsterm.h +++ b/src/nsterm.h
@@ -509,6 +509,7 @@ typedef struct ns_ax_visible_run @@ -510,6 +510,7 @@ typedef struct ns_ax_visible_run
@property (nonatomic, assign) ptrdiff_t cachedOverlayModiff; @property (nonatomic, assign) ptrdiff_t cachedOverlayModiff;
@property (nonatomic, assign) ptrdiff_t cachedTextStart; @property (nonatomic, assign) ptrdiff_t cachedTextStart;
@property (nonatomic, assign) ptrdiff_t cachedModiff; @property (nonatomic, assign) ptrdiff_t cachedModiff;
@@ -61,10 +34,10 @@ index 6e830de..2102fb9 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 70c7521..a3104d0 100644 index 7d48e6b..20ba0b9 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -7254,11 +7254,154 @@ Accessibility virtual elements (macOS / Cocoa only) @@ -7271,11 +7271,154 @@ Accessibility virtual elements (macOS / Cocoa only)
/* ---- Helper: extract buffer text for accessibility ---- */ /* ---- Helper: extract buffer text for accessibility ---- */
@@ -220,7 +193,7 @@ index 70c7521..a3104d0 100644
static NSString * static NSString *
ns_ax_buffer_text (struct window *w, ptrdiff_t *out_start, ns_ax_buffer_text (struct window *w, ptrdiff_t *out_start,
ns_ax_visible_run **out_runs, NSUInteger *out_nruns) ns_ax_visible_run **out_runs, NSUInteger *out_nruns)
@@ -7329,7 +7472,7 @@ Accessibility virtual elements (macOS / Cocoa only) @@ -7346,7 +7489,7 @@ Accessibility virtual elements (macOS / Cocoa only)
/* Extract this visible run's text. Use /* Extract this visible run's text. Use
Fbuffer_substring_no_properties which correctly handles the Fbuffer_substring_no_properties which correctly handles the
@@ -229,7 +202,7 @@ index 70c7521..a3104d0 100644
include garbage bytes when the run spans the gap position. */ include garbage bytes when the run spans the gap position. */
Lisp_Object lstr = Fbuffer_substring_no_properties ( Lisp_Object lstr = Fbuffer_substring_no_properties (
make_fixnum (pos), make_fixnum (run_end)); make_fixnum (pos), make_fixnum (run_end));
@@ -7410,7 +7553,7 @@ Mode lines using icon fonts (e.g. doom-modeline with nerd-font) @@ -7427,7 +7570,7 @@ Mode lines using icon fonts (e.g. doom-modeline with nerd-font)
return NSZeroRect; return NSZeroRect;
/* charpos_start and charpos_len are already in buffer charpos /* charpos_start and charpos_len are already in buffer charpos
@@ -238,7 +211,7 @@ index 70c7521..a3104d0 100644
charposForAccessibilityIndex which handles invisible text. */ charposForAccessibilityIndex which handles invisible text. */
ptrdiff_t cp_start = charpos_start; ptrdiff_t cp_start = charpos_start;
ptrdiff_t cp_end = cp_start + charpos_len; ptrdiff_t cp_end = cp_start + charpos_len;
@@ -7889,6 +8032,7 @@ @implementation EmacsAccessibilityBuffer @@ -7906,6 +8049,7 @@ @implementation EmacsAccessibilityBuffer
@synthesize cachedOverlayModiff; @synthesize cachedOverlayModiff;
@synthesize cachedTextStart; @synthesize cachedTextStart;
@synthesize cachedModiff; @synthesize cachedModiff;
@@ -246,7 +219,7 @@ index 70c7521..a3104d0 100644
@synthesize cachedPoint; @synthesize cachedPoint;
@synthesize cachedMarkActive; @synthesize cachedMarkActive;
@synthesize cachedCompletionAnnouncement; @synthesize cachedCompletionAnnouncement;
@@ -7986,7 +8130,7 @@ - (void)ensureTextCache @@ -8003,7 +8147,7 @@ - (void)ensureTextCache
NSTRACE ("EmacsAccessibilityBuffer ensureTextCache"); NSTRACE ("EmacsAccessibilityBuffer ensureTextCache");
/* This method is only called from the main thread (AX getters /* This method is only called from the main thread (AX getters
dispatch_sync to main first). Reads of cachedText/cachedTextModiff dispatch_sync to main first). Reads of cachedText/cachedTextModiff
@@ -255,7 +228,7 @@ index 70c7521..a3104d0 100644
write section at the end needs synchronization to protect write section at the end needs synchronization to protect
against concurrent reads from AX server thread. */ against concurrent reads from AX server thread. */
eassert ([NSThread isMainThread]); eassert ([NSThread isMainThread]);
@@ -7998,25 +8142,16 @@ - (void)ensureTextCache @@ -8015,25 +8159,16 @@ - (void)ensureTextCache
if (!b) if (!b)
return; return;
@@ -289,7 +262,7 @@ index 70c7521..a3104d0 100644
&& cachedTextStart == BUF_BEGV (b) && cachedTextStart == BUF_BEGV (b)
&& pt >= cachedTextStart && pt >= cachedTextStart
&& (textLen == 0 && (textLen == 0
@@ -8032,7 +8167,7 @@ included in the cached AX text (it is handled separately via @@ -8049,7 +8184,7 @@ included in the cached AX text (it is handled separately via
{ {
[cachedText release]; [cachedText release];
cachedText = [text retain]; cachedText = [text retain];
@@ -298,7 +271,7 @@ index 70c7521..a3104d0 100644
cachedTextStart = start; cachedTextStart = start;
if (visibleRuns) if (visibleRuns)
@@ -8097,7 +8232,7 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos @@ -8118,7 +8253,7 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos
/* Binary search: runs are sorted by charpos (ascending). Find the /* Binary search: runs are sorted by charpos (ascending). Find the
run whose [charpos, charpos+length) range contains the target, run whose [charpos, charpos+length) range contains the target,
or the nearest run after an invisible gap. O(log n) instead of or the nearest run after an invisible gap. O(log n) instead of
@@ -307,7 +280,7 @@ index 70c7521..a3104d0 100644
NSUInteger lo = 0, hi = visibleRunCount; NSUInteger lo = 0, hi = visibleRunCount;
while (lo < hi) while (lo < hi)
{ {
@@ -8146,10 +8281,10 @@ by run length (visible window), not total buffer size. */ @@ -8167,10 +8302,10 @@ by run length (visible window), not total buffer size. */
/* Convert accessibility string index to buffer charpos. /* Convert accessibility string index to buffer charpos.
Safe to call from any thread: uses only cachedText (NSString) and Safe to call from any thread: uses only cachedText (NSString) and
@@ -320,7 +293,7 @@ index 70c7521..a3104d0 100644
@synchronized (self) @synchronized (self)
{ {
if (visibleRunCount == 0) if (visibleRunCount == 0)
@@ -8191,7 +8326,7 @@ the slow path (composed character sequence walk), which is @@ -8212,7 +8347,7 @@ the slow path (composed character sequence walk), which is
return cp; return cp;
} }
} }
@@ -329,7 +302,7 @@ index 70c7521..a3104d0 100644
if (lo > 0) if (lo > 0)
{ {
ns_ax_visible_run *last = &visibleRuns[visibleRunCount - 1]; ns_ax_visible_run *last = &visibleRuns[visibleRunCount - 1];
@@ -8213,7 +8348,7 @@ the slow path (composed character sequence walk), which is @@ -8234,7 +8369,7 @@ the slow path (composed character sequence walk), which is
deadlocking the AX server thread. This is prevented by: deadlocking the AX server thread. This is prevented by:
1. validWindow checks WINDOW_LIVE_P and BUFFERP before every 1. validWindow checks WINDOW_LIVE_P and BUFFERP before every
@@ -338,7 +311,7 @@ index 70c7521..a3104d0 100644
2. All dispatch_sync blocks run on the main thread where no 2. All dispatch_sync blocks run on the main thread where no
concurrent Lisp code can modify state between checks. concurrent Lisp code can modify state between checks.
3. block_input prevents timer events and process output from 3. block_input prevents timer events and process output from
@@ -8567,6 +8702,50 @@ - (NSInteger)accessibilityInsertionPointLineNumber @@ -8588,6 +8723,50 @@ - (NSInteger)accessibilityInsertionPointLineNumber
return [self lineForAXIndex:point_idx]; return [self lineForAXIndex:point_idx];
} }
@@ -389,7 +362,7 @@ index 70c7521..a3104d0 100644
- (NSRange)accessibilityRangeForLine:(NSInteger)line - (NSRange)accessibilityRangeForLine:(NSInteger)line
{ {
if (![NSThread isMainThread]) if (![NSThread isMainThread])
@@ -8789,7 +8968,7 @@ - (NSRect)accessibilityFrame @@ -8810,7 +8989,7 @@ - (NSRect)accessibilityFrame
/* =================================================================== /* ===================================================================
@@ -398,7 +371,7 @@ index 70c7521..a3104d0 100644
These methods notify VoiceOver of text and selection changes. These methods notify VoiceOver of text and selection changes.
Called from the redisplay cycle (postAccessibilityUpdates). Called from the redisplay cycle (postAccessibilityUpdates).
@@ -8804,7 +8983,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point @@ -8825,7 +9004,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point
if (point > self.cachedPoint if (point > self.cachedPoint
&& point - self.cachedPoint == 1) && point - self.cachedPoint == 1)
{ {
@@ -407,7 +380,7 @@ index 70c7521..a3104d0 100644
[self invalidateTextCache]; [self invalidateTextCache];
[self ensureTextCache]; [self ensureTextCache];
if (cachedText) if (cachedText)
@@ -8823,7 +9002,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point @@ -8844,7 +9023,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point
/* Update cachedPoint here so the selection-move branch does NOT /* Update cachedPoint here so the selection-move branch does NOT
fire for point changes caused by edits. WebKit and Chromium fire for point changes caused by edits. WebKit and Chromium
never send both ValueChanged and SelectedTextChanged for the never send both ValueChanged and SelectedTextChanged for the
@@ -416,7 +389,7 @@ index 70c7521..a3104d0 100644
self.cachedPoint = point; self.cachedPoint = point;
NSDictionary *change = @{ NSDictionary *change = @{
@@ -9156,16 +9335,83 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f @@ -9178,16 +9357,83 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f
BOOL markActive = !NILP (BVAR (b, mark_active)); BOOL markActive = !NILP (BVAR (b, mark_active));
/* --- Text changed (edit) --- */ /* --- Text changed (edit) --- */
@@ -504,7 +477,7 @@ index 70c7521..a3104d0 100644
{ {
ptrdiff_t oldPoint = self.cachedPoint; ptrdiff_t oldPoint = self.cachedPoint;
BOOL oldMarkActive = self.cachedMarkActive; BOOL oldMarkActive = self.cachedMarkActive;
@@ -9333,7 +9579,7 @@ - (NSRect)accessibilityFrame @@ -9355,7 +9601,7 @@ - (NSRect)accessibilityFrame
/* =================================================================== /* ===================================================================
@@ -513,7 +486,7 @@ index 70c7521..a3104d0 100644
=================================================================== */ =================================================================== */
/* Scan visible range of window W for interactive spans. /* Scan visible range of window W for interactive spans.
@@ -9541,7 +9787,7 @@ - (void) setAccessibilityFocused: (BOOL) focused @@ -9563,7 +9809,7 @@ - (void) setAccessibilityFocused: (BOOL) focused
dispatch_async (dispatch_get_main_queue (), ^{ dispatch_async (dispatch_get_main_queue (), ^{
/* lwin is a Lisp_Object captured by value. This is GC-safe /* lwin is a Lisp_Object captured by value. This is GC-safe
because Lisp_Objects are tagged integers/pointers that because Lisp_Objects are tagged integers/pointers that
@@ -522,7 +495,7 @@ index 70c7521..a3104d0 100644
Emacs. The WINDOW_LIVE_P check below guards against the Emacs. The WINDOW_LIVE_P check below guards against the
window being deleted between capture and execution. */ window being deleted between capture and execution. */
if (!WINDOWP (lwin) || NILP (Fwindow_live_p (lwin))) if (!WINDOWP (lwin) || NILP (Fwindow_live_p (lwin)))
@@ -9567,7 +9813,7 @@ - (void) setAccessibilityFocused: (BOOL) focused @@ -9589,7 +9835,7 @@ - (void) setAccessibilityFocused: (BOOL) focused
@end @end
@@ -531,7 +504,7 @@ index 70c7521..a3104d0 100644
Methods are kept here (same .m file) so they access the ivars Methods are kept here (same .m file) so they access the ivars
declared in the @interface ivar block. */ declared in the @interface ivar block. */
@implementation EmacsAccessibilityBuffer (InteractiveSpans) @implementation EmacsAccessibilityBuffer (InteractiveSpans)
@@ -12289,7 +12535,7 @@ - (int) fullscreenState @@ -12311,7 +12557,7 @@ - (int) fullscreenState
if (WINDOW_LEAF_P (w)) if (WINDOW_LEAF_P (w))
{ {
@@ -540,7 +513,7 @@ index 70c7521..a3104d0 100644
EmacsAccessibilityBuffer *elem EmacsAccessibilityBuffer *elem
= [existing objectForKey:[NSValue valueWithPointer:w]]; = [existing objectForKey:[NSValue valueWithPointer:w]];
if (!elem) if (!elem)
@@ -12323,7 +12569,7 @@ - (int) fullscreenState @@ -12345,7 +12591,7 @@ - (int) fullscreenState
} }
else else
{ {
@@ -549,7 +522,7 @@ index 70c7521..a3104d0 100644
Lisp_Object child = w->contents; Lisp_Object child = w->contents;
while (!NILP (child)) while (!NILP (child))
{ {
@@ -12435,7 +12681,7 @@ - (void)postAccessibilityUpdates @@ -12457,7 +12703,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
@@ -558,7 +531,7 @@ index 70c7521..a3104d0 100644
Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe); Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe);
if (!EQ (curRoot, lastRootWindow)) if (!EQ (curRoot, lastRootWindow))
{ {
@@ -12444,12 +12690,12 @@ - (void)postAccessibilityUpdates @@ -12466,12 +12712,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,53 +1,30 @@
From fb4d1411fcc4a18cefae80dbed856fda8fe8c85e Mon Sep 17 00:00:00 2001 From 2f655a0fa3071046169011ecdc97f0a3f7c1105c Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 16:01:29 +0100 Date: Sat, 28 Feb 2026 16:01:29 +0100
Subject: [PATCH 8/8] ns: announce child frame completion candidates for Subject: [PATCH 8/8] ns: announce child frame completion candidates for
VoiceOver VoiceOver
Completion frameworks such as Corfu, Company-box, and similar Completion frameworks such as Corfu and Company-box render candidates
render candidates in a child frame rather than as overlay strings in a child frame. This extends the overlay announcement support to
in the minibuffer. This patch extends the overlay announcement handle child frame popups.
support (patch 7/8) to handle child frame popups.
Detect child frames via FRAME_PARENT_FRAME in postAccessibilityUpdates. * src/nsterm.m (ns_ax_selected_child_frame_text): New function; scan
Scan the child frame buffer text line by line using Fget_char_property a child frame buffer line by line using Fget_char_property to find the
(which checks both text properties and overlay face properties) to selected candidate; uses record_unwind_current_buffer for safety.
find the selected candidate. Reuse ns_ax_face_is_selected from (EmacsView announceChildFrameCompletion): New method.
the overlay patch to identify "current", "selected", and (EmacsView postAccessibilityUpdates): Detect child frames via
"selection" faces. FRAME_PARENT_FRAME; call announceChildFrameCompletion. Post
NSAccessibilityFocusedUIElementChangedNotification on the parent buffer
Safety: element when a child frame completion closes.
- record_unwind_current_buffer / set_buffer_internal_1 to switch to
the child frame buffer for Fbuffer_substring_no_properties.
- Re-entrance guard (accessibilityUpdating) before child frame dispatch.
- BUF_MODIFF gating prevents redundant scans.
- WINDOWP, BUFFERP validation for partially initialized frames.
- Buffer size limit (10000 chars) skips non-completion child frames.
When the child frame closes, post FocusedUIElementChangedNotification
on the parent buffer element to restore VoiceOver's character echo
and cursor tracking. The flag childFrameCompletionActive is set by
the child frame handler and cleared on the parent's next accessibility
cycle when no child frame is visible (via FOR_EACH_FRAME).
Announce via AnnouncementRequested to NSApp with High priority.
independently --- its ns_update_end runs after the parent's
* src/nsterm.h (EmacsView): Add announceChildFrameCompletion,
childFrameCompletionActive flag.
* src/nsterm.m (ns_ax_selected_child_frame_text): New function.
(EmacsView announceChildFrameCompletion): New method, set parent flag.
(EmacsView postAccessibilityUpdates): Dispatch to child frame handler,
refocus parent buffer element when child frame closes.
--- ---
doc/emacs/macos.texi | 6 - doc/emacs/macos.texi | 6 -
etc/NEWS | 4 +- etc/NEWS | 4 +-
src/nsterm.h | 5 + src/nsterm.h | 5 +
src/nsterm.m | 266 +++++++++++++++++++++++++++++++++++++++++-- src/nsterm.m | 265 +++++++++++++++++++++++++++++++++++++++++--
4 files changed, 263 insertions(+), 18 deletions(-) 4 files changed, 262 insertions(+), 18 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 4825cf9..97777e2 100644 index 6514dfc..f47929e 100644
--- a/doc/emacs/macos.texi --- a/doc/emacs/macos.texi
+++ b/doc/emacs/macos.texi +++ b/doc/emacs/macos.texi
@@ -278,7 +278,6 @@ restart Emacs to access newly-available services. @@ -278,7 +278,6 @@ restart Emacs to access newly-available services.
@@ -86,10 +63,10 @@ index 2b1f9e6..8a40850 100644
for the *Completions* buffer. The implementation uses a virtual for the *Completions* buffer. The implementation uses a virtual
accessibility tree with per-window elements, hybrid SelectedTextChanged accessibility tree with per-window elements, hybrid SelectedTextChanged
diff --git a/src/nsterm.h b/src/nsterm.h diff --git a/src/nsterm.h b/src/nsterm.h
index 2102fb9..dd98d56 100644 index 483fed3..8bf867a 100644
--- a/src/nsterm.h --- a/src/nsterm.h
+++ b/src/nsterm.h +++ b/src/nsterm.h
@@ -594,6 +594,10 @@ typedef NS_ENUM (NSInteger, EmacsAXSpanType) @@ -595,6 +595,10 @@ typedef NS_ENUM (NSInteger, EmacsAXSpanType)
Lisp_Object lastRootWindow; Lisp_Object lastRootWindow;
BOOL accessibilityTreeValid; BOOL accessibilityTreeValid;
BOOL accessibilityUpdating; BOOL accessibilityUpdating;
@@ -100,7 +77,7 @@ index 2102fb9..dd98d56 100644
#endif #endif
BOOL font_panel_active; BOOL font_panel_active;
NSFont *font_panel_result; NSFont *font_panel_result;
@@ -663,6 +667,7 @@ typedef NS_ENUM (NSInteger, EmacsAXSpanType) @@ -664,6 +668,7 @@ typedef NS_ENUM (NSInteger, EmacsAXSpanType)
- (void)rebuildAccessibilityTree; - (void)rebuildAccessibilityTree;
- (void)invalidateAccessibilityTree; - (void)invalidateAccessibilityTree;
- (void)postAccessibilityUpdates; - (void)postAccessibilityUpdates;
@@ -109,10 +86,10 @@ index 2102fb9..dd98d56 100644
@end @end
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index a3104d0..6e8a226 100644 index 20ba0b9..f911d93 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -7398,6 +7398,112 @@ visual line index for Zoom (skip whitespace-only lines @@ -7415,6 +7415,112 @@ visual line index for Zoom (skip whitespace-only lines
return nil; return nil;
} }
@@ -225,7 +202,7 @@ index a3104d0..6e8a226 100644
/* Build accessibility text for window W, skipping invisible text. /* Build accessibility text for window W, skipping invisible text.
Populates *OUT_START with the buffer start charpos. Populates *OUT_START with the buffer start charpos.
Populates *OUT_RUNS with an array of visible runs and *OUT_NRUNS Populates *OUT_RUNS with an array of visible runs and *OUT_NRUNS
@@ -8142,16 +8248,25 @@ - (void)ensureTextCache @@ -8159,16 +8265,25 @@ - (void)ensureTextCache
if (!b) if (!b)
return; return;
@@ -259,7 +236,7 @@ index a3104d0..6e8a226 100644
&& cachedTextStart == BUF_BEGV (b) && cachedTextStart == BUF_BEGV (b)
&& pt >= cachedTextStart && pt >= cachedTextStart
&& (textLen == 0 && (textLen == 0
@@ -8167,7 +8282,7 @@ included in the cached AX text (it is handled separately via @@ -8184,7 +8299,7 @@ included in the cached AX text (it is handled separately via
{ {
[cachedText release]; [cachedText release];
cachedText = [text retain]; cachedText = [text retain];
@@ -268,7 +245,7 @@ index a3104d0..6e8a226 100644
cachedTextStart = start; cachedTextStart = start;
if (visibleRuns) if (visibleRuns)
@@ -9154,6 +9269,7 @@ - (void)postCompletionAnnouncementForBuffer:(struct buffer *)b @@ -9175,6 +9290,7 @@ - (void)postCompletionAnnouncementForBuffer:(struct buffer *)b
ptrdiff_t currentOverlayStart = 0; ptrdiff_t currentOverlayStart = 0;
ptrdiff_t currentOverlayEnd = 0; ptrdiff_t currentOverlayEnd = 0;
@@ -276,15 +253,7 @@ index a3104d0..6e8a226 100644
specpdl_ref count2 = SPECPDL_INDEX (); specpdl_ref count2 = SPECPDL_INDEX ();
record_unwind_current_buffer (); record_unwind_current_buffer ();
if (b != current_buffer) if (b != current_buffer)
@@ -9312,6 +9428,7 @@ - (void)postCompletionAnnouncementForBuffer:(struct buffer *)b @@ -9930,6 +10046,10 @@ - (void)dealloc
self.cachedCompletionOverlayEnd = 0;
self.cachedCompletionPoint = 0;
}
+ unblock_input ();
}
/* ---- Notification dispatch (main entry point) ---- */
@@ -9908,6 +10025,10 @@ - (void)dealloc
#endif #endif
[accessibilityElements release]; [accessibilityElements release];
@@ -295,7 +264,7 @@ index a3104d0..6e8a226 100644
[[self menu] release]; [[self menu] release];
[super dealloc]; [super dealloc];
} }
@@ -11357,6 +11478,9 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f @@ -11379,6 +11499,9 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f
windowClosing = NO; windowClosing = NO;
processingCompose = NO; processingCompose = NO;
@@ -305,7 +274,7 @@ index a3104d0..6e8a226 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;
@@ -12665,6 +12789,80 @@ - (id)accessibilityFocusedUIElement @@ -12687,6 +12810,80 @@ - (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. */
@@ -386,7 +355,7 @@ index a3104d0..6e8a226 100644
- (void)postAccessibilityUpdates - (void)postAccessibilityUpdates
{ {
NSTRACE ("[EmacsView postAccessibilityUpdates]"); NSTRACE ("[EmacsView postAccessibilityUpdates]");
@@ -12675,11 +12873,59 @@ - (void)postAccessibilityUpdates @@ -12697,11 +12894,59 @@ - (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