patches: fix all compile errors and review issues

- ZV_S -> BUF_ZV (undefined macro)
- cf->current_buffer -> XWINDOW(cf->selected_window)->contents
  (current_buffer is a thread macro, can't use as field name)
- find_newline: add record_unwind_current_buffer + set_buffer_internal_1
- ns_zoom_track_completion: add specpdl unwind protection
- ns_zoom_face_is_selected: replace with forward decl of ns_ax_face_is_selected
  (eliminates duplicate)
- childFrameLastBuffer: struct buffer * -> Lisp_Object (safe vs kill-buffer)
- EmacsView dealloc: xfree childFrameLastCandidate (memory leak)
- postCompletionAnnouncementForBuffer: add block_input/unblock_input
This commit is contained in:
2026-03-01 03:58:04 +01:00
parent b283068f82
commit 84eb777065
9 changed files with 134 additions and 132 deletions

View File

@@ -1,4 +1,4 @@
From 45076d26a15ae82b489349d481f3c1a1792730a5 Mon Sep 17 00:00:00 2001
From 54cae6f8afe265c12cffaef121bad32bc165a777 Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 22:39:35 +0100
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 ++
src/nsterm.h | 6 ++
src/nsterm.m | 285 +++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 302 insertions(+)
src/nsterm.m | 280 +++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 297 insertions(+)
diff --git a/etc/NEWS b/etc/NEWS
index ef36df5..80661a9 100644
@@ -81,10 +81,10 @@ index 7c1ee4c..ea6e7ba 100644
}
diff --git a/src/nsterm.m b/src/nsterm.m
index 74e4ad5..05ec3d1 100644
index 74e4ad5..2dd79ca 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1081,6 +1081,217 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
@@ -1081,6 +1081,209 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
}
@@ -93,31 +93,10 @@ index 74e4ad5..05ec3d1 100644
+#if defined (MAC_OS_X_VERSION_MIN_REQUIRED) \
+ && MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
+
+/* Check whether FACE (a Lisp symbol or list) has a name suggesting
+ it marks the currently selected completion candidate. Matches
+ vertico-current, icomplete-selected-match, ivy-current-match,
+ company-tooltip-selection, corfu-current, and similar. */
+static bool
+ns_zoom_face_is_selected (Lisp_Object face)
+{
+ if (SYMBOLP (face) && !NILP (face))
+ {
+ const char *name = SSDATA (SYMBOL_NAME (face));
+ if (strstr (name, "current")
+ || strstr (name, "selected")
+ || strstr (name, "selection"))
+ return true;
+ }
+ /* Handle face list (face1 face2 ...). */
+ if (CONSP (face))
+ {
+ Lisp_Object tail;
+ for (tail = face; CONSP (tail); tail = XCDR (tail))
+ if (ns_zoom_face_is_selected (XCAR (tail)))
+ return true;
+ }
+ return false;
+}
+/* Forward declaration --- defined in the VoiceOver section below.
+ Identifies faces like vertico-current, icomplete-selected-match,
+ ivy-current-match, corfu-current that mark the selected candidate. */
+static bool ns_ax_face_is_selected (Lisp_Object face);
+
+/* Scan overlay before-string / after-string properties in the
+ selected window for a completion candidate with a "selected"
@@ -128,7 +107,7 @@ index 74e4ad5..05ec3d1 100644
+{
+ struct buffer *b = XBUFFER (w->contents);
+ ptrdiff_t beg = marker_position (w->start);
+ ptrdiff_t end = ZV_S (b);
+ ptrdiff_t end = BUF_ZV (b);
+ Lisp_Object overlays = Foverlays_in (make_fixnum (beg),
+ make_fixnum (end));
+ Lisp_Object tail;
@@ -158,7 +137,7 @@ index 74e4ad5..05ec3d1 100644
+ Lisp_Object face
+ = Fget_text_property (make_fixnum (line_start),
+ Qface, str);
+ if (ns_zoom_face_is_selected (face))
+ if (ns_ax_face_is_selected (face))
+ return line;
+ line++;
+ line_start = i + 1;
@@ -190,7 +169,8 @@ index 74e4ad5..05ec3d1 100644
+ if (FRAME_PARENT_FRAME (cf) != f)
+ continue;
+ /* Small buffer = likely completion popup. */
+ struct buffer *b = XBUFFER (cf->current_buffer);
+ struct window *cw = XWINDOW (cf->selected_window);
+ struct buffer *b = XBUFFER (cw->contents);
+ if (BUF_ZV (b) - BUF_BEGV (b) > 10000)
+ continue;
+
@@ -198,14 +178,19 @@ index 74e4ad5..05ec3d1 100644
+ ptrdiff_t zv = BUF_ZV (b);
+ int line = 0;
+
+ specpdl_ref count = SPECPDL_INDEX ();
+ record_unwind_current_buffer ();
+ set_buffer_internal_1 (b);
+
+ ptrdiff_t pos = beg;
+ while (pos < zv)
+ {
+ Lisp_Object face
+ = Fget_char_property (make_fixnum (pos), Qface,
+ cf->current_buffer);
+ if (ns_zoom_face_is_selected (face))
+ cw->contents);
+ if (ns_ax_face_is_selected (face))
+ {
+ unbind_to (count, Qnil);
+ *child_frame = cf;
+ return line;
+ }
@@ -217,6 +202,7 @@ index 74e4ad5..05ec3d1 100644
+ pos = next;
+ line++;
+ }
+ unbind_to (count, Qnil);
+ }
+ return -1;
+}
@@ -230,6 +216,11 @@ index 74e4ad5..05ec3d1 100644
+{
+ if (!UAZoomEnabled ())
+ return;
+ if (!WINDOWP (f->selected_window))
+ return;
+
+ specpdl_ref count = SPECPDL_INDEX ();
+ record_unwind_current_buffer ();
+
+ struct window *w = XWINDOW (f->selected_window);
+ int line_h = FRAME_LINE_HEIGHT (f);
@@ -261,6 +252,7 @@ index 74e4ad5..05ec3d1 100644
+
+ UAZoomChangeFocus (&cgRect, &cgRect,
+ kUAZoomFocusTypeInsertionPoint);
+ unbind_to (count, Qnil);
+ return;
+ }
+ }
@@ -302,7 +294,7 @@ index 74e4ad5..05ec3d1 100644
static void
ns_update_end (struct frame *f)
/* --------------------------------------------------------------------------
@@ -1104,6 +1315,41 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
@@ -1104,6 +1307,41 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
unblock_input ();
ns_updating_frame = NULL;
@@ -344,7 +336,7 @@ index 74e4ad5..05ec3d1 100644
}
static void
@@ -3232,6 +3478,45 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
@@ -3232,6 +3470,45 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
/* Prevent the cursor from being drawn outside the text area. */
r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA));
@@ -390,6 +382,16 @@ index 74e4ad5..05ec3d1 100644
ns_focus (f, NULL, 0);
NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
@@ -8321,6 +8598,9 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f
windowClosing = NO;
processingCompose = NO;
+#ifdef NS_IMPL_COCOA
+ childFrameLastBuffer = Qnil;
+#endif
scrollbarsNeedingUpdate = 0;
fs_state = FULLSCREEN_NONE;
fs_before_fs = next_maximized = -1;
--
2.43.0

