patches: add Zoom completion tracking (overlay + child frame)

Zoom patch 0000 now tracks completion candidates:
- Overlay: Vertico, Icomplete, Ivy (face heuristic on before-string)
- Child frame: Corfu, Company-box (scan buffer text for selected face)
Also fixes duplicate lastCursorRect ivar when applied with VoiceOver.
This commit is contained in:
2026-03-01 03:38:58 +01:00
parent 9110eee881
commit b283068f82
9 changed files with 340 additions and 86 deletions

View File

@@ -1,4 +1,4 @@
From 8a45d478b23b3bc2a1ae039493ba90b07eb89c72 Mon Sep 17 00:00:00 2001 From 45076d26a15ae82b489349d481f3c1a1792730a5 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
@@ -6,32 +6,46 @@ Subject: [PATCH 1/9] ns: integrate with macOS Zoom for cursor tracking
Inform macOS Zoom of the text cursor position so the zoomed viewport Inform macOS Zoom of the text cursor position so the zoomed viewport
follows keyboard focus in Emacs. follows keyboard focus in Emacs.
Basic cursor tracking:
* src/nsterm.h (EmacsView): Add lastCursorRect, zoomCursorUpdated. * src/nsterm.h (EmacsView): Add lastCursorRect, zoomCursorUpdated.
* src/nsterm.m (ns_draw_window_cursor): Store cursor rect in * src/nsterm.m (ns_draw_window_cursor): Store cursor rect in
lastCursorRect; call UAZoomChangeFocus with CG-space coordinates lastCursorRect; call UAZoomChangeFocus with CG-space coordinates
when UAZoomEnabled returns true. Set zoomCursorUpdated flag. when UAZoomEnabled returns true. Set zoomCursorUpdated flag.
(ns_update_end): Call UAZoomChangeFocus as fallback when cursor (ns_update_end): Call UAZoomChangeFocus as fallback when cursor
was not physically redrawn in this cycle (e.g., after C-x o window was not physically redrawn (e.g. after C-x o window switch).
switch). Gated by zoomCursorUpdated to avoid double calls. Gated by zoomCursorUpdated to avoid double calls.
Completion candidate tracking:
* src/nsterm.m (ns_zoom_face_is_selected): New predicate.
Match 'current', 'selected', and 'selection' in face symbol
names to identify the highlighted completion candidate.
(ns_zoom_find_overlay_candidate_line): Scan overlay
before-string/after-string for the selected candidate line.
Handles Vertico, Icomplete, Ivy, and similar overlay frameworks.
(ns_zoom_find_child_frame_candidate): Scan child frame buffer
text for the selected candidate. Handles Corfu, Company-box,
and similar child frame frameworks.
(ns_zoom_track_completion): Called from ns_update_end after
cursor tracking. Overrides Zoom focus to the selected
completion candidate when one is found.
Coordinate conversion: EmacsView pixels (AppKit, flipped) -> Coordinate conversion: EmacsView pixels (AppKit, flipped) ->
NSWindow -> NSScreen -> CGRect with y-flip for CoreGraphics NSWindow -> NSScreen -> CGRect with y-flip for CoreGraphics
top-left origin. UAZoomEnabled returns false when Zoom is inactive, top-left origin.
so overhead is a single function call per redisplay cycle.
Tested on macOS 14 with Zoom enabled: cursor tracking works across Tested on macOS 14 with Zoom enabled: cursor tracking works across
window splits, switches (C-x o), and normal navigation. window splits, switches (C-x o), and completion frameworks.
--- ---
etc/NEWS | 8 +++++++ etc/NEWS | 11 ++
src/nsterm.h | 6 +++++ src/nsterm.h | 6 ++
src/nsterm.m | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/nsterm.m | 285 +++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 82 insertions(+) 3 files changed, 302 insertions(+)
diff --git a/etc/NEWS b/etc/NEWS diff --git a/etc/NEWS b/etc/NEWS
index ef36df5..f10d17e 100644 index ef36df5..80661a9 100644
--- a/etc/NEWS --- a/etc/NEWS
+++ b/etc/NEWS +++ b/etc/NEWS
@@ -82,6 +82,14 @@ other directory on your system. You can also invoke the @@ -82,6 +82,17 @@ other directory on your system. You can also invoke the
* Changes in Emacs 31.1 * Changes in Emacs 31.1
@@ -41,7 +55,10 @@ index ef36df5..f10d17e 100644
+Follow keyboard focus), Emacs informs Zoom of the text cursor position +Follow keyboard focus), Emacs informs Zoom of the text cursor position
+after every cursor redraw via 'UAZoomChangeFocus'. The zoomed viewport +after every cursor redraw via 'UAZoomChangeFocus'. The zoomed viewport
+automatically tracks the insertion point across window splits and +automatically tracks the insertion point across window splits and
+switches. +switches. Completion frameworks (Vertico, Icomplete, Ivy for overlay
+candidates; Corfu, Company-box for child frame popups) are also
+tracked: Zoom follows the selected candidate rather than the text
+cursor during completion.
+ +
+++ +++
** 'line-spacing' now supports specifying spacing above the line. ** 'line-spacing' now supports specifying spacing above the line.
@@ -64,10 +81,228 @@ 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..cd721c8 100644 index 74e4ad5..05ec3d1 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -1104,6 +1104,35 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) @@ -1081,6 +1081,217 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
}
+
+#ifdef NS_IMPL_COCOA
+#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;
+}
+
+/* Scan overlay before-string / after-string properties in the
+ selected window for a completion candidate with a "selected"
+ face. Return the 0-based visual line index of the selected
+ candidate, or -1 if none found. */
+static int
+ns_zoom_find_overlay_candidate_line (struct window *w)
+{
+ struct buffer *b = XBUFFER (w->contents);
+ ptrdiff_t beg = marker_position (w->start);
+ ptrdiff_t end = ZV_S (b);
+ Lisp_Object overlays = Foverlays_in (make_fixnum (beg),
+ make_fixnum (end));
+ Lisp_Object tail;
+
+ for (tail = overlays; CONSP (tail); tail = XCDR (tail))
+ {
+ Lisp_Object ov = XCAR (tail);
+ Lisp_Object str = Foverlay_get (ov, Qbefore_string);
+
+ if (NILP (str))
+ str = Foverlay_get (ov, Qafter_string);
+ if (!STRINGP (str) || SCHARS (str) < 2)
+ continue;
+
+ /* Walk the string line by line, checking faces. */
+ ptrdiff_t len = SCHARS (str);
+ int line = 0;
+ ptrdiff_t line_start = 0;
+
+ for (ptrdiff_t i = 0; i <= len; i++)
+ {
+ bool at_newline = (i == len
+ || SREF (str, i) == '\n');
+ if (at_newline && i > line_start)
+ {
+ /* Check the face at line_start. */
+ Lisp_Object face
+ = Fget_text_property (make_fixnum (line_start),
+ Qface, str);
+ if (ns_zoom_face_is_selected (face))
+ return line;
+ line++;
+ line_start = i + 1;
+ }
+ else if (at_newline)
+ {
+ line++;
+ line_start = i + 1;
+ }
+ }
+ }
+ return -1;
+}
+
+/* Scan child frames for a completion popup with a selected
+ candidate. Return the 0-based line index, or -1 if none.
+ Set *CHILD_FRAME to the child frame if found. */
+static int
+ns_zoom_find_child_frame_candidate (struct frame *f,
+ struct frame **child_frame)
+{
+ Lisp_Object frames, tail;
+
+ FOR_EACH_FRAME (tail, frames)
+ {
+ struct frame *cf = XFRAME (frames);
+ if (!FRAME_NS_P (cf) || !FRAME_LIVE_P (cf))
+ continue;
+ if (FRAME_PARENT_FRAME (cf) != f)
+ continue;
+ /* Small buffer = likely completion popup. */
+ struct buffer *b = XBUFFER (cf->current_buffer);
+ if (BUF_ZV (b) - BUF_BEGV (b) > 10000)
+ continue;
+
+ ptrdiff_t beg = BUF_BEGV (b);
+ ptrdiff_t zv = BUF_ZV (b);
+ int line = 0;
+
+ 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))
+ {
+ *child_frame = cf;
+ return line;
+ }
+ /* Advance to next line. */
+ ptrdiff_t next = find_newline (pos, -1, zv, -1,
+ 1, NULL, NULL, false);
+ if (next <= pos)
+ break;
+ pos = next;
+ line++;
+ }
+ }
+ return -1;
+}
+
+/* Update Zoom focus based on completion candidates.
+ Called from ns_update_end after normal cursor tracking.
+ If a completion candidate is selected (overlay or child frame),
+ move Zoom to that candidate instead of the text cursor. */
+static void
+ns_zoom_track_completion (struct frame *f, EmacsView *view)
+{
+ if (!UAZoomEnabled ())
+ return;
+
+ struct window *w = XWINDOW (f->selected_window);
+ int line_h = FRAME_LINE_HEIGHT (f);
+
+ /* 1. Check overlay completions (Vertico, Icomplete, Ivy). */
+ int ov_line = ns_zoom_find_overlay_candidate_line (w);
+ if (ov_line >= 0)
+ {
+ /* Overlay candidates typically start after the input line,
+ so the visual offset is (ov_line + 1) * line_h from
+ the window top. */
+ int y_off = (ov_line + 1) * line_h;
+ if (y_off < w->pixel_height)
+ {
+ NSRect r = NSMakeRect (
+ WINDOW_TEXT_TO_FRAME_PIXEL_X (w, 0),
+ WINDOW_TO_FRAME_PIXEL_Y (w, y_off),
+ FRAME_COLUMN_WIDTH (f),
+ line_h);
+
+ NSRect windowRect = [view convertRect:r toView:nil];
+ NSRect screenRect
+ = [[view window] convertRectToScreen:windowRect];
+ CGRect cgRect = NSRectToCGRect (screenRect);
+ CGFloat primaryH
+ = [[[NSScreen screens] firstObject] frame].size.height;
+ cgRect.origin.y
+ = primaryH - cgRect.origin.y - cgRect.size.height;
+
+ UAZoomChangeFocus (&cgRect, &cgRect,
+ kUAZoomFocusTypeInsertionPoint);
+ return;
+ }
+ }
+
+ /* 2. Check child frame completions (Corfu, Company-box). */
+ struct frame *cf = NULL;
+ int cf_line = ns_zoom_find_child_frame_candidate (f, &cf);
+ if (cf_line >= 0 && cf)
+ {
+ EmacsView *cv = FRAME_NS_VIEW (cf);
+ struct window *cw
+ = XWINDOW (cf->selected_window);
+ int cf_line_h = FRAME_LINE_HEIGHT (cf);
+ int y_off = cf_line * cf_line_h;
+
+ NSRect r = NSMakeRect (
+ WINDOW_TEXT_TO_FRAME_PIXEL_X (cw, 0),
+ WINDOW_TO_FRAME_PIXEL_Y (cw, y_off),
+ FRAME_COLUMN_WIDTH (cf),
+ cf_line_h);
+
+ NSRect windowRect = [cv convertRect:r toView:nil];
+ NSRect screenRect
+ = [[cv window] convertRectToScreen:windowRect];
+ CGRect cgRect = NSRectToCGRect (screenRect);
+ CGFloat primaryH
+ = [[[NSScreen screens] firstObject] frame].size.height;
+ cgRect.origin.y
+ = primaryH - cgRect.origin.y - cgRect.size.height;
+
+ UAZoomChangeFocus (&cgRect, &cgRect,
+ kUAZoomFocusTypeInsertionPoint);
+ }
+}
+
+#endif /* MAC_OS_X_VERSION_MIN_REQUIRED >= 101000 */
+#endif /* NS_IMPL_COCOA */
+
static void
ns_update_end (struct frame *f)
/* --------------------------------------------------------------------------
@@ -1104,6 +1315,41 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
unblock_input (); unblock_input ();
ns_updating_frame = NULL; ns_updating_frame = NULL;
@@ -99,11 +334,17 @@ index 74e4ad5..cd721c8 100644
+ if (view) + if (view)
+ view->zoomCursorUpdated = NO; + view->zoomCursorUpdated = NO;
+#endif +#endif
+
+ /* Track completion candidates for Zoom (overlay and child frame).
+ Runs after cursor tracking so the selected candidate overrides
+ the default cursor position. */
+ if (view)
+ ns_zoom_track_completion (f, view);
+#endif /* NS_IMPL_COCOA */ +#endif /* NS_IMPL_COCOA */
} }
static void static void
@@ -3232,6 +3261,45 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors. @@ -3232,6 +3478,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 67bf4786bb3896241bf7ab75cdada0e3da924ec5 Mon Sep 17 00:00:00 2001 From 59ec39bdda532d02c748a7e27f9a90ba3be3c338 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 cd721c8..1320a9f 100644 index 05ec3d1..a4d0e02 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 cd721c8..1320a9f 100644
#include "systime.h" #include "systime.h"
#include "character.h" #include "character.h"
#include "xwidget.h" #include "xwidget.h"
@@ -6924,6 +6925,430 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg @@ -7141,6 +7142,430 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
} }
#endif #endif
@@ -630,7 +630,7 @@ index cd721c8..1320a9f 100644
/* ========================================================================== /* ==========================================================================
EmacsView implementation EmacsView implementation
@@ -11380,6 +11805,28 @@ Convert an X font name (XLFD) to an NS font name. @@ -11597,6 +12022,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 cd721c8..1320a9f 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));
@@ -11528,6 +11975,15 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with @@ -11745,6 +12192,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,4 +1,4 @@
From 80e590420ce74bb886849d91c245d0c0bece39bf Mon Sep 17 00:00:00 2001 From 80f6de2020c50717e2238528e6241e00017f7230 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, 1097 insertions(+) 1 file changed, 1097 insertions(+)
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 1320a9f..ab9a287 100644 index a4d0e02..e281073 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -7346,6 +7346,1103 @@ - (id)accessibilityTopLevelUIElement @@ -7563,6 +7563,1103 @@ - (id)accessibilityTopLevelUIElement
@end @end

View File

@@ -1,4 +1,4 @@
From afadafa16eef6e8e6d01ef4f340c451132198ba8 Mon Sep 17 00:00:00 2001 From 672223b6ced41898156aa476b8c77b27d3719e4a 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 ab9a287..330667d 100644 index e281073..a29f039 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -8443,6 +8443,551 @@ - (NSRect)accessibilityFrame @@ -8660,6 +8660,551 @@ - (NSRect)accessibilityFrame
@end @end

View File

@@ -1,4 +1,4 @@
From 010ec7a6e568653e12b904c5bb812a1b3c8e52cf Mon Sep 17 00:00:00 2001 From 64b373bebadd9b9c52c90da297173ea108c6e394 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 330667d..907ce47 100644 index a29f039..86bb7b6 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -8988,6 +8988,292 @@ - (NSRect)accessibilityFrame @@ -9205,6 +9205,292 @@ - (NSRect)accessibilityFrame
@end @end

View File

@@ -1,4 +1,4 @@
From f10036eeadf681cd87bbec7ec0581b572053c38a Mon Sep 17 00:00:00 2001 From be594f584a94e6fccd649be029c1af30242a12ee 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
@@ -23,14 +23,14 @@ block cursor, org-mode folded headings, indirect buffers.
Known limitations documented in patch 6 Texinfo node. Known limitations documented in patch 6 Texinfo node.
--- ---
etc/NEWS | 13 ++ etc/NEWS | 13 ++
src/nsterm.m | 369 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/nsterm.m | 376 +++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 374 insertions(+), 8 deletions(-) 2 files changed, 376 insertions(+), 13 deletions(-)
diff --git a/etc/NEWS b/etc/NEWS diff --git a/etc/NEWS b/etc/NEWS
index f10d17e..f48d05b 100644 index 80661a9..2b1f9e6 100644
--- a/etc/NEWS --- a/etc/NEWS
+++ b/etc/NEWS +++ b/etc/NEWS
@@ -4397,6 +4397,19 @@ allowing Emacs users access to speech recognition utilities. @@ -4400,6 +4400,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,20 +51,28 @@ index f10d17e..f48d05b 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 907ce47..d813274 100644 index 86bb7b6..2e8ae20 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -1133,6 +1133,9 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) @@ -1343,13 +1343,16 @@ so the visual offset is (ov_line + 1) * line_h from
}
if (view) if (view)
view->zoomCursorUpdated = NO; view->zoomCursorUpdated = NO;
#endif -#endif
/* Track completion candidates for Zoom (overlay and child frame).
Runs after cursor tracking so the selected candidate overrides
the default cursor position. */
if (view)
ns_zoom_track_completion (f, view);
+#endif
+ +
+ /* Post accessibility notifications after each redisplay cycle. */ + /* Post accessibility notifications after each redisplay cycle. */
+ [view postAccessibilityUpdates]; + [view postAccessibilityUpdates];
#endif /* NS_IMPL_COCOA */ #endif /* NS_IMPL_COCOA */
} }
@@ -3263,11 +3266,11 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors. @@ -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)); r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA));
#ifdef NS_IMPL_COCOA #ifdef NS_IMPL_COCOA
@@ -73,15 +81,20 @@ index 907ce47..d813274 100644
- element to keep the zoomed viewport centered on the cursor. - element to keep the zoomed viewport centered on the cursor.
- -
- Coordinate conversion: - Coordinate conversion:
+ /* Store cursor rect and inform macOS Zoom / VoiceOver. - EmacsView pixels (AppKit, flipped, top-left origin)
+ lastCursorRect is used by: - -> NSWindow (convertRect:toView:nil)
+ - Zoom: UAZoomChangeFocus below (unconditional when active) - -> NSScreen (convertRectToScreen:)
+ - VoiceOver: accessibilityBoundsForRange: fallback - -> 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: + Coordinate conversion for Zoom:
EmacsView pixels (AppKit, flipped, top-left origin) + EmacsView (AppKit, flipped) -> NSWindow -> NSScreen
-> NSWindow (convertRect:toView:nil) + -> CGRect with y-flip for CoreGraphics top-left origin. */
-> NSScreen (convertRectToScreen:) {
@@ -7349,7 +7352,6 @@ - (id)accessibilityTopLevelUIElement EmacsView *view = FRAME_NS_VIEW (f);
if (view && on_p && active_p)
@@ -7566,7 +7566,6 @@ - (id)accessibilityTopLevelUIElement
@@ -89,7 +102,7 @@ index 907ce47..d813274 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,
@@ -8444,7 +8446,6 @@ - (NSRect)accessibilityFrame @@ -8661,7 +8660,6 @@ - (NSRect)accessibilityFrame
@end @end
@@ -97,7 +110,7 @@ index 907ce47..d813274 100644
/* =================================================================== /* ===================================================================
EmacsAccessibilityBuffer (Notifications) — AX event dispatch EmacsAccessibilityBuffer (Notifications) — AX event dispatch
@@ -8989,7 +8990,6 @@ - (NSRect)accessibilityFrame @@ -9206,7 +9204,6 @@ - (NSRect)accessibilityFrame
@end @end
@@ -105,7 +118,7 @@ index 907ce47..d813274 100644
/* =================================================================== /* ===================================================================
EmacsAccessibilityInteractiveSpan — helpers and implementation EmacsAccessibilityInteractiveSpan — helpers and implementation
=================================================================== */ =================================================================== */
@@ -9319,6 +9319,7 @@ - (void)dealloc @@ -9536,6 +9533,7 @@ - (void)dealloc
[layer release]; [layer release];
#endif #endif
@@ -113,7 +126,7 @@ index 907ce47..d813274 100644
[[self menu] release]; [[self menu] release];
[super dealloc]; [super dealloc];
} }
@@ -10667,6 +10668,32 @@ - (void)windowDidBecomeKey /* for direct calls */ @@ -10884,6 +10882,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
@@ -146,7 +159,7 @@ index 907ce47..d813274 100644
} }
@@ -11904,6 +11931,332 @@ - (int) fullscreenState @@ -12121,6 +12145,332 @@ - (int) fullscreenState
return fs_state; return fs_state;
} }

