patches: regenerate for combined application (Zoom + VoiceOver)

VoiceOver patches 0001-0008 now apply cleanly on top of Zoom patch
0000.  The full set (git am patches/000*.patch) works without
conflicts.  Patch 0005 (integration) merges Zoom fallback and
VoiceOver postAccessibilityUpdates in ns_update_end.
This commit is contained in:
2026-03-01 03:02:46 +01:00
parent 132e32795f
commit 74fcee0820
9 changed files with 96 additions and 100 deletions

View File

@@ -1,7 +1,7 @@
From 085a2c40d1335819b7a0d43b67581cc7b547088f Mon Sep 17 00:00:00 2001 From 1c0ca763f978e02dd7b3968c3515dd7b41136f8f 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] ns: integrate with macOS Zoom for cursor tracking Subject: [PATCH 1/9] 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.

View File

@@ -1,7 +1,7 @@
From 4f148cc62b903aac6f9b98832d9f543729964d65 Mon Sep 17 00:00:00 2001 From 3b9fa3443a4a2527c45243f9d0f51f29e2715bd4 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 2/9] 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) port. No existing code paths are modified. (Cocoa) port. No existing code paths are modified.
@@ -34,7 +34,7 @@ no functional change (dead code until patch 5/6 wires it in).
2 files changed, 585 insertions(+) 2 files changed, 585 insertions(+)
diff --git a/src/nsterm.h b/src/nsterm.h diff --git a/src/nsterm.h b/src/nsterm.h
index 7c1ee4c..8ea15b1 100644 index ea6e7ba..6e830de 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,122 @@ enum ns_return_frame_mode
@@ -173,7 +173,7 @@ index 7c1ee4c..8ea15b1 100644
#endif #endif
BOOL font_panel_active; BOOL font_panel_active;
NSFont *font_panel_result; NSFont *font_panel_result;
@@ -528,6 +650,13 @@ enum ns_return_frame_mode @@ -534,6 +656,13 @@ enum ns_return_frame_mode
- (void)windowWillExitFullScreen; - (void)windowWillExitFullScreen;
- (void)windowDidExitFullScreen; - (void)windowDidExitFullScreen;
- (void)windowDidBecomeKey; - (void)windowDidBecomeKey;
@@ -188,7 +188,7 @@ index 7c1ee4c..8ea15b1 100644
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 74e4ad5..2ac1d9d 100644 index cd721c8..1320a9f 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 74e4ad5..2ac1d9d 100644
#include "systime.h" #include "systime.h"
#include "character.h" #include "character.h"
#include "xwidget.h" #include "xwidget.h"
@@ -6856,6 +6857,430 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg @@ -6924,6 +6925,430 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
} }
#endif #endif
@@ -630,7 +630,7 @@ index 74e4ad5..2ac1d9d 100644
/* ========================================================================== /* ==========================================================================
EmacsView implementation EmacsView implementation
@@ -11312,6 +11737,28 @@ Convert an X font name (XLFD) to an NS font name. @@ -11380,6 +11805,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 74e4ad5..2ac1d9d 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));
@@ -11460,6 +11907,15 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with @@ -11528,6 +11975,15 @@ 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;

View File

@@ -1,7 +1,7 @@
From 8a27823b4cbe34959582d7557eb0a418103c8a80 Mon Sep 17 00:00:00 2001 From a67b6d5c2597eb8e927228b9ed8a0d170a0ac8d2 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 3/9] ns: implement buffer accessibility element (core
protocol) protocol)
Implement the NSAccessibility text protocol for Emacs buffer windows. Implement the NSAccessibility text protocol for Emacs buffer windows.
@@ -22,10 +22,10 @@ line-by-line navigation, word/character announcements.
1 file changed, 1097 insertions(+) 1 file changed, 1097 insertions(+)
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 2ac1d9d..1cc824f 100644 index 1320a9f..ab9a287 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -7278,6 +7278,1103 @@ - (id)accessibilityTopLevelUIElement @@ -7346,6 +7346,1103 @@ - (id)accessibilityTopLevelUIElement
@end @end

View File

@@ -1,7 +1,7 @@
From 171e5fb9f27a4a1c62ddd2fc9246f29d8e2c224b Mon Sep 17 00:00:00 2001 From c8b569acd209271b60d95ffe8440f55ad0e90ab4 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 4/9] ns: add buffer notification dispatch and mode-line
element element
Add VoiceOver notification methods and mode-line readout. Add VoiceOver notification methods and mode-line readout.
@@ -24,10 +24,10 @@ region selection feedback, completion popups, mode-line reading.
1 file changed, 545 insertions(+) 1 file changed, 545 insertions(+)
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 1cc824f..30a89f0 100644 index ab9a287..330667d 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -8375,6 +8375,551 @@ - (NSRect)accessibilityFrame @@ -8443,6 +8443,551 @@ - (NSRect)accessibilityFrame
@end @end

View File

@@ -1,7 +1,7 @@
From 96763efb71773ed424230cb1773f8ce2ed7a5ac8 Mon Sep 17 00:00:00 2001 From 00c1d32d52789b8c74d53adf58871cf9e8d899c5 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 5/9] 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.
(EmacsAccessibilityInteractiveSpan): Implement AXButton/AXLink (EmacsAccessibilityInteractiveSpan): Implement AXButton/AXLink
@@ -17,10 +17,10 @@ Tested on macOS 14. Verified: Tab-cycling through org-mode links,
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 30a89f0..3a76a0f 100644 index 330667d..907ce47 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -8920,6 +8920,292 @@ - (NSRect)accessibilityFrame @@ -8988,6 +8988,292 @@ - (NSRect)accessibilityFrame
@end @end

View File

@@ -1,7 +1,7 @@
From af7342d4f5f67531b01f636e00885d40a4edeea5 Mon Sep 17 00:00:00 2001 From fc690cc07ee404cbc8a2733d4cdb6d9d19f30b2e 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 6/9] ns: integrate accessibility with EmacsView and redisplay
Wire the accessibility infrastructure into EmacsView and the Wire the accessibility infrastructure into EmacsView and the
@@ -24,14 +24,14 @@ Known limitations documented in patch 6 Texinfo node.
--- ---
etc/NEWS | 13 ++ etc/NEWS | 13 ++
src/nsterm.h | 2 + src/nsterm.h | 2 +
src/nsterm.m | 373 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/nsterm.m | 369 +++++++++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 385 insertions(+), 3 deletions(-) 3 files changed, 376 insertions(+), 8 deletions(-)
diff --git a/etc/NEWS b/etc/NEWS diff --git a/etc/NEWS b/etc/NEWS
index ef36df5..e76ee93 100644 index f10d17e..f48d05b 100644
--- a/etc/NEWS --- a/etc/NEWS
+++ b/etc/NEWS +++ b/etc/NEWS
@@ -4389,6 +4389,19 @@ allowing Emacs users access to speech recognition utilities. @@ -4397,6 +4397,19 @@ allowing Emacs users access to speech recognition utilities.
Note: Accepting this permission allows the use of system APIs, which may Note: Accepting this permission allows the use of system APIs, which may
send user data to Apple's speech recognition servers. send user data to Apple's speech recognition servers.
@@ -52,7 +52,7 @@ index ef36df5..e76ee93 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.h b/src/nsterm.h diff --git a/src/nsterm.h b/src/nsterm.h
index 8ea15b1..d308a71 100644 index 6e830de..dd4e3da 100644
--- a/src/nsterm.h --- a/src/nsterm.h
+++ b/src/nsterm.h +++ b/src/nsterm.h
@@ -593,6 +593,8 @@ typedef NS_ENUM (NSInteger, EmacsAXSpanType) @@ -593,6 +593,8 @@ typedef NS_ENUM (NSInteger, EmacsAXSpanType)
@@ -65,41 +65,37 @@ index 8ea15b1..d308a71 100644
BOOL font_panel_active; BOOL font_panel_active;
NSFont *font_panel_result; NSFont *font_panel_result;
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 3a76a0f..156dba9 100644 index 907ce47..d813274 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -1105,6 +1105,11 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) @@ -1133,6 +1133,9 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
if (view)
unblock_input (); view->zoomCursorUpdated = NO;
ns_updating_frame = NULL; #endif
+ +
+#ifdef NS_IMPL_COCOA
+ /* Post accessibility notifications after each redisplay cycle. */ + /* Post accessibility notifications after each redisplay cycle. */
+ [view postAccessibilityUpdates]; + [view postAccessibilityUpdates];
+#endif #endif /* NS_IMPL_COCOA */
} }
static void @@ -3263,11 +3266,11 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
@@ -3233,6 +3238,18 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
/* 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));
+#ifdef NS_IMPL_COCOA #ifdef NS_IMPL_COCOA
+ /* Accessibility: store cursor rect for VoiceOver bounds queries. - /* Zoom integration: inform macOS Zoom of the cursor position.
+ accessibilityBoundsForRange: / accessibilityFrameForRange: - Zoom (System Settings -> Accessibility -> Zoom) tracks a focus
+ use this as a fallback when no valid window/glyph data is - element to keep the zoomed viewport centered on the cursor.
+ available. Skipped when ns-accessibility-enabled is nil. */ -
+ { - Coordinate conversion:
+ EmacsView *view = FRAME_NS_VIEW (f); + /* Store cursor rect and inform macOS Zoom / VoiceOver.
+ if (view && on_p && active_p && ns_accessibility_enabled) + lastCursorRect is used by:
+ view->lastCursorRect = r; + - Zoom: UAZoomChangeFocus below (unconditional when active)
+ } + - VoiceOver: accessibilityBoundsForRange: fallback
+#endif + Coordinate conversion for Zoom:
+ EmacsView pixels (AppKit, flipped, top-left origin)
ns_focus (f, NULL, 0); -> NSWindow (convertRect:toView:nil)
-> NSScreen (convertRectToScreen:)
NSGraphicsContext *ctx = [NSGraphicsContext currentContext]; @@ -7349,7 +7352,6 @@ - (id)accessibilityTopLevelUIElement
@@ -7281,7 +7298,6 @@ - (id)accessibilityTopLevelUIElement
@@ -107,7 +103,7 @@ index 3a76a0f..156dba9 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,
@@ -8376,7 +8392,6 @@ - (NSRect)accessibilityFrame @@ -8444,7 +8446,6 @@ - (NSRect)accessibilityFrame
@end @end
@@ -115,7 +111,7 @@ index 3a76a0f..156dba9 100644
/* =================================================================== /* ===================================================================
EmacsAccessibilityBuffer (Notifications) — AX event dispatch EmacsAccessibilityBuffer (Notifications) — AX event dispatch
@@ -8921,7 +8936,6 @@ - (NSRect)accessibilityFrame @@ -8989,7 +8990,6 @@ - (NSRect)accessibilityFrame
@end @end
@@ -123,7 +119,7 @@ index 3a76a0f..156dba9 100644
/* =================================================================== /* ===================================================================
EmacsAccessibilityInteractiveSpan — helpers and implementation EmacsAccessibilityInteractiveSpan — helpers and implementation
=================================================================== */ =================================================================== */
@@ -9251,6 +9265,7 @@ - (void)dealloc @@ -9319,6 +9319,7 @@ - (void)dealloc
[layer release]; [layer release];
#endif #endif
@@ -131,7 +127,7 @@ index 3a76a0f..156dba9 100644
[[self menu] release]; [[self menu] release];
[super dealloc]; [super dealloc];
} }
@@ -10599,6 +10614,32 @@ - (void)windowDidBecomeKey /* for direct calls */ @@ -10667,6 +10668,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
@@ -164,7 +160,7 @@ index 3a76a0f..156dba9 100644
} }
@@ -11836,6 +11877,332 @@ - (int) fullscreenState @@ -11904,6 +11931,332 @@ - (int) fullscreenState
return fs_state; return fs_state;
} }