View File

@@ -1,4 +1,4 @@
From 59ec39bdda532d02c748a7e27f9a90ba3be3c338 Mon Sep 17 00:00:00 2001
From 0e9726214f1081b8c4f23451ada5fc956042938b Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 12:58:11 +0100
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
index 05ec3d1..a4d0e02 100644
index 2dd79ca..b4437f1 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -46,6 +46,7 @@ Updated by Christian Limpach (chris@nice.ch)
@@ -199,7 +199,7 @@ index 05ec3d1..a4d0e02 100644
#include "systime.h"
#include "character.h"
#include "xwidget.h"
@@ -7141,6 +7142,430 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
@@ -7133,6 +7134,430 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
}
#endif
@@ -630,7 +630,7 @@ index 05ec3d1..a4d0e02 100644
/* ==========================================================================
EmacsView implementation
@@ -11597,6 +12022,28 @@ Convert an X font name (XLFD) to an NS font name.
@@ -11592,6 +12017,28 @@ Convert an X font name (XLFD) to an NS font name.
DEFSYM (Qns_drag_operation_generic, "ns-drag-operation-generic");
DEFSYM (Qns_handle_drag_motion, "ns-handle-drag-motion");
@@ -659,7 +659,7 @@ index 05ec3d1..a4d0e02 100644
Fput (Qalt, Qmodifier_value, make_fixnum (alt_modifier));
Fput (Qhyper, Qmodifier_value, make_fixnum (hyper_modifier));
Fput (Qmeta, Qmodifier_value, make_fixnum (meta_modifier));
@@ -11745,6 +12192,15 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with
@@ -11740,6 +12187,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. */);
ns_use_srgb_colorspace = YES;

