From 946b138a0a7a4757c59a0a771209485c549c79c1 Mon Sep 17 00:00:00 2001 From: Daneel Date: Thu, 26 Feb 2026 10:47:12 +0100 Subject: [PATCH] v14.1: post notifications on focused virtual element, not EmacsView Root cause of typing echo + cursor movement failure: VoiceOver tracks the focused element (EmacsAccessibilityBuffer, AXTextArea). Notifications from EmacsView (AXGroup) were ignored because VoiceOver doesn't monitor non-focused elements for text changes. Fix: ns_draw_window_cursor now calls [view accessibilityFocusedUIElement] to find the right EmacsAccessibilityBuffer, and posts ValueChanged + SelectedTextChanged on IT instead of on the view. Also: postAccessibilityUpdatesForWindow now uses [self accessibilityStringForRange:] instead of [view accessibilityStringForRange:] for typing echo text. --- ...oundsForRange-for-macOS-Zoom-cursor-.patch | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/patches/0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch b/patches/0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch index 36809b0..25f621a 100644 --- a/patches/0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch +++ b/patches/0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch @@ -2,12 +2,9 @@ From: Martin Sukany Date: Wed, 26 Feb 2026 00:00:00 +0100 Subject: [PATCH] ns: add macOS Zoom cursor tracking and VoiceOver accessibility -Dual accessibility support: -1. UAZoomChangeFocus + accessibilityBoundsForRange on EmacsView for Zoom -2. Virtual element tree (EmacsAccessibilityBuffer) for VoiceOver -3. Typing echo from ns_draw_window_cursor on EmacsView -4. Full hierarchy plumbing: accessibilityWindow, accessibilityTopLevelUIElement, - accessibilityParent, isAccessibilityFocused, FocusedUIElementChanged +Dual accessibility: UAZoomChangeFocus for Zoom + virtual element tree for +VoiceOver. Notifications target the focused virtual element, not EmacsView. +Full hierarchy plumbing (accessibilityWindow, accessibilityParent, etc.). MRC compatible. --- --- a/src/nsterm.h 2026-02-26 08:46:18.118172281 +0100 @@ -74,7 +71,7 @@ MRC compatible. --- a/src/nsterm.m 2026-02-26 08:46:18.124172384 +0100 -+++ b/src/nsterm.m 2026-02-26 10:19:39.112307036 +0100 ++++ b/src/nsterm.m 2026-02-26 10:46:54.607568839 +0100 @@ -1104,6 +1104,11 @@ unblock_input (); @@ -87,7 +84,7 @@ MRC compatible. } static void -@@ -3232,6 +3237,75 @@ +@@ -3232,6 +3237,82 @@ /* Prevent the cursor from being drawn outside the text area. */ r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA)); @@ -103,13 +100,20 @@ MRC compatible. + /* Store cursor rect for accessibilityBoundsForRange: queries. */ + view->lastAccessibilityCursorRect = r; + ++ /* Find the focused virtual element — VoiceOver tracks IT, ++ not the EmacsView (AXGroup). Notifications must come from ++ the element VoiceOver is monitoring. */ ++ id axTarget = [view accessibilityFocusedUIElement]; ++ if (!axTarget) ++ axTarget = view; ++ + struct buffer *curbuf + = XBUFFER (XWINDOW (f->selected_window)->contents); + + if (curbuf && BUF_MODIFF (curbuf) != view->lastAccessibilityModiff) + { + /* Buffer content changed — typing echo. Post ValueChanged -+ with rich userInfo on the VIEW so VoiceOver can speak. ++ with rich userInfo on the FOCUSED ELEMENT. + kAXTextStateChangeTypeEdit = 1, kAXTextEditTypeTyping = 3. */ + view->lastAccessibilityModiff = BUF_MODIFF (curbuf); + @@ -133,12 +137,12 @@ MRC compatible. + @"AXTextChangeValues": @[change] + }; + NSAccessibilityPostNotificationWithUserInfo ( -+ view, NSAccessibilityValueChangedNotification, userInfo); ++ axTarget, NSAccessibilityValueChangedNotification, userInfo); + } + -+ /* Always notify cursor movement. */ ++ /* Always notify cursor movement on the focused element. */ + NSAccessibilityPostNotification ( -+ view, NSAccessibilitySelectedTextChangedNotification); ++ axTarget, NSAccessibilitySelectedTextChangedNotification); + + /* Tell macOS Zoom where the cursor is. UAZoomChangeFocus() + expects top-left origin (CG coordinate space). */ @@ -163,7 +167,7 @@ MRC compatible. ns_focus (f, NULL, 0); NSGraphicsContext *ctx = [NSGraphicsContext currentContext]; -@@ -6849,6 +6923,650 @@ +@@ -6849,6 +6930,646 @@ /* ========================================================================== @@ -772,15 +776,11 @@ MRC compatible. + ptrdiff_t pt = BUF_PT (b); + if (pt > BUF_BEGV (b)) + { -+ EmacsView *view = self.emacsView; -+ if (view) -+ { -+ NSRange charRange = NSMakeRange ( -+ (NSUInteger)(pt - BUF_BEGV (b) - 1), 1); -+ changedText = [view accessibilityStringForRange:charRange]; -+ if (!changedText) -+ changedText = @""; -+ } ++ NSRange charRange = NSMakeRange ( ++ (NSUInteger)(pt - BUF_BEGV (b) - 1), 1); ++ changedText = [self accessibilityStringForRange:charRange]; ++ if (!changedText) ++ changedText = @""; + } + + NSDictionary *change = @{ @@ -814,7 +814,7 @@ MRC compatible. EmacsView implementation ========================================================================== */ -@@ -6889,6 +7607,7 @@ +@@ -6889,6 +7610,7 @@ [layer release]; #endif @@ -822,7 +822,7 @@ MRC compatible. [[self menu] release]; [super dealloc]; } -@@ -8237,6 +8956,18 @@ +@@ -8237,6 +8959,18 @@ XSETFRAME (event.frame_or_window, emacsframe); kbd_buffer_store_event (&event); ns_send_appdefined (-1); // Kick main loop @@ -841,7 +841,7 @@ MRC compatible. } -@@ -9474,6 +10205,391 @@ +@@ -9474,6 +10208,391 @@ return fs_state; } @@ -1233,7 +1233,7 @@ MRC compatible. @end /* EmacsView */ -@@ -9941,6 +11057,14 @@ +@@ -9941,6 +11060,14 @@ return [super accessibilityAttributeValue:attribute]; }