View File

@@ -1,4 +1,4 @@
From 40bf8a0163b876e1d8f71fa8bd3c4aaa2a28ad52 Mon Sep 17 00:00:00 2001 From 10ae74cefffd3733b8ff60e07a2b7f0dcb4d62cb 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 456c6a734fb94943b1d9acfaa8c1ffd0f1321ab5 Mon Sep 17 00:00:00 2001 From 43b0625a7b9ce634006811e814fff037af8a51cd 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 d813274..9e089f3 100644 index 2e8ae20..b9b2a80 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -6938,11 +6938,154 @@ Accessibility virtual elements (macOS / Cocoa only) @@ -7152,11 +7152,154 @@ Accessibility virtual elements (macOS / Cocoa only)
/* ---- Helper: extract buffer text for accessibility ---- */ /* ---- Helper: extract buffer text for accessibility ---- */
@@ -220,7 +220,7 @@ index d813274..9e089f3 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)
@@ -7013,7 +7156,7 @@ Accessibility virtual elements (macOS / Cocoa only) @@ -7227,7 +7370,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 d813274..9e089f3 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));
@@ -7094,7 +7237,7 @@ Mode lines using icon fonts (e.g. doom-modeline with nerd-font) @@ -7308,7 +7451,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 d813274..9e089f3 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;
@@ -7573,6 +7716,7 @@ @implementation EmacsAccessibilityBuffer @@ -7787,6 +7930,7 @@ @implementation EmacsAccessibilityBuffer
@synthesize cachedOverlayModiff; @synthesize cachedOverlayModiff;
@synthesize cachedTextStart; @synthesize cachedTextStart;
@synthesize cachedModiff; @synthesize cachedModiff;
@@ -246,7 +246,7 @@ index d813274..9e089f3 100644
@synthesize cachedPoint; @synthesize cachedPoint;
@synthesize cachedMarkActive; @synthesize cachedMarkActive;
@synthesize cachedCompletionAnnouncement; @synthesize cachedCompletionAnnouncement;
@@ -7670,7 +7814,7 @@ - (void)ensureTextCache @@ -7884,7 +8028,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 d813274..9e089f3 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]);
@@ -7683,16 +7827,15 @@ - (void)ensureTextCache @@ -7897,16 +8041,15 @@ - (void)ensureTextCache
return; return;
ptrdiff_t modiff = BUF_MODIFF (b); ptrdiff_t modiff = BUF_MODIFF (b);
@@ -278,7 +278,7 @@ index d813274..9e089f3 100644
&& cachedTextStart == BUF_BEGV (b) && cachedTextStart == BUF_BEGV (b)
&& pt >= cachedTextStart && pt >= cachedTextStart
&& (textLen == 0 && (textLen == 0
@@ -7709,7 +7852,6 @@ - (void)ensureTextCache @@ -7923,7 +8066,6 @@ - (void)ensureTextCache
[cachedText release]; [cachedText release];
cachedText = [text retain]; cachedText = [text retain];
cachedTextModiff = modiff; cachedTextModiff = modiff;
@@ -286,7 +286,7 @@ index d813274..9e089f3 100644
cachedTextStart = start; cachedTextStart = start;
if (visibleRuns) if (visibleRuns)
@@ -7774,7 +7916,7 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos @@ -7988,7 +8130,7 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos
/* Binary search: runs are sorted by charpos (ascending). Find the /* Binary search: runs are sorted by charpos (ascending). Find the
run whose [charpos, charpos+length) range contains the target, run whose [charpos, charpos+length) range contains the target,
or the nearest run after an invisible gap. O(log n) instead of or the nearest run after an invisible gap. O(log n) instead of
@@ -295,7 +295,7 @@ index d813274..9e089f3 100644
NSUInteger lo = 0, hi = visibleRunCount; NSUInteger lo = 0, hi = visibleRunCount;
while (lo < hi) while (lo < hi)
{ {
@@ -7787,7 +7929,7 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos @@ -8001,7 +8143,7 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos
else else
{ {
/* Found: charpos is inside this run. Compute UTF-16 delta /* Found: charpos is inside this run. Compute UTF-16 delta
@@ -304,7 +304,7 @@ index d813274..9e089f3 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;
@@ -7812,10 +7954,10 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos @@ -8026,10 +8168,10 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos
/* Convert accessibility string index to buffer charpos. /* Convert accessibility string index to buffer charpos.
Safe to call from any thread: uses only cachedText (NSString) and Safe to call from any thread: uses only cachedText (NSString) and
@@ -317,7 +317,7 @@ index d813274..9e089f3 100644
@synchronized (self) @synchronized (self)
{ {
if (visibleRunCount == 0) if (visibleRunCount == 0)
@@ -7849,7 +7991,7 @@ - (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx @@ -8063,7 +8205,7 @@ - (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx
return cp; return cp;
} }
} }
@@ -326,7 +326,7 @@ index d813274..9e089f3 100644
if (lo > 0) if (lo > 0)
{ {
ns_ax_visible_run *last = &visibleRuns[visibleRunCount - 1]; ns_ax_visible_run *last = &visibleRuns[visibleRunCount - 1];
@@ -7871,7 +8013,7 @@ - (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx @@ -8085,7 +8227,7 @@ - (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx
deadlocking the AX server thread. This is prevented by: deadlocking the AX server thread. This is prevented by:
1. validWindow checks WINDOW_LIVE_P and BUFFERP before every 1. validWindow checks WINDOW_LIVE_P and BUFFERP before every
@@ -335,7 +335,7 @@ index d813274..9e089f3 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
@@ -8225,6 +8367,50 @@ - (NSInteger)accessibilityInsertionPointLineNumber @@ -8439,6 +8581,50 @@ - (NSInteger)accessibilityInsertionPointLineNumber
return [self lineForAXIndex:point_idx]; return [self lineForAXIndex:point_idx];
} }
@@ -386,7 +386,7 @@ index d813274..9e089f3 100644
- (NSRange)accessibilityRangeForLine:(NSInteger)line - (NSRange)accessibilityRangeForLine:(NSInteger)line
{ {
if (![NSThread isMainThread]) if (![NSThread isMainThread])
@@ -8447,7 +8633,7 @@ - (NSRect)accessibilityFrame @@ -8661,7 +8847,7 @@ - (NSRect)accessibilityFrame
/* =================================================================== /* ===================================================================
@@ -395,7 +395,7 @@ index d813274..9e089f3 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).
@@ -8462,7 +8648,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point @@ -8676,7 +8862,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point
if (point > self.cachedPoint if (point > self.cachedPoint
&& point - self.cachedPoint == 1) && point - self.cachedPoint == 1)
{ {
@@ -404,7 +404,7 @@ index d813274..9e089f3 100644
[self invalidateTextCache]; [self invalidateTextCache];
[self ensureTextCache]; [self ensureTextCache];
if (cachedText) if (cachedText)
@@ -8481,7 +8667,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point @@ -8695,7 +8881,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point
/* Update cachedPoint here so the selection-move branch does NOT /* Update cachedPoint here so the selection-move branch does NOT
fire for point changes caused by edits. WebKit and Chromium fire for point changes caused by edits. WebKit and Chromium
never send both ValueChanged and SelectedTextChanged for the never send both ValueChanged and SelectedTextChanged for the
@@ -413,7 +413,7 @@ index d813274..9e089f3 100644
self.cachedPoint = point; self.cachedPoint = point;
NSDictionary *change = @{ NSDictionary *change = @{
@@ -8814,14 +9000,72 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f @@ -9028,14 +9214,72 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f
BOOL markActive = !NILP (BVAR (b, mark_active)); BOOL markActive = !NILP (BVAR (b, mark_active));
/* --- Text changed (edit) --- */ /* --- Text changed (edit) --- */
@@ -488,7 +488,7 @@ index d813274..9e089f3 100644
per the WebKit/Chromium pattern. */ per the WebKit/Chromium pattern. */
else if (point != self.cachedPoint || markActive != self.cachedMarkActive) else if (point != self.cachedPoint || markActive != self.cachedMarkActive)
{ {
@@ -8991,7 +9235,7 @@ - (NSRect)accessibilityFrame @@ -9205,7 +9449,7 @@ - (NSRect)accessibilityFrame
/* =================================================================== /* ===================================================================
@@ -497,7 +497,7 @@ index d813274..9e089f3 100644
=================================================================== */ =================================================================== */
/* Scan visible range of window W for interactive spans. /* Scan visible range of window W for interactive spans.
@@ -9199,7 +9443,7 @@ - (void) setAccessibilityFocused: (BOOL) focused @@ -9413,7 +9657,7 @@ - (void) setAccessibilityFocused: (BOOL) focused
dispatch_async (dispatch_get_main_queue (), ^{ dispatch_async (dispatch_get_main_queue (), ^{
/* lwin is a Lisp_Object captured by value. This is GC-safe /* lwin is a Lisp_Object captured by value. This is GC-safe
because Lisp_Objects are tagged integers/pointers that because Lisp_Objects are tagged integers/pointers that
@@ -506,7 +506,7 @@ index d813274..9e089f3 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)))
@@ -9225,7 +9469,7 @@ - (void) setAccessibilityFocused: (BOOL) focused @@ -9439,7 +9683,7 @@ - (void) setAccessibilityFocused: (BOOL) focused
@end @end
@@ -515,7 +515,7 @@ index d813274..9e089f3 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)
@@ -11947,7 +12191,7 @@ - (int) fullscreenState @@ -12161,7 +12405,7 @@ - (int) fullscreenState
if (WINDOW_LEAF_P (w)) if (WINDOW_LEAF_P (w))
{ {
@@ -524,7 +524,7 @@ index d813274..9e089f3 100644
EmacsAccessibilityBuffer *elem EmacsAccessibilityBuffer *elem
= [existing objectForKey:[NSValue valueWithPointer:w]]; = [existing objectForKey:[NSValue valueWithPointer:w]];
if (!elem) if (!elem)
@@ -11981,7 +12225,7 @@ - (int) fullscreenState @@ -12195,7 +12439,7 @@ - (int) fullscreenState
} }
else else
{ {
@@ -533,7 +533,7 @@ index d813274..9e089f3 100644
Lisp_Object child = w->contents; Lisp_Object child = w->contents;
while (!NILP (child)) while (!NILP (child))
{ {
@@ -12093,7 +12337,7 @@ - (void)postAccessibilityUpdates @@ -12307,7 +12551,7 @@ - (void)postAccessibilityUpdates
accessibilityUpdating = YES; accessibilityUpdating = YES;
/* Detect window tree change (split, delete, new buffer). Compare /* Detect window tree change (split, delete, new buffer). Compare
@@ -542,7 +542,7 @@ index d813274..9e089f3 100644
Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe); Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe);
if (!EQ (curRoot, lastRootWindow)) if (!EQ (curRoot, lastRootWindow))
{ {
@@ -12102,12 +12346,12 @@ - (void)postAccessibilityUpdates @@ -12316,12 +12560,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 16e60a514212d8e5e541eb731db242b2bcd1bca2 Mon Sep 17 00:00:00 2001 From 62e619d508d4ce3b3bf0f8dd959041bcd9a75350 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
@@ -71,10 +71,10 @@ index 4825cf9..97777e2 100644
To disable the accessibility interface entirely (for instance, to To disable the accessibility interface entirely (for instance, to
eliminate overhead on systems where assistive technology is not in eliminate overhead on systems where assistive technology is not in
diff --git a/etc/NEWS b/etc/NEWS diff --git a/etc/NEWS b/etc/NEWS
index f48d05b..ec4b95e 100644 index 2b1f9e6..8a40850 100644
--- a/etc/NEWS --- a/etc/NEWS
+++ b/etc/NEWS +++ b/etc/NEWS
@@ -4401,8 +4401,8 @@ send user data to Apple's speech recognition servers. @@ -4404,8 +4404,8 @@ send user data to Apple's speech recognition servers.
** VoiceOver accessibility support on macOS. ** VoiceOver accessibility support on macOS.
Emacs now exposes buffer content, cursor position, and interactive Emacs now exposes buffer content, cursor position, and interactive
elements to the macOS accessibility subsystem (VoiceOver). This elements to the macOS accessibility subsystem (VoiceOver). This
@@ -109,10 +109,10 @@ index 2102fb9..2fc4de4 100644
@end @end
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 9e089f3..1a623be 100644 index b9b2a80..dc49417 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -7082,6 +7082,112 @@ visual line index for Zoom (skip whitespace-only lines @@ -7296,6 +7296,112 @@ visual line index for Zoom (skip whitespace-only lines
return nil; return nil;
} }
@@ -225,7 +225,7 @@ index 9e089f3..1a623be 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
@@ -12321,6 +12427,77 @@ - (id)accessibilityFocusedUIElement @@ -12535,6 +12641,77 @@ - (id)accessibilityFocusedUIElement
The existing elements carry cached state (modiff, point) from the The existing elements carry cached state (modiff, point) from the
previous redisplay cycle. Rebuilding first would create fresh previous redisplay cycle. Rebuilding first would create fresh
elements with current values, making change detection impossible. */ elements with current values, making change detection impossible. */
@@ -303,7 +303,7 @@ index 9e089f3..1a623be 100644
- (void)postAccessibilityUpdates - (void)postAccessibilityUpdates
{ {
NSTRACE ("[EmacsView postAccessibilityUpdates]"); NSTRACE ("[EmacsView postAccessibilityUpdates]");
@@ -12331,11 +12508,59 @@ - (void)postAccessibilityUpdates @@ -12545,11 +12722,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