View File

@@ -1,4 +1,4 @@
From 80f6de2020c50717e2238528e6241e00017f7230 Mon Sep 17 00:00:00 2001
From 5d8811666c12f236e8b3ebae8a44520085aa938e Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 12:58:11 +0100
Subject: [PATCH 3/9] ns: implement buffer accessibility element (core
@@ -22,10 +22,10 @@ line-by-line navigation, word/character announcements.
1 file changed, 1097 insertions(+)
diff --git a/src/nsterm.m b/src/nsterm.m
index a4d0e02..e281073 100644
index b4437f1..ef645e6 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -7563,6 +7563,1103 @@ - (id)accessibilityTopLevelUIElement
@@ -7555,6 +7555,1103 @@ - (id)accessibilityTopLevelUIElement
@end

View File

@@ -1,4 +1,4 @@
From 672223b6ced41898156aa476b8c77b27d3719e4a Mon Sep 17 00:00:00 2001
From c2caac09ebe9bf816059c752a2f4f32e39347b54 Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 12:58:11 +0100
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(+)
diff --git a/src/nsterm.m b/src/nsterm.m
index e281073..a29f039 100644
index ef645e6..41e8d4b 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -8660,6 +8660,551 @@ - (NSRect)accessibilityFrame
@@ -8652,6 +8652,551 @@ - (NSRect)accessibilityFrame
@end

View File

@@ -1,4 +1,4 @@
From 64b373bebadd9b9c52c90da297173ea108c6e394 Mon Sep 17 00:00:00 2001
From 5d254e28a56e023b9bf1e46cf061b9d596f49b02 Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 12:58:11 +0100
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(+)
diff --git a/src/nsterm.m b/src/nsterm.m
index a29f039..86bb7b6 100644
index 41e8d4b..4692a58 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -9205,6 +9205,292 @@ - (NSRect)accessibilityFrame
@@ -9197,6 +9197,292 @@ - (NSRect)accessibilityFrame
@end

View File

@@ -1,4 +1,4 @@
From be594f584a94e6fccd649be029c1af30242a12ee Mon Sep 17 00:00:00 2001
From 4c7ddcb5e6f5905efefa55cb7188667744bd3d0e Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 12:58:11 +0100
Subject: [PATCH 6/9] ns: integrate accessibility with EmacsView and redisplay
@@ -23,8 +23,8 @@ block cursor, org-mode folded headings, indirect buffers.
Known limitations documented in patch 6 Texinfo node.
---
etc/NEWS | 13 ++
src/nsterm.m | 376 +++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 376 insertions(+), 13 deletions(-)
src/nsterm.m | 359 ++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 369 insertions(+), 3 deletions(-)
diff --git a/etc/NEWS b/etc/NEWS
index 80661a9..2b1f9e6 100644
@@ -51,50 +51,20 @@ index 80661a9..2b1f9e6 100644
** Re-introduced dictation, lost in Emacs v30 (macOS).
We lost macOS dictation in v30 when migrating to NSTextInputClient.
diff --git a/src/nsterm.m b/src/nsterm.m
index 86bb7b6..2e8ae20 100644
index 4692a58..93da88a 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1343,13 +1343,16 @@ so the visual offset is (ov_line + 1) * line_h from
}
if (view)
view->zoomCursorUpdated = NO;
-#endif
/* Track completion candidates for Zoom (overlay and child frame).
Runs after cursor tracking so the selected candidate overrides
the default cursor position. */
@@ -1343,6 +1343,9 @@ so the visual offset is (ov_line + 1) * line_h from
if (view)
ns_zoom_track_completion (f, view);
+#endif
#endif /* NS_IMPL_COCOA */
+
+ /* Post accessibility notifications after each redisplay cycle. */
+ [view postAccessibilityUpdates];
#endif /* NS_IMPL_COCOA */
}
@@ -3480,15 +3483,12 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA));
#ifdef NS_IMPL_COCOA
- /* Zoom integration: inform macOS Zoom of the cursor position.
- Zoom (System Settings -> Accessibility -> Zoom) tracks a focus
- element to keep the zoomed viewport centered on the cursor.
-
- Coordinate conversion:
- EmacsView pixels (AppKit, flipped, top-left origin)
- -> NSWindow (convertRect:toView:nil)
- -> NSScreen (convertRectToScreen:)
- -> CGRect with y-flip for CoreGraphics top-left origin. */
+ /* Store cursor rect for Zoom and VoiceOver bounds queries.
+ Zoom: UAZoomChangeFocus below.
+ VoiceOver: accessibilityBoundsForRange: fallback.
+ Coordinate conversion for Zoom:
+ EmacsView (AppKit, flipped) -> NSWindow -> NSScreen
+ -> CGRect with y-flip for CoreGraphics top-left origin. */
{
EmacsView *view = FRAME_NS_VIEW (f);
if (view && on_p && active_p)
@@ -7566,7 +7566,6 @@ - (id)accessibilityTopLevelUIElement
static void
@@ -7558,7 +7561,6 @@ - (id)accessibilityTopLevelUIElement
@@ -102,7 +72,7 @@ index 86bb7b6..2e8ae20 100644
static BOOL
ns_ax_find_completion_overlay_range (struct buffer *b, ptrdiff_t point,
ptrdiff_t *out_start,
@@ -8661,7 +8660,6 @@ - (NSRect)accessibilityFrame
@@ -8653,7 +8655,6 @@ - (NSRect)accessibilityFrame
@end
@@ -110,7 +80,7 @@ index 86bb7b6..2e8ae20 100644
/* ===================================================================
EmacsAccessibilityBuffer (Notifications) — AX event dispatch
@@ -9206,7 +9204,6 @@ - (NSRect)accessibilityFrame
@@ -9198,7 +9199,6 @@ - (NSRect)accessibilityFrame
@end
@@ -118,7 +88,7 @@ index 86bb7b6..2e8ae20 100644
/* ===================================================================
EmacsAccessibilityInteractiveSpan — helpers and implementation
=================================================================== */
@@ -9536,6 +9533,7 @@ - (void)dealloc
@@ -9528,6 +9528,7 @@ - (void)dealloc
[layer release];
#endif
@@ -126,7 +96,7 @@ index 86bb7b6..2e8ae20 100644
[[self menu] release];
[super dealloc];
}
@@ -10884,6 +10882,32 @@ - (void)windowDidBecomeKey /* for direct calls */
@@ -10876,6 +10877,32 @@ - (void)windowDidBecomeKey /* for direct calls */
XSETFRAME (event.frame_or_window, emacsframe);
kbd_buffer_store_event (&event);
ns_send_appdefined (-1); // Kick main loop
@@ -159,7 +129,7 @@ index 86bb7b6..2e8ae20 100644
}
@@ -12121,6 +12145,332 @@ - (int) fullscreenState
@@ -12116,6 +12143,332 @@ - (int) fullscreenState
return fs_state;
}

