patches: apply all maintainer review fixes (review pass 2)

All 9 patches now apply cleanly with git am on Linux (git 2.43.0).
Root cause of previous failures: hunk offsets were systematically wrong
by 7-40 lines; macOS git fuzzy-matched them, Linux did not.
Patches regenerated via git format-patch after applying all changes.

Fixes applied:
- P0000: unbind_to on no-candidate fall-through path; hunk regenerated
- P0001: block_input + record_unwind_protect_void in ns_ax_buffer_text
- P0003: [trims release] MRC memory leak; block_input already present
- P0004: mojibake comment (--- not UTF-8 em-dash)
- P0006: texinfo dangling semicolons -> periods in GNUstep paragraph
- P0007: em-dash fixes removed (content was already --- from P0004/P0005)
- P0008: childFrameLastBuffer -> BVAR(b,name) for GC safety;
  BUF_OVERLAY_MODIFF removed from ensureTextCache (hl-line-mode O(N)
  rebuild regression); block_input in ns_ax_buffer_text (P0001 scope);
  voiceoverSetPoint and childFrameLastBuffer explicit init in
  initFrameFromEmacs:; cachedOverlayModiffForText ivar removed
This commit is contained in:
2026-03-02 18:50:45 +01:00
parent 51f59441c1
commit 6176087cfb
9 changed files with 176 additions and 200 deletions

View File

