patches: fix Corfu Zoom tracking — replace rate-limit with parent-frame guard

Root cause: the 50ms rate limit broke child-frame (Corfu) tracking.
When the Corfu child frame redraws, its ns_update_end fires first and
resets the rate-limit timer.  When the parent frame's ns_update_end
fires immediately after, the timer has not expired, so
ns_zoom_track_completion returns early without scanning child frames.
Zoom focus stays on the first candidate.

Fix: remove the rate limit; add a FRAME_PARENT_FRAME(f) guard instead.
Child frames have no completion children to scan; their parent's
ns_update_end does the scan via FOR_EACH_FRAME.  Returning early on
child-frame calls avoids the redundant scan and leaves the timer
problem moot.  Overhead without the rate limit is ~40 Lisp evaluations
per redisplay (~5-20 µs), acceptable given ns_zoom_enabled_p() already
caches the UAZoomEnabled() IPC call.
This commit is contained in:
2026-03-01 07:11:00 +01:00
parent 19cc43dbbb
commit c4975c3fe4
9 changed files with 83 additions and 86 deletions

View File

@@ -1,4 +1,4 @@
From d494967af08ec3e5bb94ff50126c2994505a6d85 Mon Sep 17 00:00:00 2001 From aeab7e4db306049fb543f60cadcaaa346350f77b 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 1/9] ns: integrate with macOS Zoom for cursor tracking Subject: [PATCH 1/9] ns: integrate with macOS Zoom for cursor tracking
@@ -38,8 +38,8 @@ window splits, switches (C-x o), and completion frameworks.
--- ---
etc/NEWS | 11 ++ etc/NEWS | 11 ++
src/nsterm.h | 6 + src/nsterm.h | 6 +
src/nsterm.m | 339 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/nsterm.m | 336 +++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 356 insertions(+) 3 files changed, 353 insertions(+)
diff --git a/etc/NEWS b/etc/NEWS diff --git a/etc/NEWS b/etc/NEWS
index ef36df5..80661a9 100644 index ef36df5..80661a9 100644
@@ -81,10 +81,10 @@ 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..6527750 100644 index 74e4ad5..5498d7a 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -1081,6 +1081,271 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) @@ -1081,6 +1081,268 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
} }
@@ -268,18 +268,15 @@ index 74e4ad5..6527750 100644
+ return; + return;
+ if (!WINDOWP (f->selected_window)) + if (!WINDOWP (f->selected_window))
+ return; + return;
+ + /* Child frames (e.g. the Corfu popup itself) have no children to
+ /* Rate-limit completion scan to 20 Hz (50 ms). UAZoomEnabled() is + scan for completion candidates; their parent frame's ns_update_end
+ now cached so the main cost is the overlay/child-frame scan. + will scan them via FOR_EACH_FRAME. Return early to avoid a
+ 50 ms is imperceptible for completion navigation while preventing + redundant O(frames) scan on every child-frame redisplay cycle.
+ per-frame FOR_EACH_FRAME overhead. */ + Note: the rate limit that was here caused corfu tracking to fail:
+ { + the child frame's ns_update_end reset the timer, so the parent
+ static CFAbsoluteTime last_check; + frame's subsequent ns_update_end returned early without scanning. */
+ CFAbsoluteTime now = CFAbsoluteTimeGetCurrent (); + if (FRAME_PARENT_FRAME (f))
+ if (now - last_check < 0.05) + return;
+ return;
+ last_check = now;
+ }
+ +
+ specpdl_ref count = SPECPDL_INDEX (); + specpdl_ref count = SPECPDL_INDEX ();
+ record_unwind_current_buffer (); + record_unwind_current_buffer ();
@@ -356,7 +353,7 @@ index 74e4ad5..6527750 100644
static void static void
ns_update_end (struct frame *f) ns_update_end (struct frame *f)
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
@@ -1104,6 +1369,41 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) @@ -1104,6 +1366,41 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
unblock_input (); unblock_input ();
ns_updating_frame = NULL; ns_updating_frame = NULL;
@@ -398,7 +395,7 @@ index 74e4ad5..6527750 100644
} }
static void static void
@@ -3232,6 +3532,45 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors. @@ -3232,6 +3529,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,4 +1,4 @@
From 971bf8f19bec3a89144ddf2375cd17a4d95bb707 Mon Sep 17 00:00:00 2001 From 60516534a246d6ac5b6c91c1e673785943c65fe1 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/9] ns: add accessibility base classes and text extraction Subject: [PATCH 2/9] ns: add accessibility base classes and text extraction
@@ -188,7 +188,7 @@ index ea6e7ba..6e830de 100644
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 6527750..3e97248 100644 index 5498d7a..e516946 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 6527750..3e97248 100644
#include "systime.h" #include "systime.h"
#include "character.h" #include "character.h"
#include "xwidget.h" #include "xwidget.h"
@@ -7195,6 +7196,430 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg @@ -7192,6 +7193,430 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
} }
#endif #endif
@@ -630,7 +630,7 @@ index 6527750..3e97248 100644
/* ========================================================================== /* ==========================================================================
EmacsView implementation EmacsView implementation
@@ -11651,6 +12076,28 @@ Convert an X font name (XLFD) to an NS font name. @@ -11648,6 +12073,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 6527750..3e97248 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));
@@ -11783,7 +12230,7 @@ Convert an X font name (XLFD) to an NS font name. @@ -11780,7 +12227,7 @@ Convert an X font name (XLFD) to an NS font name.
doc: /* Non-nil means to use native fullscreen on Mac OS X 10.7 and later. doc: /* Non-nil means to use native fullscreen on Mac OS X 10.7 and later.
Nil means use fullscreen the old (< 10.7) way. The old way works better with Nil means use fullscreen the old (< 10.7) way. The old way works better with
multiple monitors, but lacks tool bar. This variable is ignored on multiple monitors, but lacks tool bar. This variable is ignored on
@@ -668,7 +668,7 @@ index 6527750..3e97248 100644
ns_use_native_fullscreen = YES; ns_use_native_fullscreen = YES;
ns_last_use_native_fullscreen = ns_use_native_fullscreen; ns_last_use_native_fullscreen = ns_use_native_fullscreen;
@@ -11799,10 +12246,19 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with @@ -11796,10 +12243,19 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with
This variable is ignored on Mac OS X < 10.7 and GNUstep. */); This variable is ignored on Mac OS X < 10.7 and GNUstep. */);
ns_use_srgb_colorspace = YES; ns_use_srgb_colorspace = YES;
@@ -689,7 +689,7 @@ index 6527750..3e97248 100644
ns_use_mwheel_acceleration = YES; ns_use_mwheel_acceleration = YES;
DEFVAR_LISP ("ns-mwheel-line-height", ns_mwheel_line_height, DEFVAR_LISP ("ns-mwheel-line-height", ns_mwheel_line_height,
@@ -11813,7 +12269,7 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with @@ -11810,7 +12266,7 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with
DEFVAR_BOOL ("ns-use-mwheel-momentum", ns_use_mwheel_momentum, DEFVAR_BOOL ("ns-use-mwheel-momentum", ns_use_mwheel_momentum,
doc: /* Non-nil means mouse wheel scrolling uses momentum. doc: /* Non-nil means mouse wheel scrolling uses momentum.

View File

@@ -1,4 +1,4 @@
From d49a7ef5e31a5988d6c54a04561326cff8444a2f Mon Sep 17 00:00:00 2001 From a75cbfb2c856c834a16d60cad2ada4ad4487bbb9 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/9] ns: implement buffer accessibility element (core Subject: [PATCH 3/9] ns: implement buffer accessibility element (core
@@ -22,10 +22,10 @@ line-by-line navigation, word/character announcements.
1 file changed, 1104 insertions(+) 1 file changed, 1104 insertions(+)
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 3e97248..7beedc4 100644 index e516946..8f528bd 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -7617,6 +7617,1110 @@ - (id)accessibilityTopLevelUIElement @@ -7614,6 +7614,1110 @@ - (id)accessibilityTopLevelUIElement
@end @end

View File

@@ -1,4 +1,4 @@
From 5a58044053001af12f388af2960f903427676c8f Mon Sep 17 00:00:00 2001 From 04dfd354788b32dc81dd57b61fb5494b0f84a817 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/9] ns: add buffer notification dispatch and mode-line Subject: [PATCH 4/9] ns: add buffer notification dispatch and mode-line
@@ -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 7beedc4..ecc8c54 100644 index 8f528bd..7e3d57a 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -8721,6 +8721,551 @@ - (NSRect)accessibilityFrame @@ -8718,6 +8718,551 @@ - (NSRect)accessibilityFrame
@end @end

View File

@@ -1,4 +1,4 @@
From 388628944f6ecc74806932e0ee2ad89c3df669ed Mon Sep 17 00:00:00 2001 From 7303dd3913729a454b44fe71219acbd612d624b4 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/9] ns: add interactive span elements for Tab navigation Subject: [PATCH 5/9] ns: add interactive span elements for Tab navigation
@@ -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 ecc8c54..5eaf480 100644 index 7e3d57a..1a21f2e 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -9266,6 +9266,292 @@ - (NSRect)accessibilityFrame @@ -9263,6 +9263,292 @@ - (NSRect)accessibilityFrame
@end @end

View File

@@ -1,4 +1,4 @@
From 61a0ae5344e6f52cb21ae299133b39b2ede010d0 Mon Sep 17 00:00:00 2001 From 4a81923ad68bc5866f7343b37429a882f7a388dd 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/9] ns: integrate accessibility with EmacsView and redisplay Subject: [PATCH 6/9] ns: integrate accessibility with EmacsView and redisplay
@@ -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 5eaf480..4ad451f 100644 index 1a21f2e..f8b7a8d 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -1258,7 +1258,7 @@ If a completion candidate is selected (overlay or child frame), @@ -1258,7 +1258,7 @@ If a completion candidate is selected (overlay or child frame),
@@ -63,7 +63,7 @@ index 5eaf480..4ad451f 100644
return; return;
if (!WINDOWP (f->selected_window)) if (!WINDOWP (f->selected_window))
return; return;
@@ -1378,7 +1378,8 @@ so the visual offset is (ov_line + 1) * line_h from @@ -1375,7 +1375,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 5eaf480..4ad451f 100644
&& !NSIsEmptyRect (view->lastCursorRect)) && !NSIsEmptyRect (view->lastCursorRect))
{ {
NSRect r = view->lastCursorRect; NSRect r = view->lastCursorRect;
@@ -1405,6 +1406,9 @@ so the visual offset is (ov_line + 1) * line_h from @@ -1402,6 +1403,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 5eaf480..4ad451f 100644
} }
static void static void
@@ -3552,7 +3556,7 @@ EmacsView pixels (AppKit, flipped, top-left origin) @@ -3549,7 +3553,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 5eaf480..4ad451f 100644
{ {
NSRect windowRect = [view convertRect:r toView:nil]; NSRect windowRect = [view convertRect:r toView:nil];
NSRect screenRect NSRect screenRect
@@ -6717,9 +6721,56 @@ - (void)applicationDidFinishLaunching: (NSNotification *)notification @@ -6714,9 +6718,56 @@ - (void)applicationDidFinishLaunching: (NSNotification *)notification
} }
#endif #endif
@@ -149,7 +149,7 @@ index 5eaf480..4ad451f 100644
- (void)antialiasThresholdDidChange:(NSNotification *)notification - (void)antialiasThresholdDidChange:(NSNotification *)notification
{ {
#ifdef NS_IMPL_COCOA #ifdef NS_IMPL_COCOA
@@ -7620,7 +7671,6 @@ - (id)accessibilityTopLevelUIElement @@ -7617,7 +7668,6 @@ - (id)accessibilityTopLevelUIElement
@@ -157,7 +157,7 @@ index 5eaf480..4ad451f 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,
@@ -8722,7 +8772,6 @@ - (NSRect)accessibilityFrame @@ -8719,7 +8769,6 @@ - (NSRect)accessibilityFrame
@end @end
@@ -165,7 +165,7 @@ index 5eaf480..4ad451f 100644
/* =================================================================== /* ===================================================================
EmacsAccessibilityBuffer (Notifications) — AX event dispatch EmacsAccessibilityBuffer (Notifications) — AX event dispatch
@@ -9267,7 +9316,6 @@ - (NSRect)accessibilityFrame @@ -9264,7 +9313,6 @@ - (NSRect)accessibilityFrame
@end @end
@@ -173,7 +173,7 @@ index 5eaf480..4ad451f 100644
/* =================================================================== /* ===================================================================
EmacsAccessibilityInteractiveSpan — helpers and implementation EmacsAccessibilityInteractiveSpan — helpers and implementation
=================================================================== */ =================================================================== */
@@ -9597,6 +9645,7 @@ - (void)dealloc @@ -9594,6 +9642,7 @@ - (void)dealloc
[layer release]; [layer release];
#endif #endif
@@ -181,7 +181,7 @@ index 5eaf480..4ad451f 100644
[[self menu] release]; [[self menu] release];
[super dealloc]; [super dealloc];
} }
@@ -10945,6 +10994,32 @@ - (void)windowDidBecomeKey /* for direct calls */ @@ -10942,6 +10991,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 5eaf480..4ad451f 100644
} }
@@ -12182,6 +12257,332 @@ - (int) fullscreenState @@ -12179,6 +12254,332 @@ - (int) fullscreenState
return fs_state; return fs_state;
} }
@@ -547,7 +547,7 @@ index 5eaf480..4ad451f 100644
@end /* EmacsView */ @end /* EmacsView */
@@ -14182,12 +14583,17 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with @@ -14179,12 +14580,17 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with
ns_use_srgb_colorspace = YES; ns_use_srgb_colorspace = YES;
DEFVAR_BOOL ("ns-accessibility-enabled", ns_accessibility_enabled, DEFVAR_BOOL ("ns-accessibility-enabled", ns_accessibility_enabled,

View File

@@ -1,4 +1,4 @@
From e2213409bca76154764263879b4bd43459230d6c Mon Sep 17 00:00:00 2001 From ca89e85f199e054a6cef6272425d2920e55b2ea5 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 7/9] doc: add VoiceOver accessibility section to macOS Subject: [PATCH 7/9] doc: add VoiceOver accessibility section to macOS

View File

@@ -1,4 +1,4 @@
From a5041ede13b2bbcea1ebcb501f8c7317c0fe998f Mon Sep 17 00:00:00 2001 From 0e5f90b02aa9d9b9ba1fdba7d88ae54e17417fe9 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 8/9] ns: announce overlay completion candidates for VoiceOver Subject: [PATCH 8/9] ns: announce overlay completion candidates for VoiceOver
@@ -61,10 +61,10 @@ index 6e830de..2102fb9 100644
@property (nonatomic, assign) BOOL cachedMarkActive; @property (nonatomic, assign) BOOL cachedMarkActive;
@property (nonatomic, copy) NSString *cachedCompletionAnnouncement; @property (nonatomic, copy) NSString *cachedCompletionAnnouncement;
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 4ad451f..5654283 100644 index f8b7a8d..3e005e0 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -7257,11 +7257,154 @@ Accessibility virtual elements (macOS / Cocoa only) @@ -7254,11 +7254,154 @@ Accessibility virtual elements (macOS / Cocoa only)
/* ---- Helper: extract buffer text for accessibility ---- */ /* ---- Helper: extract buffer text for accessibility ---- */
@@ -220,7 +220,7 @@ index 4ad451f..5654283 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)
@@ -7332,7 +7475,7 @@ Accessibility virtual elements (macOS / Cocoa only) @@ -7329,7 +7472,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 4ad451f..5654283 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));
@@ -7413,7 +7556,7 @@ Mode lines using icon fonts (e.g. doom-modeline with nerd-font) @@ -7410,7 +7553,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 4ad451f..5654283 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;
@@ -7892,6 +8035,7 @@ @implementation EmacsAccessibilityBuffer @@ -7889,6 +8032,7 @@ @implementation EmacsAccessibilityBuffer
@synthesize cachedOverlayModiff; @synthesize cachedOverlayModiff;
@synthesize cachedTextStart; @synthesize cachedTextStart;
@synthesize cachedModiff; @synthesize cachedModiff;
@@ -246,7 +246,7 @@ index 4ad451f..5654283 100644
@synthesize cachedPoint; @synthesize cachedPoint;
@synthesize cachedMarkActive; @synthesize cachedMarkActive;
@synthesize cachedCompletionAnnouncement; @synthesize cachedCompletionAnnouncement;
@@ -7989,7 +8133,7 @@ - (void)ensureTextCache @@ -7986,7 +8130,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 4ad451f..5654283 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]);
@@ -8001,25 +8145,16 @@ - (void)ensureTextCache @@ -7998,25 +8142,16 @@ - (void)ensureTextCache
if (!b) if (!b)
return; return;
@@ -289,7 +289,7 @@ index 4ad451f..5654283 100644
&& cachedTextStart == BUF_BEGV (b) && cachedTextStart == BUF_BEGV (b)
&& pt >= cachedTextStart && pt >= cachedTextStart
&& (textLen == 0 && (textLen == 0
@@ -8035,7 +8170,7 @@ included in the cached AX text (it is handled separately via @@ -8032,7 +8167,7 @@ included in the cached AX text (it is handled separately via
{ {
[cachedText release]; [cachedText release];
cachedText = [text retain]; cachedText = [text retain];
@@ -298,7 +298,7 @@ index 4ad451f..5654283 100644
cachedTextStart = start; cachedTextStart = start;
if (visibleRuns) if (visibleRuns)
@@ -8100,7 +8235,7 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos @@ -8097,7 +8232,7 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos
/* Binary search: runs are sorted by charpos (ascending). Find the /* Binary search: runs are sorted by charpos (ascending). Find the
run whose [charpos, charpos+length) range contains the target, run whose [charpos, charpos+length) range contains the target,
or the nearest run after an invisible gap. O(log n) instead of or the nearest run after an invisible gap. O(log n) instead of
@@ -307,7 +307,7 @@ index 4ad451f..5654283 100644
NSUInteger lo = 0, hi = visibleRunCount; NSUInteger lo = 0, hi = visibleRunCount;
while (lo < hi) while (lo < hi)
{ {
@@ -8113,7 +8248,7 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos @@ -8110,7 +8245,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
@@ -316,7 +316,7 @@ index 4ad451f..5654283 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;
@@ -8138,10 +8273,10 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos @@ -8135,10 +8270,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
@@ -329,7 +329,7 @@ index 4ad451f..5654283 100644
@synchronized (self) @synchronized (self)
{ {
if (visibleRunCount == 0) if (visibleRunCount == 0)
@@ -8175,7 +8310,7 @@ - (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx @@ -8172,7 +8307,7 @@ - (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx
return cp; return cp;
} }
} }
@@ -338,7 +338,7 @@ index 4ad451f..5654283 100644
if (lo > 0) if (lo > 0)
{ {
ns_ax_visible_run *last = &visibleRuns[visibleRunCount - 1]; ns_ax_visible_run *last = &visibleRuns[visibleRunCount - 1];
@@ -8197,7 +8332,7 @@ - (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx @@ -8194,7 +8329,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
@@ -347,7 +347,7 @@ index 4ad451f..5654283 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
@@ -8551,6 +8686,50 @@ - (NSInteger)accessibilityInsertionPointLineNumber @@ -8548,6 +8683,50 @@ - (NSInteger)accessibilityInsertionPointLineNumber
return [self lineForAXIndex:point_idx]; return [self lineForAXIndex:point_idx];
} }
@@ -398,7 +398,7 @@ index 4ad451f..5654283 100644
- (NSRange)accessibilityRangeForLine:(NSInteger)line - (NSRange)accessibilityRangeForLine:(NSInteger)line
{ {
if (![NSThread isMainThread]) if (![NSThread isMainThread])
@@ -8773,7 +8952,7 @@ - (NSRect)accessibilityFrame @@ -8770,7 +8949,7 @@ - (NSRect)accessibilityFrame
/* =================================================================== /* ===================================================================
@@ -407,7 +407,7 @@ index 4ad451f..5654283 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).
@@ -8788,7 +8967,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point @@ -8785,7 +8964,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point
if (point > self.cachedPoint if (point > self.cachedPoint
&& point - self.cachedPoint == 1) && point - self.cachedPoint == 1)
{ {
@@ -416,7 +416,7 @@ index 4ad451f..5654283 100644
[self invalidateTextCache]; [self invalidateTextCache];
[self ensureTextCache]; [self ensureTextCache];
if (cachedText) if (cachedText)
@@ -8807,7 +8986,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point @@ -8804,7 +8983,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
@@ -425,7 +425,7 @@ index 4ad451f..5654283 100644
self.cachedPoint = point; self.cachedPoint = point;
NSDictionary *change = @{ NSDictionary *change = @{
@@ -9140,16 +9319,83 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f @@ -9137,16 +9316,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) --- */
@@ -513,7 +513,7 @@ index 4ad451f..5654283 100644
{ {
ptrdiff_t oldPoint = self.cachedPoint; ptrdiff_t oldPoint = self.cachedPoint;
BOOL oldMarkActive = self.cachedMarkActive; BOOL oldMarkActive = self.cachedMarkActive;
@@ -9317,7 +9563,7 @@ - (NSRect)accessibilityFrame @@ -9314,7 +9560,7 @@ - (NSRect)accessibilityFrame
/* =================================================================== /* ===================================================================
@@ -522,7 +522,7 @@ index 4ad451f..5654283 100644
=================================================================== */ =================================================================== */
/* Scan visible range of window W for interactive spans. /* Scan visible range of window W for interactive spans.
@@ -9525,7 +9771,7 @@ - (void) setAccessibilityFocused: (BOOL) focused @@ -9522,7 +9768,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
@@ -531,7 +531,7 @@ index 4ad451f..5654283 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)))
@@ -9551,7 +9797,7 @@ - (void) setAccessibilityFocused: (BOOL) focused @@ -9548,7 +9794,7 @@ - (void) setAccessibilityFocused: (BOOL) focused
@end @end
@@ -540,7 +540,7 @@ index 4ad451f..5654283 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)
@@ -12273,7 +12519,7 @@ - (int) fullscreenState @@ -12270,7 +12516,7 @@ - (int) fullscreenState
if (WINDOW_LEAF_P (w)) if (WINDOW_LEAF_P (w))
{ {
@@ -549,7 +549,7 @@ index 4ad451f..5654283 100644
EmacsAccessibilityBuffer *elem EmacsAccessibilityBuffer *elem
= [existing objectForKey:[NSValue valueWithPointer:w]]; = [existing objectForKey:[NSValue valueWithPointer:w]];
if (!elem) if (!elem)
@@ -12307,7 +12553,7 @@ - (int) fullscreenState @@ -12304,7 +12550,7 @@ - (int) fullscreenState
} }
else else
{ {
@@ -558,7 +558,7 @@ index 4ad451f..5654283 100644
Lisp_Object child = w->contents; Lisp_Object child = w->contents;
while (!NILP (child)) while (!NILP (child))
{ {
@@ -12419,7 +12665,7 @@ - (void)postAccessibilityUpdates @@ -12416,7 +12662,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
@@ -567,7 +567,7 @@ index 4ad451f..5654283 100644
Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe); Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe);
if (!EQ (curRoot, lastRootWindow)) if (!EQ (curRoot, lastRootWindow))
{ {
@@ -12428,12 +12674,12 @@ - (void)postAccessibilityUpdates @@ -12425,12 +12671,12 @@ - (void)postAccessibilityUpdates
} }
/* If tree is stale, rebuild FIRST so we don't iterate freed /* If tree is stale, rebuild FIRST so we don't iterate freed

View File

@@ -1,4 +1,4 @@
From 2dabc7e44d316fa29e64958383fbac230f0f6870 Mon Sep 17 00:00:00 2001 From 85259296769fe8e30d3fa2d8a7626729023c8691 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 9/9] ns: announce child frame completion candidates for Subject: [PATCH 9/9] ns: announce child frame completion candidates for
@@ -109,10 +109,10 @@ index 2102fb9..dd98d56 100644
@end @end
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 5654283..69644d5 100644 index 3e005e0..d61a634 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -7401,6 +7401,112 @@ visual line index for Zoom (skip whitespace-only lines @@ -7398,6 +7398,112 @@ visual line index for Zoom (skip whitespace-only lines
return nil; return nil;
} }
@@ -225,7 +225,7 @@ index 5654283..69644d5 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
@@ -8145,16 +8251,25 @@ - (void)ensureTextCache @@ -8142,16 +8248,25 @@ - (void)ensureTextCache
if (!b) if (!b)
return; return;
@@ -259,7 +259,7 @@ index 5654283..69644d5 100644
&& cachedTextStart == BUF_BEGV (b) && cachedTextStart == BUF_BEGV (b)
&& pt >= cachedTextStart && pt >= cachedTextStart
&& (textLen == 0 && (textLen == 0
@@ -8170,7 +8285,7 @@ included in the cached AX text (it is handled separately via @@ -8167,7 +8282,7 @@ included in the cached AX text (it is handled separately via
{ {
[cachedText release]; [cachedText release];
cachedText = [text retain]; cachedText = [text retain];
@@ -268,7 +268,7 @@ index 5654283..69644d5 100644
cachedTextStart = start; cachedTextStart = start;
if (visibleRuns) if (visibleRuns)
@@ -9138,6 +9253,7 @@ - (void)postCompletionAnnouncementForBuffer:(struct buffer *)b @@ -9135,6 +9250,7 @@ - (void)postCompletionAnnouncementForBuffer:(struct buffer *)b
ptrdiff_t currentOverlayStart = 0; ptrdiff_t currentOverlayStart = 0;
ptrdiff_t currentOverlayEnd = 0; ptrdiff_t currentOverlayEnd = 0;
@@ -276,7 +276,7 @@ index 5654283..69644d5 100644
specpdl_ref count2 = SPECPDL_INDEX (); specpdl_ref count2 = SPECPDL_INDEX ();
record_unwind_current_buffer (); record_unwind_current_buffer ();
if (b != current_buffer) if (b != current_buffer)
@@ -9296,6 +9412,7 @@ - (void)postCompletionAnnouncementForBuffer:(struct buffer *)b @@ -9293,6 +9409,7 @@ - (void)postCompletionAnnouncementForBuffer:(struct buffer *)b
self.cachedCompletionOverlayEnd = 0; self.cachedCompletionOverlayEnd = 0;
self.cachedCompletionPoint = 0; self.cachedCompletionPoint = 0;
} }
@@ -284,7 +284,7 @@ index 5654283..69644d5 100644
} }
/* ---- Notification dispatch (main entry point) ---- */ /* ---- Notification dispatch (main entry point) ---- */
@@ -9892,6 +10009,10 @@ - (void)dealloc @@ -9889,6 +10006,10 @@ - (void)dealloc
#endif #endif
[accessibilityElements release]; [accessibilityElements release];
@@ -295,7 +295,7 @@ index 5654283..69644d5 100644
[[self menu] release]; [[self menu] release];
[super dealloc]; [super dealloc];
} }
@@ -11341,6 +11462,9 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f @@ -11338,6 +11459,9 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f
windowClosing = NO; windowClosing = NO;
processingCompose = NO; processingCompose = NO;
@@ -305,7 +305,7 @@ index 5654283..69644d5 100644
scrollbarsNeedingUpdate = 0; scrollbarsNeedingUpdate = 0;
fs_state = FULLSCREEN_NONE; fs_state = FULLSCREEN_NONE;
fs_before_fs = next_maximized = -1; fs_before_fs = next_maximized = -1;
@@ -12649,6 +12773,80 @@ - (id)accessibilityFocusedUIElement @@ -12646,6 +12770,80 @@ - (id)accessibilityFocusedUIElement
The existing elements carry cached state (modiff, point) from the The existing elements carry cached state (modiff, point) from the
previous redisplay cycle. Rebuilding first would create fresh previous redisplay cycle. Rebuilding first would create fresh
elements with current values, making change detection impossible. */ elements with current values, making change detection impossible. */
@@ -386,7 +386,7 @@ index 5654283..69644d5 100644
- (void)postAccessibilityUpdates - (void)postAccessibilityUpdates
{ {
NSTRACE ("[EmacsView postAccessibilityUpdates]"); NSTRACE ("[EmacsView postAccessibilityUpdates]");
@@ -12659,11 +12857,59 @@ - (void)postAccessibilityUpdates @@ -12656,11 +12854,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