View File

@@ -1,4 +1,4 @@
From 10ae74cefffd3733b8ff60e07a2b7f0dcb4d62cb Mon Sep 17 00:00:00 2001
From 4c53081f3308316fa6d03172d727876b8c90815c Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 12:58:11 +0100
Subject: [PATCH 7/9] doc: add VoiceOver accessibility section to macOS

View File

@@ -1,4 +1,4 @@
From 43b0625a7b9ce634006811e814fff037af8a51cd Mon Sep 17 00:00:00 2001
From a9ea010735a6839a9f5cceb728e1a072cf0b3bfb Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 14:46:25 +0100
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, copy) NSString *cachedCompletionAnnouncement;
diff --git a/src/nsterm.m b/src/nsterm.m
index 2e8ae20..b9b2a80 100644
index 93da88a..b3bdd42 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -7152,11 +7152,154 @@ Accessibility virtual elements (macOS / Cocoa only)
@@ -7147,11 +7147,154 @@ Accessibility virtual elements (macOS / Cocoa only)
/* ---- Helper: extract buffer text for accessibility ---- */
@@ -220,7 +220,7 @@ index 2e8ae20..b9b2a80 100644
static NSString *
ns_ax_buffer_text (struct window *w, ptrdiff_t *out_start,
ns_ax_visible_run **out_runs, NSUInteger *out_nruns)
@@ -7227,7 +7370,7 @@ Accessibility virtual elements (macOS / Cocoa only)
@@ -7222,7 +7365,7 @@ Accessibility virtual elements (macOS / Cocoa only)
/* Extract this visible run's text. Use
Fbuffer_substring_no_properties which correctly handles the
@@ -229,7 +229,7 @@ index 2e8ae20..b9b2a80 100644
include garbage bytes when the run spans the gap position. */
Lisp_Object lstr = Fbuffer_substring_no_properties (
make_fixnum (pos), make_fixnum (run_end));
@@ -7308,7 +7451,7 @@ Mode lines using icon fonts (e.g. doom-modeline with nerd-font)
@@ -7303,7 +7446,7 @@ Mode lines using icon fonts (e.g. doom-modeline with nerd-font)
return NSZeroRect;
/* charpos_start and charpos_len are already in buffer charpos
@@ -238,7 +238,7 @@ index 2e8ae20..b9b2a80 100644
charposForAccessibilityIndex which handles invisible text. */
ptrdiff_t cp_start = charpos_start;
ptrdiff_t cp_end = cp_start + charpos_len;
@@ -7787,6 +7930,7 @@ @implementation EmacsAccessibilityBuffer
@@ -7782,6 +7925,7 @@ @implementation EmacsAccessibilityBuffer
@synthesize cachedOverlayModiff;
@synthesize cachedTextStart;
@synthesize cachedModiff;
@@ -246,7 +246,7 @@ index 2e8ae20..b9b2a80 100644
@synthesize cachedPoint;
@synthesize cachedMarkActive;
@synthesize cachedCompletionAnnouncement;
@@ -7884,7 +8028,7 @@ - (void)ensureTextCache
@@ -7879,7 +8023,7 @@ - (void)ensureTextCache
NSTRACE ("EmacsAccessibilityBuffer ensureTextCache");
/* This method is only called from the main thread (AX getters
dispatch_sync to main first). Reads of cachedText/cachedTextModiff
@@ -255,7 +255,7 @@ index 2e8ae20..b9b2a80 100644
write section at the end needs synchronization to protect
against concurrent reads from AX server thread. */
eassert ([NSThread isMainThread]);
@@ -7897,16 +8041,15 @@ - (void)ensureTextCache
@@ -7892,16 +8036,15 @@ - (void)ensureTextCache
return;
ptrdiff_t modiff = BUF_MODIFF (b);
@@ -278,7 +278,7 @@ index 2e8ae20..b9b2a80 100644
&& cachedTextStart == BUF_BEGV (b)
&& pt >= cachedTextStart
&& (textLen == 0
@@ -7923,7 +8066,6 @@ - (void)ensureTextCache
@@ -7918,7 +8061,6 @@ - (void)ensureTextCache
[cachedText release];
cachedText = [text retain];
cachedTextModiff = modiff;
@@ -286,7 +286,7 @@ index 2e8ae20..b9b2a80 100644
cachedTextStart = start;
if (visibleRuns)
@@ -7988,7 +8130,7 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos
@@ -7983,7 +8125,7 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos
/* Binary search: runs are sorted by charpos (ascending). Find the
run whose [charpos, charpos+length) range contains the target,
or the nearest run after an invisible gap. O(log n) instead of
@@ -295,7 +295,7 @@ index 2e8ae20..b9b2a80 100644
NSUInteger lo = 0, hi = visibleRunCount;
while (lo < hi)
{
@@ -8001,7 +8143,7 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos
@@ -7996,7 +8138,7 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos
else
{
/* Found: charpos is inside this run. Compute UTF-16 delta
@@ -304,7 +304,7 @@ index 2e8ae20..b9b2a80 100644
NSUInteger chars_in = (NSUInteger)(charpos - r->charpos);
if (chars_in == 0 || !cachedText)
return r->ax_start;
@@ -8026,10 +8168,10 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos
@@ -8021,10 +8163,10 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos
/* Convert accessibility string index to buffer charpos.
Safe to call from any thread: uses only cachedText (NSString) and
@@ -317,7 +317,7 @@ index 2e8ae20..b9b2a80 100644
@synchronized (self)
{
if (visibleRunCount == 0)
@@ -8063,7 +8205,7 @@ - (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx
@@ -8058,7 +8200,7 @@ - (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx
return cp;
}
}
@@ -326,7 +326,7 @@ index 2e8ae20..b9b2a80 100644
if (lo > 0)
{
ns_ax_visible_run *last = &visibleRuns[visibleRunCount - 1];
@@ -8085,7 +8227,7 @@ - (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx
@@ -8080,7 +8222,7 @@ - (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx
deadlocking the AX server thread. This is prevented by:
1. validWindow checks WINDOW_LIVE_P and BUFFERP before every
@@ -335,7 +335,7 @@ index 2e8ae20..b9b2a80 100644
2. All dispatch_sync blocks run on the main thread where no
concurrent Lisp code can modify state between checks.
3. block_input prevents timer events and process output from
@@ -8439,6 +8581,50 @@ - (NSInteger)accessibilityInsertionPointLineNumber
@@ -8434,6 +8576,50 @@ - (NSInteger)accessibilityInsertionPointLineNumber
return [self lineForAXIndex:point_idx];
}
@@ -386,7 +386,7 @@ index 2e8ae20..b9b2a80 100644
- (NSRange)accessibilityRangeForLine:(NSInteger)line
{
if (![NSThread isMainThread])
@@ -8661,7 +8847,7 @@ - (NSRect)accessibilityFrame
@@ -8656,7 +8842,7 @@ - (NSRect)accessibilityFrame
/* ===================================================================
@@ -395,7 +395,7 @@ index 2e8ae20..b9b2a80 100644
These methods notify VoiceOver of text and selection changes.
Called from the redisplay cycle (postAccessibilityUpdates).
@@ -8676,7 +8862,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point
@@ -8671,7 +8857,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point
if (point > self.cachedPoint
&& point - self.cachedPoint == 1)
{
@@ -404,7 +404,7 @@ index 2e8ae20..b9b2a80 100644
[self invalidateTextCache];
[self ensureTextCache];
if (cachedText)
@@ -8695,7 +8881,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point
@@ -8690,7 +8876,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point
/* Update cachedPoint here so the selection-move branch does NOT
fire for point changes caused by edits. WebKit and Chromium
never send both ValueChanged and SelectedTextChanged for the
@@ -413,7 +413,7 @@ index 2e8ae20..b9b2a80 100644
self.cachedPoint = point;
NSDictionary *change = @{
@@ -9028,14 +9214,72 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f
@@ -9023,14 +9209,72 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f
BOOL markActive = !NILP (BVAR (b, mark_active));
/* --- Text changed (edit) --- */
@@ -488,7 +488,7 @@ index 2e8ae20..b9b2a80 100644
per the WebKit/Chromium pattern. */
else if (point != self.cachedPoint || markActive != self.cachedMarkActive)
{
@@ -9205,7 +9449,7 @@ - (NSRect)accessibilityFrame
@@ -9200,7 +9444,7 @@ - (NSRect)accessibilityFrame
/* ===================================================================
@@ -497,7 +497,7 @@ index 2e8ae20..b9b2a80 100644
=================================================================== */
/* Scan visible range of window W for interactive spans.
@@ -9413,7 +9657,7 @@ - (void) setAccessibilityFocused: (BOOL) focused
@@ -9408,7 +9652,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
@@ -506,7 +506,7 @@ index 2e8ae20..b9b2a80 100644
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)))
@@ -9439,7 +9683,7 @@ - (void) setAccessibilityFocused: (BOOL) focused
@@ -9434,7 +9678,7 @@ - (void) setAccessibilityFocused: (BOOL) focused
@end
@@ -515,7 +515,7 @@ index 2e8ae20..b9b2a80 100644
Methods are kept here (same .m file) so they access the ivars
declared in the @interface ivar block. */
@implementation EmacsAccessibilityBuffer (InteractiveSpans)
@@ -12161,7 +12405,7 @@ - (int) fullscreenState
@@ -12159,7 +12403,7 @@ - (int) fullscreenState
if (WINDOW_LEAF_P (w))
{
@@ -524,7 +524,7 @@ index 2e8ae20..b9b2a80 100644
EmacsAccessibilityBuffer *elem
= [existing objectForKey:[NSValue valueWithPointer:w]];
if (!elem)
@@ -12195,7 +12439,7 @@ - (int) fullscreenState
@@ -12193,7 +12437,7 @@ - (int) fullscreenState
}
else
{
@@ -533,7 +533,7 @@ index 2e8ae20..b9b2a80 100644
Lisp_Object child = w->contents;
while (!NILP (child))
{
@@ -12307,7 +12551,7 @@ - (void)postAccessibilityUpdates
@@ -12305,7 +12549,7 @@ - (void)postAccessibilityUpdates
accessibilityUpdating = YES;
/* Detect window tree change (split, delete, new buffer). Compare
@@ -542,7 +542,7 @@ index 2e8ae20..b9b2a80 100644
Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe);
if (!EQ (curRoot, lastRootWindow))
{
@@ -12316,12 +12560,12 @@ - (void)postAccessibilityUpdates
@@ -12314,12 +12558,12 @@ - (void)postAccessibilityUpdates
}
/* If tree is stale, rebuild FIRST so we don't iterate freed

View File

@@ -1,4 +1,4 @@
From 62e619d508d4ce3b3bf0f8dd959041bcd9a75350 Mon Sep 17 00:00:00 2001
From ca488b10323f136b3ce2593b56e72e222bdae7d6 Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 16:01:29 +0100
Subject: [PATCH 9/9] ns: announce child frame completion candidates for
@@ -43,8 +43,8 @@ refocus parent buffer element when child frame closes.
doc/emacs/macos.texi | 6 --
etc/NEWS | 4 +-
src/nsterm.h | 5 +
src/nsterm.m | 227 ++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 233 insertions(+), 9 deletions(-)
src/nsterm.m | 236 ++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 242 insertions(+), 9 deletions(-)
diff --git a/doc/emacs/macos.texi b/doc/emacs/macos.texi
index 4825cf9..97777e2 100644
@@ -86,7 +86,7 @@ index 2b1f9e6..8a40850 100644
for the *Completions* buffer. The implementation uses a virtual
accessibility tree with per-window elements, hybrid SelectedTextChanged
diff --git a/src/nsterm.h b/src/nsterm.h
index 2102fb9..2fc4de4 100644
index 2102fb9..dd98d56 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -594,6 +594,10 @@ typedef NS_ENUM (NSInteger, EmacsAXSpanType)
@@ -95,7 +95,7 @@ index 2102fb9..2fc4de4 100644
BOOL accessibilityUpdating;
+ BOOL childFrameCompletionActive;
+ char *childFrameLastCandidate;
+ struct buffer *childFrameLastBuffer;
+ Lisp_Object childFrameLastBuffer;
+ EMACS_INT childFrameLastModiff;
#endif
BOOL font_panel_active;
@@ -109,10 +109,10 @@ index 2102fb9..2fc4de4 100644
@end
diff --git a/src/nsterm.m b/src/nsterm.m
index b9b2a80..dc49417 100644
index b3bdd42..9f37c4e 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -7296,6 +7296,112 @@ visual line index for Zoom (skip whitespace-only lines
@@ -7291,6 +7291,112 @@ visual line index for Zoom (skip whitespace-only lines
return nil;
}
@@ -225,7 +225,34 @@ index b9b2a80..dc49417 100644
/* Build accessibility text for window W, skipping invisible text.
Populates *OUT_START with the buffer start charpos.
Populates *OUT_RUNS with an array of visible runs and *OUT_NRUNS
@@ -12535,6 +12641,77 @@ - (id)accessibilityFocusedUIElement
@@ -9028,6 +9134,7 @@ - (void)postCompletionAnnouncementForBuffer:(struct buffer *)b
ptrdiff_t currentOverlayStart = 0;
ptrdiff_t currentOverlayEnd = 0;
+ block_input ();
specpdl_ref count2 = SPECPDL_INDEX ();
record_unwind_current_buffer ();
if (b != current_buffer)
@@ -9186,6 +9293,7 @@ - (void)postCompletionAnnouncementForBuffer:(struct buffer *)b
self.cachedCompletionOverlayEnd = 0;
self.cachedCompletionPoint = 0;
}
+ unblock_input ();
}
/* ---- Notification dispatch (main entry point) ---- */
@@ -9773,6 +9881,10 @@ - (void)dealloc
#endif
[accessibilityElements release];
+#ifdef NS_IMPL_COCOA
+ if (childFrameLastCandidate)
+ xfree (childFrameLastCandidate);
+#endif
[[self menu] release];
[super dealloc];
}
@@ -12533,6 +12645,80 @@ - (id)accessibilityFocusedUIElement
The existing elements carry cached state (modiff, point) from the
previous redisplay cycle. Rebuilding first would create fresh
elements with current values, making change detection impossible. */
@@ -251,9 +278,12 @@ index b9b2a80..dc49417 100644
+ also guards against re-entrance: if Lisp calls below
+ trigger redisplay, the modiff check short-circuits. */
+ EMACS_INT modiff = BUF_MODIFF (b);
+ if (b == childFrameLastBuffer && modiff == childFrameLastModiff)
+ if (!BUFFER_LIVE_P (b))
+ return;
+ childFrameLastBuffer = b;
+ if (EQ (childFrameLastBuffer, make_lisp_ptr (b, Lisp_Vectorlike))
+ && modiff == childFrameLastModiff)
+ return;
+ childFrameLastBuffer = make_lisp_ptr (b, Lisp_Vectorlike);
+ childFrameLastModiff = modiff;
+
+ /* Skip buffers larger than a typical completion popup.
@@ -303,7 +333,7 @@ index b9b2a80..dc49417 100644
- (void)postAccessibilityUpdates
{
NSTRACE ("[EmacsView postAccessibilityUpdates]");
@@ -12545,11 +12722,59 @@ - (void)postAccessibilityUpdates
@@ -12543,11 +12729,59 @@ - (void)postAccessibilityUpdates
/* Re-entrance guard: VoiceOver callbacks during notification posting
can trigger redisplay, which calls ns_update_end, which calls us