@@ -1,7 +1,7 @@
From edb3a37f853fc9b98dfc96a7ba0434e87e1fdc3e Mon Sep 17 00:00:00 2001 From cc35459697126cf2fe7d152978170fa1e152b11d 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 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. Also track completion candidates so follows keyboard focus in Emacs. Also track completion candidates so
@@ -28,11 +28,11 @@ to the selected completion candidate after normal cursor tracking.
--- ---
etc/NEWS | 11 ++ etc/NEWS | 11 ++
src/nsterm.h | 6 + src/nsterm.h | 6 +
src/nsterm.m | 353 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/nsterm.m | 354 +++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 370 insertions(+) 3 files changed, 371 insertions(+)
diff --git a/etc/NEWS b/etc/NEWS diff --git a/etc/NEWS b/etc/NEWS
index ef36df5..80661a9 100644 index 7367e3ccbd..4c149e41d6 100644
--- a/etc/NEWS --- a/etc/NEWS
+++ b/etc/NEWS +++ b/etc/NEWS
@@ -82,6 +82,17 @@ other directory on your system. You can also invoke the @@ -82,6 +82,17 @@ other directory on your system. You can also invoke the
@@ -54,7 +54,7 @@ index ef36df5..80661a9 100644
** 'line-spacing' now supports specifying spacing above the line. ** 'line-spacing' now supports specifying spacing above the line.
Previously, only spacing below the line could be specified. The user Previously, only spacing below the line could be specified. The user
diff --git a/src/nsterm.h b/src/nsterm.h diff --git a/src/nsterm.h b/src/nsterm.h
index 7c1ee4c..ea6e7ba 100644 index 7c1ee4cf53..ea6e7ba4f5 100644
--- a/src/nsterm.h --- a/src/nsterm.h
+++ b/src/nsterm.h +++ b/src/nsterm.h
@@ -484,6 +484,12 @@ enum ns_return_frame_mode @@ -484,6 +484,12 @@ enum ns_return_frame_mode
@@ -71,7 +71,7 @@ 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..fc75910 100644 index 932d209f56..88c9251c18 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -71,6 +71,11 @@ Updated by Christian Limpach (chris@nice.ch) @@ -71,6 +71,11 @@ Updated by Christian Limpach (chris@nice.ch)
@@ -86,7 +86,7 @@ index 74e4ad5..fc75910 100644
#endif #endif
static EmacsMenu *dockMenu; static EmacsMenu *dockMenu;
@@ -1081,6 +1086,280 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) @@ -1081,6 +1086,281 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
} }
@@ -359,6 +359,7 @@ index 74e4ad5..fc75910 100644
+ UAZoomChangeFocus (&cgRect, &cgRect, + UAZoomChangeFocus (&cgRect, &cgRect,
+ kUAZoomFocusTypeInsertionPoint); + kUAZoomFocusTypeInsertionPoint);
+ } + }
+ unbind_to (count, Qnil);
+} +}
+ +
+#endif /* MAC_OS_X_VERSION_MIN_REQUIRED >= 101000 */ +#endif /* MAC_OS_X_VERSION_MIN_REQUIRED >= 101000 */
@@ -367,7 +368,7 @@ index 74e4ad5..fc75910 100644
static void static void
ns_update_end (struct frame *f) ns_update_end (struct frame *f)
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
@@ -1104,6 +1383,41 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) @@ -1104,6 +1384,41 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
unblock_input (); unblock_input ();
ns_updating_frame = NULL; ns_updating_frame = NULL;
@@ -409,7 +410,7 @@ index 74e4ad5..fc75910 100644
} }
static void static void
@@ -3232,6 +3546,45 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors. @@ -3232,6 +3547,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,7 +1,7 @@
From 1ecb71555d3419523d8ccebc03ace9bffac4f99e Mon Sep 17 00:00:00 2001 From 3658dbc6fed51b2d34ab8c06707eaca1ee1a78d4 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 (Cocoa) Add the foundation for macOS VoiceOver accessibility in the NS (Cocoa)
port. No existing code paths are modified. port. No existing code paths are modified.
@@ -29,11 +29,11 @@ ns-accessibility-enabled with corrected doc: initial value is nil,
set non-nil automatically when an AT is detected at startup. set non-nil automatically when an AT is detected at startup.
--- ---
src/nsterm.h | 131 +++++++++++++++ src/nsterm.h | 131 +++++++++++++++
src/nsterm.m | 456 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/nsterm.m | 452 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 583 insertions(+) 2 files changed, 583 insertions(+)
diff --git a/src/nsterm.h b/src/nsterm.h diff --git a/src/nsterm.h b/src/nsterm.h
index ea6e7ba..5746e9e 100644 index ea6e7ba4f5..5746e9e9bd 100644
--- a/src/nsterm.h --- a/src/nsterm.h
+++ b/src/nsterm.h +++ b/src/nsterm.h
@@ -453,6 +453,124 @@ enum ns_return_frame_mode @@ -453,6 +453,124 @@ enum ns_return_frame_mode
@@ -189,7 +189,7 @@ index ea6e7ba..5746e9e 100644
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index fc75910..852e7f9 100644 index 88c9251c18..8b6f12517d 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)
@@ -200,7 +200,7 @@ index fc75910..852e7f9 100644
#include "systime.h" #include "systime.h"
#include "character.h" #include "character.h"
#include "xwidget.h" #include "xwidget.h"
@@ -7209,6 +7210,430 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg @@ -7201,6 +7202,430 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
} }
#endif #endif
@@ -631,7 +631,7 @@ index fc75910..852e7f9 100644
/* ========================================================================== /* ==========================================================================
EmacsView implementation EmacsView implementation
@@ -11665,6 +12090,24 @@ Convert an X font name (XLFD) to an NS font name. @@ -11657,6 +12082,24 @@ 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");
@@ -656,7 +656,7 @@ index fc75910..852e7f9 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));
@@ -11813,6 +12260,15 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with @@ -11805,6 +12248,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 f29ed7e4e305a5f8857e72d3762d43c0901be718 Mon Sep 17 00:00:00 2001 From 5491457834dc7a35eaca62b22baa607ce73f102b 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.
@@ -23,14 +23,14 @@ loop is safe: it runs only on actual character modifications.
(accessibilityRangeForPosition:, setAccessibilitySelectedTextRange:) (accessibilityRangeForPosition:, setAccessibilitySelectedTextRange:)
(setAccessibilityFocused:): Implement NSAccessibility protocol methods. (setAccessibilityFocused:): Implement NSAccessibility protocol methods.
--- ---
src/nsterm.m | 1127 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/nsterm.m | 1115 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 1123 insertions(+) 1 file changed, 1115 insertions(+)
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 852e7f9..3e1ac74 100644 index 8b6f12517d..13b8357286 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -7631,6 +7631,1121 @@ - (id)accessibilityTopLevelUIElement @@ -7623,6 +7623,1121 @@ - (id)accessibilityTopLevelUIElement
@end @end

View File

@@ -1,7 +1,7 @@
From b4b09f69a8a4742e9f1431242d58e2c52400112c Mon Sep 17 00:00:00 2001 From ab845beb9112989f85fe3f5b344e5a26ec1b5421 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 dispatch and mode-line readout. Add VoiceOver notification dispatch and mode-line readout.
@@ -22,14 +22,14 @@ detects text edit, cursor/mark change, or overlay change.
(EmacsAccessibilityModeLine): Implement AXStaticText element for the (EmacsAccessibilityModeLine): Implement AXStaticText element for the
mode line. mode line.
--- ---
src/nsterm.m | 546 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/nsterm.m | 604 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 546 insertions(+) 1 file changed, 604 insertions(+)
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 3e1ac74..d3015e2 100644 index 13b8357286..cbf29694dd 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -8758,6 +8758,609 @@ - (NSRect)accessibilityFrame @@ -8738,6 +8738,610 @@ - (NSRect)accessibilityFrame
@end @end
@@ -196,6 +196,7 @@ index 3e1ac74..d3015e2 100644
+ [trims formUnionWithCharacterSet: + [trims formUnionWithCharacterSet:
+ [NSCharacterSet punctuationCharacterSet]]; + [NSCharacterSet punctuationCharacterSet]];
+ word = [word stringByTrimmingCharactersInSet:trims]; + word = [word stringByTrimmingCharactersInSet:trims];
+ [trims release];
+ if ([word length] > 0) + if ([word length] > 0)
+ { + {
+ NSDictionary *annInfo = @{ + NSDictionary *annInfo = @{

View File

@@ -1,7 +1,7 @@
From 1ce902a7a0b0d4d63e9d9f8718145d183a3c5d1a Mon Sep 17 00:00:00 2001 From e3c3878e65d8273e5fca97e942fe97ff3c5c113f 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; scans the * src/nsterm.m (ns_ax_scan_interactive_spans): New function; scans the
visible portion of a buffer for interactive text properties visible portion of a buffer for interactive text properties
@@ -18,17 +18,17 @@ rebuilding lazily when interactiveSpansDirty is set.
1 file changed, 287 insertions(+) 1 file changed, 287 insertions(+)
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index d3015e2..6d3b87a 100644 index cbf29694dd..e8bbc1ffe1 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -9304,6 +9304,293 @@ - (NSRect)accessibilityFrame @@ -9342,6 +9342,293 @@ - (NSRect)accessibilityFrame
@end @end
+ +
+ +
+/* =================================================================== +/* ===================================================================
+ EmacsAccessibilityInteractiveSpan helpers and implementation + EmacsAccessibilityInteractiveSpan --- helpers and implementation
+ =================================================================== */ + =================================================================== */
+ +
+/* Scan visible range of window W for interactive spans. +/* Scan visible range of window W for interactive spans.
@@ -125,7 +125,7 @@ index d3015e2..6d3b87a 100644
+ { + {
+ /* Skip to the next position where any interactive property + /* Skip to the next position where any interactive property
+ changes. Try each scannable property in turn and take + changes. Try each scannable property in turn and take
+ the nearest change point — O(properties) per gap rather + the nearest change point --- O(properties) per gap rather
+ than O(chars). Fall back to pos+1 as safety net. */ + than O(chars). Fall back to pos+1 as safety net. */
+ ptrdiff_t next_interesting = vis_end; + ptrdiff_t next_interesting = vis_end;
+ Lisp_Object skip_props[5] + Lisp_Object skip_props[5]
@@ -237,7 +237,7 @@ index d3015e2..6d3b87a 100644
+ 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
+ remain valid across GC GC does not relocate objects in + remain valid across GC --- GC does not relocate objects in
+ 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)))
@@ -263,7 +263,7 @@ index d3015e2..6d3b87a 100644
+ +
+@end +@end
+ +
+/* EmacsAccessibilityBuffer InteractiveSpans category. +/* EmacsAccessibilityBuffer --- InteractiveSpans category.
+ 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)

View File

@@ -1,7 +1,7 @@
From 6e6c5f4cf33d302e23141ce241c1747cdc0304be Mon Sep 17 00:00:00 2001 From d020bfbad36ff87ac4fd1ec6b52f7f451435260a 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 element tree into EmacsView and hook it into Wire the accessibility element tree into EmacsView and hook it into
the redisplay cycle. the redisplay cycle.
@@ -27,10 +27,10 @@ com.apple.accessibility.api distributed notification.
2 files changed, 431 insertions(+), 12 deletions(-) 2 files changed, 431 insertions(+), 12 deletions(-)
diff --git a/etc/NEWS b/etc/NEWS diff --git a/etc/NEWS b/etc/NEWS
index 80661a9..2b1f9e6 100644 index 4c149e41d6..7f917f93b2 100644
--- a/etc/NEWS --- a/etc/NEWS
+++ b/etc/NEWS +++ b/etc/NEWS
@@ -4400,6 +4400,19 @@ allowing Emacs users access to speech recognition utilities. @@ -4385,6 +4385,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.
@@ -51,7 +51,7 @@ 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 6d3b87a..7574da4 100644 index e8bbc1ffe1..231ae95a17 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -1275,7 +1275,7 @@ If a completion candidate is selected (overlay or child frame), @@ -1275,7 +1275,7 @@ If a completion candidate is selected (overlay or child frame),
@@ -63,7 +63,7 @@ index 6d3b87a..7574da4 100644
return; return;
if (!WINDOWP (f->selected_window)) if (!WINDOWP (f->selected_window))
return; return;
@@ -1392,7 +1392,8 @@ so the visual offset is (ov_line + 1) * line_h from @@ -1393,7 +1393,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 6d3b87a..7574da4 100644
&& !NSIsEmptyRect (view->lastCursorRect)) && !NSIsEmptyRect (view->lastCursorRect))
{ {
NSRect r = view->lastCursorRect; NSRect r = view->lastCursorRect;
@@ -1419,6 +1420,9 @@ so the visual offset is (ov_line + 1) * line_h from @@ -1420,6 +1421,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 6d3b87a..7574da4 100644
} }
static void static void
@@ -3566,7 +3570,7 @@ EmacsView pixels (AppKit, flipped, top-left origin) @@ -3567,7 +3571,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 6d3b87a..7574da4 100644
{ {
NSRect windowRect = [view convertRect:r toView:nil]; NSRect windowRect = [view convertRect:r toView:nil];
NSRect screenRect NSRect screenRect
@@ -6731,9 +6735,56 @@ - (void)applicationDidFinishLaunching: (NSNotification *)notification @@ -6723,9 +6727,56 @@ - (void)applicationDidFinishLaunching: (NSNotification *)notification
} }
#endif #endif
@@ -149,7 +149,7 @@ index 6d3b87a..7574da4 100644
- (void)antialiasThresholdDidChange:(NSNotification *)notification - (void)antialiasThresholdDidChange:(NSNotification *)notification
{ {
#ifdef NS_IMPL_COCOA #ifdef NS_IMPL_COCOA
@@ -7634,7 +7685,6 @@ - (id)accessibilityTopLevelUIElement @@ -7626,7 +7677,6 @@ - (id)accessibilityTopLevelUIElement
@@ -157,7 +157,7 @@ index 6d3b87a..7574da4 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,
@@ -8759,7 +8809,6 @@ - (NSRect)accessibilityFrame @@ -8739,7 +8789,6 @@ - (NSRect)accessibilityFrame
@end @end
@@ -165,15 +165,15 @@ index 6d3b87a..7574da4 100644
/* =================================================================== /* ===================================================================
EmacsAccessibilityBuffer (Notifications) — AX event dispatch EmacsAccessibilityBuffer (Notifications) — AX event dispatch
@@ -9305,7 +9354,6 @@ - (NSRect)accessibilityFrame @@ -9343,7 +9392,6 @@ - (NSRect)accessibilityFrame
@end @end
- -
/* =================================================================== /* ===================================================================
EmacsAccessibilityInteractiveSpan helpers and implementation EmacsAccessibilityInteractiveSpan --- helpers and implementation
=================================================================== */ =================================================================== */
@@ -9636,6 +9684,7 @@ - (void)dealloc @@ -9674,6 +9722,7 @@ - (void)dealloc
[layer release]; [layer release];
#endif #endif
@@ -181,7 +181,7 @@ index 6d3b87a..7574da4 100644
[[self menu] release]; [[self menu] release];
[super dealloc]; [super dealloc];
} }
@@ -10984,6 +11033,32 @@ - (void)windowDidBecomeKey /* for direct calls */ @@ -11022,6 +11071,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 6d3b87a..7574da4 100644
} }
@@ -12221,6 +12296,332 @@ - (int) fullscreenState @@ -12259,6 +12334,332 @@ - (int) fullscreenState
return fs_state; return fs_state;
} }
@@ -547,7 +547,7 @@ index 6d3b87a..7574da4 100644
@end /* EmacsView */ @end /* EmacsView */
@@ -14221,12 +14622,17 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with @@ -14255,12 +14656,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,7 +1,7 @@
From cd2037fc2078f3db365fa3f222413d9022c882f5 Mon Sep 17 00:00:00 2001 From 8bc94f26137898cb050f176c244caa3d3a1c5946 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 between * doc/emacs/macos.texi (VoiceOver Accessibility): New node between
@@ -16,7 +16,7 @@ default: initial value is nil, set automatically at startup.
2 files changed, 83 insertions(+), 3 deletions(-) 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..6514dfc 100644 index 6bd334f48e..8d4a7825d8 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.
@@ -99,8 +99,8 @@ index 6bd334f..6514dfc 100644
+left-to-right glyph layout. +left-to-right glyph layout.
+@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.
+Block-style cursors are handled +Block-style 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.
@@ -110,10 +110,10 @@ index 6bd334f..6514dfc 100644
@section GNUstep Support @section GNUstep Support
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 7574da4..426b029 100644 index 231ae95a17..062066d4e5 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -14623,9 +14623,13 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with @@ -14657,9 +14657,13 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with
DEFVAR_BOOL ("ns-accessibility-enabled", ns_accessibility_enabled, DEFVAR_BOOL ("ns-accessibility-enabled", ns_accessibility_enabled,
doc: /* Non-nil enables Zoom cursor tracking and VoiceOver support. doc: /* Non-nil enables Zoom cursor tracking and VoiceOver support.

View File

@@ -1,7 +1,7 @@
From c777281b5f05b463ec4bb8bdcb71bdcf400a10c6 Mon Sep 17 00:00:00 2001 From 1c8bbd32afe8d5686c8c875feb399a20ea656d72 Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Daneel <daneel@sukany.cz>
Date: Sat, 28 Feb 2026 14:46:25 +0100 Date: Mon, 2 Mar 2026 18:39:46 +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. Without candidates via overlay before-string/after-string properties. Without
@@ -18,11 +18,11 @@ ValueChanged; keep overlay_modiff out of ensureTextCache to prevent a
race where an AX query consumes the change before notification. race where an AX query consumes the change before notification.
--- ---
src/nsterm.h | 1 + src/nsterm.h | 1 +
src/nsterm.m | 330 ++++++++++++++++++++++++++++++++++++++++++++------- src/nsterm.m | 324 ++++++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 289 insertions(+), 42 deletions(-) 2 files changed, 286 insertions(+), 39 deletions(-)
diff --git a/src/nsterm.h b/src/nsterm.h diff --git a/src/nsterm.h b/src/nsterm.h
index 5746e9e..21a93bc 100644 index 5746e9e9bd..21a93bc799 100644
--- a/src/nsterm.h --- a/src/nsterm.h
+++ b/src/nsterm.h +++ b/src/nsterm.h
@@ -510,6 +510,7 @@ typedef struct ns_ax_visible_run @@ -510,6 +510,7 @@ typedef struct ns_ax_visible_run
@@ -34,10 +34,10 @@ index 5746e9e..21a93bc 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 426b029..8d44b5f 100644 index 062066d4e5..5e6ab11295 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -7271,11 +7271,154 @@ Accessibility virtual elements (macOS / Cocoa only) @@ -7263,11 +7263,154 @@ Accessibility virtual elements (macOS / Cocoa only)
/* ---- Helper: extract buffer text for accessibility ---- */ /* ---- Helper: extract buffer text for accessibility ---- */
@@ -193,7 +193,7 @@ index 426b029..8d44b5f 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)
@@ -7346,7 +7489,7 @@ Accessibility virtual elements (macOS / Cocoa only) @@ -7338,7 +7481,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
@@ -202,7 +202,7 @@ index 426b029..8d44b5f 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));
@@ -7427,7 +7570,7 @@ Mode lines using icon fonts (e.g. nerd-font icons) @@ -7419,7 +7562,7 @@ Mode lines using icon fonts (e.g. nerd-font icons)
return NSZeroRect; return NSZeroRect;
/* charpos_start and charpos_len are already in buffer charpos /* charpos_start and charpos_len are already in buffer charpos
@@ -211,7 +211,7 @@ index 426b029..8d44b5f 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;
@@ -7906,6 +8049,7 @@ @implementation EmacsAccessibilityBuffer @@ -7894,6 +8037,7 @@ @implementation EmacsAccessibilityBuffer
@synthesize cachedOverlayModiff; @synthesize cachedOverlayModiff;
@synthesize cachedTextStart; @synthesize cachedTextStart;
@synthesize cachedModiff; @synthesize cachedModiff;
@@ -219,7 +219,7 @@ index 426b029..8d44b5f 100644
@synthesize cachedPoint; @synthesize cachedPoint;
@synthesize cachedMarkActive; @synthesize cachedMarkActive;
@synthesize cachedCompletionAnnouncement; @synthesize cachedCompletionAnnouncement;
@@ -8003,7 +8147,7 @@ - (void)ensureTextCache @@ -7991,7 +8135,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
@@ -228,7 +228,7 @@ index 426b029..8d44b5f 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]);
@@ -8015,25 +8159,16 @@ - (void)ensureTextCache @@ -8003,25 +8147,16 @@ - (void)ensureTextCache
if (!b) if (!b)
return; return;
@@ -262,7 +262,7 @@ index 426b029..8d44b5f 100644
&& cachedTextStart == BUF_BEGV (b) && cachedTextStart == BUF_BEGV (b)
&& pt >= cachedTextStart && pt >= cachedTextStart
&& (textLen == 0 && (textLen == 0
@@ -8049,7 +8184,7 @@ included in the cached AX text (it is handled separately via @@ -8037,7 +8172,7 @@ included in the cached AX text (it is handled separately via
{ {
[cachedText release]; [cachedText release];
cachedText = [text retain]; cachedText = [text retain];
@@ -271,7 +271,7 @@ index 426b029..8d44b5f 100644
cachedTextStart = start; cachedTextStart = start;
if (visibleRuns) if (visibleRuns)
@@ -8118,7 +8253,7 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos @@ -8106,7 +8241,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
@@ -280,7 +280,7 @@ index 426b029..8d44b5f 100644
NSUInteger lo = 0, hi = visibleRunCount; NSUInteger lo = 0, hi = visibleRunCount;
while (lo < hi) while (lo < hi)
{ {
@@ -8167,10 +8302,10 @@ by run length (visible window), not total buffer size. */ @@ -8155,10 +8290,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
@@ -293,7 +293,7 @@ index 426b029..8d44b5f 100644
@synchronized (self) @synchronized (self)
{ {
if (visibleRunCount == 0) if (visibleRunCount == 0)
@@ -8212,7 +8347,7 @@ the slow path (composed character sequence walk), which is @@ -8200,7 +8335,7 @@ the slow path (composed character sequence walk), which is
return cp; return cp;
} }
} }
@@ -302,7 +302,7 @@ index 426b029..8d44b5f 100644
if (lo > 0) if (lo > 0)
{ {
ns_ax_visible_run *last = &visibleRuns[visibleRunCount - 1]; ns_ax_visible_run *last = &visibleRuns[visibleRunCount - 1];
@@ -8234,7 +8369,7 @@ the slow path (composed character sequence walk), which is @@ -8222,7 +8357,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
@@ -311,7 +311,7 @@ index 426b029..8d44b5f 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
@@ -8588,6 +8723,50 @@ - (NSInteger)accessibilityInsertionPointLineNumber @@ -8568,6 +8703,50 @@ - (NSInteger)accessibilityInsertionPointLineNumber
return [self lineForAXIndex:point_idx]; return [self lineForAXIndex:point_idx];
} }
@@ -362,7 +362,7 @@ index 426b029..8d44b5f 100644
- (NSRange)accessibilityRangeForLine:(NSInteger)line - (NSRange)accessibilityRangeForLine:(NSInteger)line
{ {
if (![NSThread isMainThread]) if (![NSThread isMainThread])
@@ -8810,7 +8989,7 @@ - (NSRect)accessibilityFrame @@ -8790,7 +8969,7 @@ - (NSRect)accessibilityFrame
/* =================================================================== /* ===================================================================
@@ -371,7 +371,7 @@ index 426b029..8d44b5f 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).
@@ -8825,7 +9004,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point @@ -8805,7 +8984,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point
if (point > self.cachedPoint if (point > self.cachedPoint
&& point - self.cachedPoint == 1) && point - self.cachedPoint == 1)
{ {
@@ -380,7 +380,7 @@ index 426b029..8d44b5f 100644
[self invalidateTextCache]; [self invalidateTextCache];
[self ensureTextCache]; [self ensureTextCache];
if (cachedText) if (cachedText)
@@ -8844,7 +9023,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point @@ -8824,7 +9003,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
@@ -389,7 +389,7 @@ index 426b029..8d44b5f 100644
self.cachedPoint = point; self.cachedPoint = point;
NSDictionary *change = @{ NSDictionary *change = @{
@@ -9178,16 +9357,83 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f @@ -9216,16 +9395,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) --- */
@@ -477,34 +477,7 @@ index 426b029..8d44b5f 100644
{ {
ptrdiff_t oldPoint = self.cachedPoint; ptrdiff_t oldPoint = self.cachedPoint;
BOOL oldMarkActive = self.cachedMarkActive; BOOL oldMarkActive = self.cachedMarkActive;
@@ -9355,7 +9601,7 @@ - (NSRect)accessibilityFrame @@ -12350,7 +12596,7 @@ - (int) fullscreenState
/* ===================================================================
- EmacsAccessibilityInteractiveSpan — helpers and implementation
+ EmacsAccessibilityInteractiveSpan --- helpers and implementation
=================================================================== */
/* Scan visible range of window W for interactive spans.
@@ -9564,7 +9810,7 @@ - (void) setAccessibilityFocused: (BOOL) focused
dispatch_async (dispatch_get_main_queue (), ^{
/* lwin is a Lisp_Object captured by value. This is GC-safe
because Lisp_Objects are tagged integers/pointers that
- remain valid across GC — GC does not relocate objects in
+ remain valid across GC --- GC does not relocate objects in
Emacs. The WINDOW_LIVE_P check below guards against the
window being deleted between capture and execution. */
if (!WINDOWP (lwin) || NILP (Fwindow_live_p (lwin)))
@@ -9590,7 +9836,7 @@ - (void) setAccessibilityFocused: (BOOL) focused
@end
-/* EmacsAccessibilityBuffer — InteractiveSpans category.
+/* EmacsAccessibilityBuffer --- InteractiveSpans category.
Methods are kept here (same .m file) so they access the ivars
declared in the @interface ivar block. */
@implementation EmacsAccessibilityBuffer (InteractiveSpans)
@@ -12312,7 +12558,7 @@ - (int) fullscreenState
if (WINDOW_LEAF_P (w)) if (WINDOW_LEAF_P (w))
{ {
@@ -513,7 +486,7 @@ index 426b029..8d44b5f 100644
EmacsAccessibilityBuffer *elem EmacsAccessibilityBuffer *elem
= [existing objectForKey:[NSValue valueWithPointer:w]]; = [existing objectForKey:[NSValue valueWithPointer:w]];
if (!elem) if (!elem)
@@ -12346,7 +12592,7 @@ - (int) fullscreenState @@ -12384,7 +12630,7 @@ - (int) fullscreenState
} }
else else
{ {
@@ -522,7 +495,7 @@ index 426b029..8d44b5f 100644
Lisp_Object child = w->contents; Lisp_Object child = w->contents;
while (!NILP (child)) while (!NILP (child))
{ {
@@ -12458,7 +12704,7 @@ - (void)postAccessibilityUpdates @@ -12496,7 +12742,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
@@ -531,7 +504,7 @@ index 426b029..8d44b5f 100644
Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe); Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe);
if (!EQ (curRoot, lastRootWindow)) if (!EQ (curRoot, lastRootWindow))
{ {
@@ -12467,12 +12713,12 @@ - (void)postAccessibilityUpdates @@ -12505,12 +12751,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,30 +1,43 @@
From 830710de8a7bac560d71ae802dcf7df60517c57b Mon Sep 17 00:00:00 2001 From 3daf00725fe434bfe5e820fd69e390697e9e6373 Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Daneel <daneel@sukany.cz>
Date: Sat, 28 Feb 2026 16:01:29 +0100 Date: Mon, 2 Mar 2026 18:49:13 +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 and Company-box render candidates Child frame popups (Corfu, Company-mode child frames) render completion
in a child frame. This extends the overlay announcement support to candidates in a separate frame whose buffer is not accessible via the
handle child frame popups. minibuffer overlay path. This patch scans child frame buffers for
selected candidates and announces them via VoiceOver.
* src/nsterm.m (ns_ax_selected_child_frame_text): New function; scan * src/nsterm.h (EmacsView): Add childFrameLastBuffer, childFrameLastModiff,
a child frame buffer line by line using Fget_char_property to find the childFrameLastCandidate, childFrameCompletionActive, lastEchoCharsModiff
selected candidate; uses record_unwind_current_buffer for safety. ivars; remove cachedOverlayModiffForText (unused after BUF_OVERLAY_MODIFF
(EmacsView announceChildFrameCompletion): New method. removed from ensureTextCache to prevent hl-line-mode O(N) rebuilds).
(EmacsView postAccessibilityUpdates): Detect child frames via Initialize voiceoverSetPoint, childFrameLastBuffer in initFrameFromEmacs:.
FRAME_PARENT_FRAME; call announceChildFrameCompletion. Post (EmacsAXBuffer): Add voiceoverSetPoint ivar.
NSAccessibilityFocusedUIElementChangedNotification on the parent buffer * src/nsterm.m (ns_ax_buffer_text): Add block_input protection for
element when a child frame completion closes. Lisp calls; use record_unwind_protect_void to guarantee unblock_input.
(ensureTextCache): Remove BUF_OVERLAY_MODIFF tracking; keep only
BUF_CHARS_MODIFF. BUF_OVERLAY_MODIFF caused O(buffer-size) rebuilds
with hl-line-mode (moves overlay on every post-command-hook).
(announceChildFrameCompletion): New method; scans child frame buffers
for selected completion candidates. Store childFrameLastBuffer as
BVAR(b, name) (buffer name symbol, GC-reachable via obarray) rather
than make_lisp_ptr to avoid dangling pointer after buffer kill.
(postEchoAreaAnnouncementIfNeeded): New method; announces echo area
changes for commands like C-g.
(postAccessibilityNotificationsForFrame:): Drive child frame and echo
area announcements.
* doc/emacs/macos.texi: Fix dangling semicolon in GNUstep paragraph.
--- ---
doc/emacs/macos.texi | 18 +- doc/emacs/macos.texi | 14 +-
etc/NEWS | 18 +- etc/NEWS | 18 +-
src/nsterm.h | 21 ++ src/nsterm.h | 20 ++
src/nsterm.m | 496 +++++++++++++++++++++++++++++++++++++++---- src/nsterm.m | 537 +++++++++++++++++++++++++++++++++++++++----
4 files changed, 501 insertions(+), 52 deletions(-) 4 files changed, 538 insertions(+), 51 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 6514dfc..bcf74b3 100644 index 8d4a7825d8..03a657f970 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.
@@ -55,22 +68,11 @@ index 6514dfc..bcf74b3 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
@@ -341,8 +345,8 @@ but @code{accessibilityRangeForPosition} hit-testing assumes
left-to-right glyph layout.
@end itemize
- This support is available only on the Cocoa build; GNUstep has a
-different accessibility model and is not yet supported;
+ This support is available only on the Cocoa build. GNUstep has a
+different accessibility model and is not yet supported.
Block-style cursors are handled
correctly: character navigation announces the character at the cursor
position, not the character before it.
diff --git a/etc/NEWS b/etc/NEWS diff --git a/etc/NEWS b/etc/NEWS
index 2b1f9e6..5766428 100644 index 7f917f93b2..d7631fa6c7 100644
--- a/etc/NEWS --- a/etc/NEWS
+++ b/etc/NEWS +++ b/etc/NEWS
@@ -4400,15 +4400,19 @@ allowing Emacs users access to speech recognition utilities. @@ -4385,16 +4385,20 @@ 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.
@@ -99,10 +101,10 @@ index 2b1f9e6..5766428 100644
interface and eliminate the associated overhead. interface and eliminate the associated overhead.
diff --git a/src/nsterm.h b/src/nsterm.h diff --git a/src/nsterm.h b/src/nsterm.h
index 21a93bc..d435e42 100644 index 21a93bc799..bdd40b8eb7 100644
--- a/src/nsterm.h --- a/src/nsterm.h
+++ b/src/nsterm.h +++ b/src/nsterm.h
@@ -504,9 +504,21 @@ typedef struct ns_ax_visible_run @@ -504,9 +504,20 @@ typedef struct ns_ax_visible_run
NSUInteger lineCount; /* Entries in lineStartOffsets. */ NSUInteger lineCount; /* Entries in lineStartOffsets. */
NSMutableArray *cachedInteractiveSpans; NSMutableArray *cachedInteractiveSpans;
BOOL interactiveSpansDirty; BOOL interactiveSpansDirty;
@@ -120,11 +122,10 @@ index 21a93bc..d435e42 100644
+/* Overlay modiff at last text cache rebuild. Tracked separately from +/* Overlay modiff at last text cache rebuild. Tracked separately from
+ cachedOverlayModiff (which is used for completion announcements) so + cachedOverlayModiff (which is used for completion announcements) so
+ that fold/unfold detection is independent of notification dispatch. */ + that fold/unfold detection is independent of notification dispatch. */
+@property (nonatomic, assign) ptrdiff_t cachedOverlayModiffForText;
@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;
@@ -596,6 +608,14 @@ typedef NS_ENUM (NSInteger, EmacsAXSpanType) @@ -596,6 +607,14 @@ typedef NS_ENUM (NSInteger, EmacsAXSpanType)
Lisp_Object lastRootWindow; Lisp_Object lastRootWindow;
BOOL accessibilityTreeValid; BOOL accessibilityTreeValid;
BOOL accessibilityUpdating; BOOL accessibilityUpdating;
@@ -139,7 +140,7 @@ index 21a93bc..d435e42 100644
#endif #endif
BOOL font_panel_active; BOOL font_panel_active;
NSFont *font_panel_result; NSFont *font_panel_result;
@@ -665,6 +685,7 @@ typedef NS_ENUM (NSInteger, EmacsAXSpanType) @@ -665,6 +684,7 @@ typedef NS_ENUM (NSInteger, EmacsAXSpanType)
- (void)rebuildAccessibilityTree; - (void)rebuildAccessibilityTree;
- (void)invalidateAccessibilityTree; - (void)invalidateAccessibilityTree;
- (void)postAccessibilityUpdates; - (void)postAccessibilityUpdates;
@@ -148,7 +149,7 @@ index 21a93bc..d435e42 100644
@end @end
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 8d44b5f..29b646d 100644 index 5e6ab11295..6e0575677e 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -1126,24 +1126,19 @@ Uses CFAbsoluteTimeGetCurrent() (~5 ns, a VDSO read) for timing. */ @@ -1126,24 +1126,19 @@ Uses CFAbsoluteTimeGetCurrent() (~5 ns, a VDSO read) for timing. */
@@ -186,9 +187,9 @@ index 8d44b5f..29b646d 100644
} }
/* Scan overlay before-string / after-string properties in the /* Scan overlay before-string / after-string properties in the
@@ -1356,6 +1351,12 @@ so the visual offset is (ov_line + 1) * line_h from @@ -1464,6 +1459,12 @@ so the visual offset is (ov_line + 1) * line_h from
UAZoomChangeFocus (&cgRect, &cgRect, #endif
kUAZoomFocusTypeInsertionPoint); gsaved = YES;
} }
+ /* Unbind record_unwind_current_buffer for both overlay and child + /* Unbind record_unwind_current_buffer for both overlay and child
+ frame paths. The overlay path calls unbind_to before return; + frame paths. The overlay path calls unbind_to before return;
@@ -198,8 +199,8 @@ index 8d44b5f..29b646d 100644
+ unbind_to (count, Qnil); + unbind_to (count, Qnil);
} }
#endif /* MAC_OS_X_VERSION_MIN_REQUIRED >= 101000 */
@@ -7415,6 +7416,112 @@ visual line index for Zoom (skip whitespace-only lines @@ -7407,6 +7408,112 @@ visual line index for Zoom (skip whitespace-only lines
return nil; return nil;
} }
@@ -312,15 +313,16 @@ index 8d44b5f..29b646d 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
@@ -8046,6 +8153,7 @@ that remapped bindings (e.g., C-j -> next-line) are recognized. @@ -7441,6 +7548,8 @@ visual line index for Zoom (skip whitespace-only lines
@implementation EmacsAccessibilityBuffer
@synthesize cachedText; specpdl_ref count = SPECPDL_INDEX ();
@synthesize cachedTextModiff; record_unwind_current_buffer ();
+@synthesize cachedOverlayModiffForText; + record_unwind_protect_void (unblock_input);
@synthesize cachedOverlayModiff; + block_input ();
@synthesize cachedTextStart; if (b != current_buffer)
@synthesize cachedModiff; set_buffer_internal_1 (b);
@@ -8159,16 +8267,34 @@ - (void)ensureTextCache
@@ -8147,16 +8256,32 @@ - (void)ensureTextCache
if (!b) if (!b)
return; return;
@@ -348,32 +350,29 @@ index 8d44b5f..29b646d 100644
+ Including overlay_modiff would silently update cachedOverlayModiff + Including overlay_modiff would silently update cachedOverlayModiff
+ and prevent the notification dispatch from detecting changes. */ + and prevent the notification dispatch from detecting changes. */
+ ptrdiff_t chars_modiff = BUF_CHARS_MODIFF (b); + ptrdiff_t chars_modiff = BUF_CHARS_MODIFF (b);
+ ptrdiff_t overlay_modiff = BUF_OVERLAY_MODIFF (b);
+ ptrdiff_t pt = BUF_PT (b); + ptrdiff_t pt = BUF_PT (b);
+ NSUInteger textLen = cachedText ? [cachedText length] : 0; + NSUInteger textLen = cachedText ? [cachedText length] : 0;
+ /* Cache is valid when neither characters nor fold-state have changed. + /* Cache is valid when neither characters nor fold-state have changed.
+ BUF_CHARS_MODIFF guards against character edits and is not bumped + BUF_CHARS_MODIFF guards against character edits and is not bumped
+ by font-lock (text-property changes), preserving the O(1) hot path. + by font-lock (text-property changes), preserving the O(1) hot path.
+ BUF_OVERLAY_MODIFF catches fold/unfold: outline-mode, org-mode, and + Do NOT add BUF_OVERLAY_MODIFF here: modes like hl-line-mode move
+ hideshow all use overlays (via outline-flag-region) in Emacs 28+. + overlays on every post-command-hook, bumping BUF_OVERLAY_MODIFF and
+ Font-lock uses only text properties, so adding BUF_OVERLAY_MODIFF + causing O(buffer-size) text rebuilds per keystroke. Fold/unfold
+ here does not reintroduce the per-redisplay rebuild. */ + changes visible characters and thereby bumps BUF_CHARS_MODIFF. */
+ if (cachedText && cachedTextModiff == chars_modiff + if (cachedText && cachedTextModiff == chars_modiff
+ && cachedOverlayModiffForText == overlay_modiff
&& cachedTextStart == BUF_BEGV (b) && cachedTextStart == BUF_BEGV (b)
&& pt >= cachedTextStart && pt >= cachedTextStart
&& (textLen == 0 && (textLen == 0
@@ -8184,7 +8310,8 @@ included in the cached AX text (it is handled separately via @@ -8172,7 +8297,7 @@ included in the cached AX text (it is handled separately via
{ {
[cachedText release]; [cachedText release];
cachedText = [text retain]; cachedText = [text retain];
- cachedTextModiff = modiff; - cachedTextModiff = modiff;
+ cachedTextModiff = chars_modiff; + cachedTextModiff = chars_modiff;
+ cachedOverlayModiffForText = overlay_modiff;
cachedTextStart = start; cachedTextStart = start;
if (visibleRuns) if (visibleRuns)
@@ -8597,6 +8724,11 @@ - (void)setAccessibilitySelectedTextRange:(NSRange)range @@ -8585,6 +8710,11 @@ - (void)setAccessibilitySelectedTextRange:(NSRange)range
[self ensureTextCache]; [self ensureTextCache];
@@ -385,7 +384,7 @@ index 8d44b5f..29b646d 100644
specpdl_ref count = SPECPDL_INDEX (); specpdl_ref count = SPECPDL_INDEX ();
record_unwind_current_buffer (); record_unwind_current_buffer ();
/* Ensure block_input is always matched by unblock_input even if /* Ensure block_input is always matched by unblock_input even if
@@ -9064,14 +9196,22 @@ - (void)postFocusedCursorNotification:(ptrdiff_t)point @@ -9040,15 +9170,23 @@ - (void)postFocusedCursorNotification:(ptrdiff_t)point
= @(ns_ax_text_state_change_selection_move); = @(ns_ax_text_state_change_selection_move);
moveInfo[@"AXTextSelectionDirection"] = @(direction); moveInfo[@"AXTextSelectionDirection"] = @(direction);
moveInfo[@"AXTextChangeElement"] = self; moveInfo[@"AXTextChangeElement"] = self;
@@ -414,7 +413,8 @@ index 8d44b5f..29b646d 100644
+ self.emacsView, + self.emacsView,
NSAccessibilitySelectedTextChangedNotification, NSAccessibilitySelectedTextChangedNotification,
moveInfo); moveInfo);
@@ -9111,12 +9254,17 @@ derive its own speech (it would read the wrong character
@@ -9146,12 +9284,17 @@ user expectation ("w" jumps to next word and reads it). */
} }
} }
@@ -437,7 +437,7 @@ index 8d44b5f..29b646d 100644
if (cachedText if (cachedText
&& granularity == ns_ax_text_selection_granularity_line) && granularity == ns_ax_text_selection_granularity_line)
{ {
@@ -9179,7 +9327,14 @@ - (void)postCompletionAnnouncementForBuffer:(struct buffer *)b @@ -9214,7 +9357,14 @@ - (void)postCompletionAnnouncementForBuffer:(struct buffer *)b
ptrdiff_t currentOverlayStart = 0; ptrdiff_t currentOverlayStart = 0;
ptrdiff_t currentOverlayEnd = 0; ptrdiff_t currentOverlayEnd = 0;
@@ -452,7 +452,7 @@ index 8d44b5f..29b646d 100644
record_unwind_current_buffer (); record_unwind_current_buffer ();
if (b != current_buffer) if (b != current_buffer)
set_buffer_internal_1 (b); set_buffer_internal_1 (b);
@@ -9356,12 +9511,29 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f @@ -9390,12 +9540,29 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f
if (!b) if (!b)
return; return;
@@ -482,7 +482,7 @@ index 8d44b5f..29b646d 100644
if (modiff != self.cachedModiff) if (modiff != self.cachedModiff)
{ {
self.cachedModiff = modiff; self.cachedModiff = modiff;
@@ -9375,6 +9547,7 @@ Text property changes (e.g. face updates from @@ -9409,6 +9576,7 @@ Text property changes (e.g. face updates from
{ {
self.cachedCharsModiff = chars_modiff; self.cachedCharsModiff = chars_modiff;
[self postTextChangedNotification:point]; [self postTextChangedNotification:point];
@@ -490,7 +490,7 @@ index 8d44b5f..29b646d 100644
} }
} }
@@ -9397,8 +9570,15 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property @@ -9431,8 +9599,15 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property
displayed in the minibuffer. In normal editing buffers, displayed in the minibuffer. In normal editing buffers,
font-lock and other modes change BUF_OVERLAY_MODIFF on font-lock and other modes change BUF_OVERLAY_MODIFF on
every redisplay, triggering O(overlays) work per keystroke. every redisplay, triggering O(overlays) work per keystroke.
@@ -508,7 +508,7 @@ index 8d44b5f..29b646d 100644
goto skip_overlay_scan; goto skip_overlay_scan;
int selected_line = -1; int selected_line = -1;
@@ -9444,7 +9624,18 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property @@ -9478,7 +9653,18 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property
self.cachedPoint = point; self.cachedPoint = point;
self.cachedMarkActive = markActive; self.cachedMarkActive = markActive;
@@ -528,7 +528,7 @@ index 8d44b5f..29b646d 100644
NSInteger direction = ns_ax_text_selection_direction_discontiguous; NSInteger direction = ns_ax_text_selection_direction_discontiguous;
if (point > oldPoint) if (point > oldPoint)
direction = ns_ax_text_selection_direction_next; direction = ns_ax_text_selection_direction_next;
@@ -9492,6 +9683,58 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property @@ -9526,6 +9712,58 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property
granularity = ns_ax_text_selection_granularity_line; granularity = ns_ax_text_selection_granularity_line;
} }
@@ -587,7 +587,7 @@ index 8d44b5f..29b646d 100644
/* Post notifications for focused and non-focused elements. */ /* Post notifications for focused and non-focused elements. */
if ([self isAccessibilityFocused]) if ([self isAccessibilityFocused])
[self postFocusedCursorNotification:point [self postFocusedCursorNotification:point
@@ -9634,6 +9865,17 @@ - (NSRect)accessibilityFrame @@ -9668,6 +9906,17 @@ - (NSRect)accessibilityFrame
if (vis_start >= vis_end) if (vis_start >= vis_end)
return @[]; return @[];
@@ -605,7 +605,7 @@ index 8d44b5f..29b646d 100644
/* Symbols are interned once at startup via DEFSYM in syms_of_nsterm; /* Symbols are interned once at startup via DEFSYM in syms_of_nsterm;
reference them directly here (GC-safe, no repeated obarray lookup). */ reference them directly here (GC-safe, no repeated obarray lookup). */
@@ -9754,6 +9996,7 @@ than O(chars). Fall back to pos+1 as safety net. */ @@ -9788,6 +10037,7 @@ than O(chars). Fall back to pos+1 as safety net. */
pos = span_end; pos = span_end;
} }
@@ -613,7 +613,7 @@ index 8d44b5f..29b646d 100644
return [[spans copy] autorelease]; return [[spans copy] autorelease];
} }
@@ -9935,6 +10178,10 @@ - (void)dealloc @@ -9969,6 +10219,10 @@ - (void)dealloc
#endif #endif
[accessibilityElements release]; [accessibilityElements release];
@@ -624,17 +624,18 @@ index 8d44b5f..29b646d 100644
[[self menu] release]; [[self menu] release];
[super dealloc]; [super dealloc];
} }
@@ -11384,6 +11631,9 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f @@ -11418,6 +11672,10 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f
windowClosing = NO; windowClosing = NO;
processingCompose = NO; processingCompose = NO;
+#ifdef NS_IMPL_COCOA +#ifdef NS_IMPL_COCOA
+ childFrameLastBuffer = Qnil; + childFrameLastBuffer = Qnil;
+ voiceoverSetPoint = NO;
+#endif +#endif
scrollbarsNeedingUpdate = 0; scrollbarsNeedingUpdate = 0;
fs_state = FULLSCREEN_NONE; fs_state = FULLSCREEN_NONE;
fs_before_fs = next_maximized = -1; fs_before_fs = next_maximized = -1;
@@ -12692,6 +12942,154 @@ - (id)accessibilityFocusedUIElement @@ -12726,6 +12984,154 @@ - (id)accessibilityFocusedUIElement
The existing elements carry cached state (modiff, point) from the The existing elements carry cached state (modiff, point) from the
previous redisplay cycle. Rebuilding first would create fresh previous redisplay cycle. Rebuilding first would create fresh
elements with current values, making change detection impossible. */ elements with current values, making change detection impossible. */
@@ -719,15 +720,15 @@ index 8d44b5f..29b646d 100644
+ if (!BUFFER_LIVE_P (b)) + if (!BUFFER_LIVE_P (b))
+ return; + return;
+ EMACS_INT modiff = BUF_MODIFF (b); + EMACS_INT modiff = BUF_MODIFF (b);
+ /* Compare buffer identity using the raw pointer, not a Lisp_Object. + /* Compare buffer identity via the buffer name symbol, which is always
+ A killed buffer can be GC'd even if we hold a Lisp_Object for it + GC-reachable through the obarray. Storing the name avoids keeping
+ (EmacsView is not GC-visible). Storing and comparing struct buffer * + a direct buffer pointer in a non-GC-visible ObjC ivar: if the buffer
+ is safe because we only test identity (not dereference) here, and + were killed and GC swept, a stale make_lisp_ptr value could collide
+ we guard all actual buffer field reads with BUFFER_LIVE_P below. */ + with a newly-allocated buffer at the same address. */
+ if ((struct buffer *) XLP (childFrameLastBuffer) == b + if (EQ (childFrameLastBuffer, BVAR (b, name))
+ && modiff == childFrameLastModiff) + && modiff == childFrameLastModiff)
+ return; + return;
+ childFrameLastBuffer = make_lisp_ptr (b, Lisp_Vectorlike); + childFrameLastBuffer = BVAR (b, name);
+ childFrameLastModiff = modiff; + childFrameLastModiff = modiff;
+ +
+ if (!BUFFER_LIVE_P (b)) + if (!BUFFER_LIVE_P (b))
@@ -789,7 +790,7 @@ index 8d44b5f..29b646d 100644
- (void)postAccessibilityUpdates - (void)postAccessibilityUpdates
{ {
NSTRACE ("[EmacsView postAccessibilityUpdates]"); NSTRACE ("[EmacsView postAccessibilityUpdates]");
@@ -12702,11 +13098,64 @@ - (void)postAccessibilityUpdates @@ -12736,11 +13142,64 @@ - (void)postAccessibilityUpdates
/* Re-entrance guard: VoiceOver callbacks during notification posting /* Re-entrance guard: VoiceOver callbacks during notification posting
can trigger redisplay, which calls ns_update_end, which calls us can trigger redisplay, which calls ns_update_end, which calls us