View File

@@ -1,7 +1,7 @@
From 970373177e737b5050ce4160c6609a21b2501e85 Mon Sep 17 00:00:00 2001 From f2e85cbcb428a0a405fe84f3342dcd1f88c7745e 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 7/9] 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. Document

View File

@@ -1,7 +1,7 @@
From d8a9886e85772c934b9f619251f301ee9baddc84 Mon Sep 17 00:00:00 2001 From 6e0de5153029aa55e8f62eddfe5cba7c25d74278 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 8/9] 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 rather
@@ -49,7 +49,7 @@ Independent overlay branch, BUF_CHARS_MODIFF gating, candidate
2 files changed, 276 insertions(+), 31 deletions(-) 2 files changed, 276 insertions(+), 31 deletions(-)
diff --git a/src/nsterm.h b/src/nsterm.h diff --git a/src/nsterm.h b/src/nsterm.h
index d308a71..7a8c5a0 100644 index dd4e3da..2daa885 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 @@ -509,6 +509,7 @@ typedef struct ns_ax_visible_run
@@ -61,10 +61,10 @@ index d308a71..7a8c5a0 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 156dba9..a6fce74 100644 index d813274..9e089f3 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -6884,11 +6884,154 @@ Accessibility virtual elements (macOS / Cocoa only) @@ -6938,11 +6938,154 @@ Accessibility virtual elements (macOS / Cocoa only)
/* ---- Helper: extract buffer text for accessibility ---- */ /* ---- Helper: extract buffer text for accessibility ---- */
@@ -220,7 +220,7 @@ index 156dba9..a6fce74 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)
@@ -6959,7 +7102,7 @@ Accessibility virtual elements (macOS / Cocoa only) @@ -7013,7 +7156,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 +229,7 @@ index 156dba9..a6fce74 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));
@@ -7040,7 +7183,7 @@ Mode lines using icon fonts (e.g. doom-modeline with nerd-font) @@ -7094,7 +7237,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 +238,7 @@ index 156dba9..a6fce74 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;
@@ -7519,6 +7662,7 @@ @implementation EmacsAccessibilityBuffer @@ -7573,6 +7716,7 @@ @implementation EmacsAccessibilityBuffer
@synthesize cachedOverlayModiff; @synthesize cachedOverlayModiff;
@synthesize cachedTextStart; @synthesize cachedTextStart;
@synthesize cachedModiff; @synthesize cachedModiff;
@@ -246,7 +246,7 @@ index 156dba9..a6fce74 100644
@synthesize cachedPoint; @synthesize cachedPoint;
@synthesize cachedMarkActive; @synthesize cachedMarkActive;
@synthesize cachedCompletionAnnouncement; @synthesize cachedCompletionAnnouncement;
@@ -7616,7 +7760,7 @@ - (void)ensureTextCache @@ -7670,7 +7814,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 +255,7 @@ index 156dba9..a6fce74 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]);
@@ -7629,16 +7773,15 @@ - (void)ensureTextCache @@ -7683,16 +7827,15 @@ - (void)ensureTextCache
return; return;
ptrdiff_t modiff = BUF_MODIFF (b); ptrdiff_t modiff = BUF_MODIFF (b);
@@ -278,7 +278,7 @@ index 156dba9..a6fce74 100644
&& cachedTextStart == BUF_BEGV (b) && cachedTextStart == BUF_BEGV (b)
&& pt >= cachedTextStart && pt >= cachedTextStart
&& (textLen == 0 && (textLen == 0
@@ -7655,7 +7798,6 @@ - (void)ensureTextCache @@ -7709,7 +7852,6 @@ - (void)ensureTextCache
[cachedText release]; [cachedText release];
cachedText = [text retain]; cachedText = [text retain];
cachedTextModiff = modiff; cachedTextModiff = modiff;
@@ -286,7 +286,7 @@ index 156dba9..a6fce74 100644
cachedTextStart = start; cachedTextStart = start;
if (visibleRuns) if (visibleRuns)
@@ -7720,7 +7862,7 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos @@ -7774,7 +7916,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
@@ -295,7 +295,7 @@ index 156dba9..a6fce74 100644
NSUInteger lo = 0, hi = visibleRunCount; NSUInteger lo = 0, hi = visibleRunCount;
while (lo < hi) while (lo < hi)
{ {
@@ -7733,7 +7875,7 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos @@ -7787,7 +7929,7 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos
else else
{ {
/* Found: charpos is inside this run. Compute UTF-16 delta /* Found: charpos is inside this run. Compute UTF-16 delta
@@ -304,7 +304,7 @@ index 156dba9..a6fce74 100644
NSUInteger chars_in = (NSUInteger)(charpos - r->charpos); NSUInteger chars_in = (NSUInteger)(charpos - r->charpos);
if (chars_in == 0 || !cachedText) if (chars_in == 0 || !cachedText)
return r->ax_start; return r->ax_start;
@@ -7758,10 +7900,10 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos @@ -7812,10 +7954,10 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos
/* 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
@@ -317,7 +317,7 @@ index 156dba9..a6fce74 100644
@synchronized (self) @synchronized (self)
{ {
if (visibleRunCount == 0) if (visibleRunCount == 0)
@@ -7795,7 +7937,7 @@ - (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx @@ -7849,7 +7991,7 @@ - (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx
return cp; return cp;
} }
} }
@@ -326,7 +326,7 @@ index 156dba9..a6fce74 100644
if (lo > 0) if (lo > 0)
{ {
ns_ax_visible_run *last = &visibleRuns[visibleRunCount - 1]; ns_ax_visible_run *last = &visibleRuns[visibleRunCount - 1];
@@ -7817,7 +7959,7 @@ - (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx @@ -7871,7 +8013,7 @@ - (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx
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
@@ -335,7 +335,7 @@ index 156dba9..a6fce74 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
@@ -8171,6 +8313,50 @@ - (NSInteger)accessibilityInsertionPointLineNumber @@ -8225,6 +8367,50 @@ - (NSInteger)accessibilityInsertionPointLineNumber
return [self lineForAXIndex:point_idx]; return [self lineForAXIndex:point_idx];
} }
@@ -386,7 +386,7 @@ index 156dba9..a6fce74 100644
- (NSRange)accessibilityRangeForLine:(NSInteger)line - (NSRange)accessibilityRangeForLine:(NSInteger)line
{ {
if (![NSThread isMainThread]) if (![NSThread isMainThread])
@@ -8393,7 +8579,7 @@ - (NSRect)accessibilityFrame @@ -8447,7 +8633,7 @@ - (NSRect)accessibilityFrame
/* =================================================================== /* ===================================================================
@@ -395,7 +395,7 @@ index 156dba9..a6fce74 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).
@@ -8408,7 +8594,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point @@ -8462,7 +8648,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point
if (point > self.cachedPoint if (point > self.cachedPoint
&& point - self.cachedPoint == 1) && point - self.cachedPoint == 1)
{ {
@@ -404,7 +404,7 @@ index 156dba9..a6fce74 100644
[self invalidateTextCache]; [self invalidateTextCache];
[self ensureTextCache]; [self ensureTextCache];
if (cachedText) if (cachedText)
@@ -8427,7 +8613,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point @@ -8481,7 +8667,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
@@ -413,7 +413,7 @@ index 156dba9..a6fce74 100644
self.cachedPoint = point; self.cachedPoint = point;
NSDictionary *change = @{ NSDictionary *change = @{
@@ -8760,14 +8946,72 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f @@ -8814,14 +9000,72 @@ - (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) --- */
@@ -488,7 +488,7 @@ index 156dba9..a6fce74 100644
per the WebKit/Chromium pattern. */ per the WebKit/Chromium pattern. */
else if (point != self.cachedPoint || markActive != self.cachedMarkActive) else if (point != self.cachedPoint || markActive != self.cachedMarkActive)
{ {
@@ -8937,7 +9181,7 @@ - (NSRect)accessibilityFrame @@ -8991,7 +9235,7 @@ - (NSRect)accessibilityFrame
/* =================================================================== /* ===================================================================
@@ -497,7 +497,7 @@ index 156dba9..a6fce74 100644
=================================================================== */ =================================================================== */
/* Scan visible range of window W for interactive spans. /* Scan visible range of window W for interactive spans.
@@ -9145,7 +9389,7 @@ - (void) setAccessibilityFocused: (BOOL) focused @@ -9199,7 +9443,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
@@ -506,7 +506,7 @@ index 156dba9..a6fce74 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)))
@@ -9171,7 +9415,7 @@ - (void) setAccessibilityFocused: (BOOL) focused @@ -9225,7 +9469,7 @@ - (void) setAccessibilityFocused: (BOOL) focused
@end @end
@@ -515,7 +515,7 @@ index 156dba9..a6fce74 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)
@@ -11893,7 +12137,7 @@ - (int) fullscreenState @@ -11947,7 +12191,7 @@ - (int) fullscreenState
if (WINDOW_LEAF_P (w)) if (WINDOW_LEAF_P (w))
{ {
@@ -524,7 +524,7 @@ index 156dba9..a6fce74 100644
EmacsAccessibilityBuffer *elem EmacsAccessibilityBuffer *elem
= [existing objectForKey:[NSValue valueWithPointer:w]]; = [existing objectForKey:[NSValue valueWithPointer:w]];
if (!elem) if (!elem)
@@ -11927,7 +12171,7 @@ - (int) fullscreenState @@ -11981,7 +12225,7 @@ - (int) fullscreenState
} }
else else
{ {
@@ -533,7 +533,7 @@ index 156dba9..a6fce74 100644
Lisp_Object child = w->contents; Lisp_Object child = w->contents;
while (!NILP (child)) while (!NILP (child))
{ {
@@ -12039,7 +12283,7 @@ - (void)postAccessibilityUpdates @@ -12093,7 +12337,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
@@ -542,7 +542,7 @@ index 156dba9..a6fce74 100644
Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe); Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe);
if (!EQ (curRoot, lastRootWindow)) if (!EQ (curRoot, lastRootWindow))
{ {
@@ -12048,12 +12292,12 @@ - (void)postAccessibilityUpdates @@ -12102,12 +12346,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,7 +1,7 @@
From 14ee30459ef494e4c6f0c766cf8d24007e75659a Mon Sep 17 00:00:00 2001 From 41ddfb0403ea43279fcadc6b1cd8a493629464bc 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 9/9] ns: announce child frame completion candidates for
VoiceOver VoiceOver
Completion frameworks such as Corfu, Company-box, and similar Completion frameworks such as Corfu, Company-box, and similar
@@ -71,10 +71,10 @@ index 4825cf9..97777e2 100644
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
diff --git a/etc/NEWS b/etc/NEWS diff --git a/etc/NEWS b/etc/NEWS
index e76ee93..c3e0b40 100644 index f48d05b..ec4b95e 100644
--- a/etc/NEWS --- a/etc/NEWS
+++ b/etc/NEWS +++ b/etc/NEWS
@@ -4393,8 +4393,8 @@ send user data to Apple's speech recognition servers. @@ -4401,8 +4401,8 @@ send user data to Apple's speech recognition servers.
** VoiceOver accessibility support on macOS. ** VoiceOver accessibility support on macOS.
Emacs now exposes buffer content, cursor position, and interactive Emacs now exposes buffer content, cursor position, and interactive
elements to the macOS accessibility subsystem (VoiceOver). This elements to the macOS accessibility subsystem (VoiceOver). This
@@ -86,7 +86,7 @@ index e76ee93..c3e0b40 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 7a8c5a0..9ac65ac 100644 index 2daa885..f32d4b2 100644
--- a/src/nsterm.h --- a/src/nsterm.h
+++ b/src/nsterm.h +++ b/src/nsterm.h
@@ -596,6 +596,10 @@ typedef NS_ENUM (NSInteger, EmacsAXSpanType) @@ -596,6 +596,10 @@ typedef NS_ENUM (NSInteger, EmacsAXSpanType)
@@ -100,7 +100,7 @@ index 7a8c5a0..9ac65ac 100644
#endif #endif
BOOL font_panel_active; BOOL font_panel_active;
NSFont *font_panel_result; NSFont *font_panel_result;
@@ -659,6 +663,7 @@ typedef NS_ENUM (NSInteger, EmacsAXSpanType) @@ -665,6 +669,7 @@ typedef NS_ENUM (NSInteger, EmacsAXSpanType)
- (void)rebuildAccessibilityTree; - (void)rebuildAccessibilityTree;
- (void)invalidateAccessibilityTree; - (void)invalidateAccessibilityTree;
- (void)postAccessibilityUpdates; - (void)postAccessibilityUpdates;
@@ -109,10 +109,10 @@ index 7a8c5a0..9ac65ac 100644
@end @end
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index a6fce74..75ef0e0 100644 index 9e089f3..1a623be 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -7028,6 +7028,112 @@ visual line index for Zoom (skip whitespace-only lines @@ -7082,6 +7082,112 @@ visual line index for Zoom (skip whitespace-only lines
return nil; return nil;
} }
@@ -225,7 +225,7 @@ index a6fce74..75ef0e0 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
@@ -12267,6 +12373,77 @@ - (id)accessibilityFocusedUIElement @@ -12321,6 +12427,77 @@ - (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. */
@@ -303,7 +303,7 @@ index a6fce74..75ef0e0 100644
- (void)postAccessibilityUpdates - (void)postAccessibilityUpdates
{ {
NSTRACE ("[EmacsView postAccessibilityUpdates]"); NSTRACE ("[EmacsView postAccessibilityUpdates]");
@@ -12277,11 +12454,59 @@ - (void)postAccessibilityUpdates @@ -12331,11 +12508,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