diff --git a/patches/0002-ns-implement-buffer-accessibility-element-core-proto.patch b/patches/0002-ns-implement-buffer-accessibility-element-core-proto.patch index 6503541..0be0b10 100644 --- a/patches/0002-ns-implement-buffer-accessibility-element-core-proto.patch +++ b/patches/0002-ns-implement-buffer-accessibility-element-core-proto.patch @@ -30,7 +30,7 @@ diff --git a/src/nsterm.m b/src/nsterm.m index 852e7f9..3e1ac74 100644 --- a/src/nsterm.m +++ b/src/nsterm.m -@@ -7631,6 +7631,1129 @@ - (id)accessibilityTopLevelUIElement +@@ -7631,6 +7631,1121 @@ - (id)accessibilityTopLevelUIElement @end @@ -833,20 +833,12 @@ index 852e7f9..3e1ac74 100644 + + SET_PT_BOTH (charpos, CHAR_TO_BYTE (charpos)); + -+ /* Keep mark state aligned with requested selection range. */ -+ if (range.length > 0) -+ { -+ ptrdiff_t mark_charpos = [self charposForAccessibilityIndex: -+ range.location + range.length]; -+ if (mark_charpos > BUF_ZV (b)) -+ mark_charpos = BUF_ZV (b); -+ Fset_marker (BVAR (b, mark), make_fixnum (mark_charpos), -+ Fcurrent_buffer ()); -+ bset_mark_active (b, Qt); -+ } -+ else -+ bset_mark_active (b, Qnil); -+ ++ /* Always deactivate mark: VoiceOver range.length is an internal ++ word boundary hint, not a text selection. Activating the mark ++ makes accessibilitySelectedTextRange return a non-zero length, ++ which confuses VoiceOver into positioning its browse cursor at ++ the END of the selection instead of the start. */ ++ bset_mark_active (b, Qnil); + unbind_to (count, Qnil); + + /* Update cached state so the next notification cycle doesn't diff --git a/patches/0003-ns-add-buffer-notification-dispatch-and-mode-line-el.patch b/patches/0003-ns-add-buffer-notification-dispatch-and-mode-line-el.patch index 919818a..b2a107d 100644 --- a/patches/0003-ns-add-buffer-notification-dispatch-and-mode-line-el.patch +++ b/patches/0003-ns-add-buffer-notification-dispatch-and-mode-line-el.patch @@ -29,7 +29,7 @@ diff --git a/src/nsterm.m b/src/nsterm.m index 3e1ac74..d3015e2 100644 --- a/src/nsterm.m +++ b/src/nsterm.m -@@ -8758,6 +8758,605 @@ - (NSRect)accessibilityFrame +@@ -8758,6 +8758,609 @@ - (NSRect)accessibilityFrame @end @@ -162,8 +162,8 @@ index 3e1ac74..d3015e2 100644 + user expectation ("w" jumps to next word and reads it). */ + BOOL isWordMove + = (!markActive && !oldMarkActive -+ && granularity -+ == ns_ax_text_selection_granularity_word); ++ && granularity == ns_ax_text_selection_granularity_word ++ && direction == ns_ax_text_selection_direction_discontiguous); + if (isWordMove && cachedText) + { + NSCharacterSet *ws @@ -191,7 +191,11 @@ index 3e1ac74..d3015e2 100644 + NSString *word + = [cachedText substringWithRange: + NSMakeRange (wstart, wend - wstart)]; -+ word = [word stringByTrimmingCharactersInSet: ws]; ++ NSMutableCharacterSet *trims ++ = [ws mutableCopy]; ++ [trims formUnionWithCharacterSet: ++ [NSCharacterSet punctuationCharacterSet]]; ++ word = [word stringByTrimmingCharactersInSet:trims]; + if ([word length] > 0) + { + NSDictionary *annInfo = @{ diff --git a/patches/0008-ns-announce-child-frame-completion-candidates-for-Vo.patch b/patches/0008-ns-announce-child-frame-completion-candidates-for-Vo.patch index 49202a5..457ee5f 100644 --- a/patches/0008-ns-announce-child-frame-completion-candidates-for-Vo.patch +++ b/patches/0008-ns-announce-child-frame-completion-candidates-for-Vo.patch @@ -70,7 +70,7 @@ diff --git a/etc/NEWS b/etc/NEWS index 2b1f9e6..5766428 100644 --- a/etc/NEWS +++ b/etc/NEWS -@@ -4400,16 +4400,20 @@ allowing Emacs users access to speech recognition utilities. +@@ -4400,16 +4400,19 @@ allowing Emacs users access to speech recognition utilities. Note: Accepting this permission allows the use of system APIs, which may send user data to Apple's speech recognition servers. @@ -518,7 +518,7 @@ index 8d44b5f..29b646d 100644 NSInteger direction = ns_ax_text_selection_direction_discontiguous; if (point > oldPoint) direction = ns_ax_text_selection_direction_next; -@@ -9488,6 +9670,41 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property +@@ -9488,6 +9670,36 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property granularity = ns_ax_text_selection_granularity_line; } @@ -542,19 +542,14 @@ index 8d44b5f..29b646d 100644 + if (emacsMovedCursor && !isCtrlNP) + direction = ns_ax_text_selection_direction_discontiguous; + -+ /* Post FocusedUIElementChanged when Emacs moved the cursor -+ across a line boundary so VoiceOver re-anchors its browse -+ cursor at the new accessibilitySelectedTextRange. -+ SelectedTextChanged with discontiguous direction alone is -+ not sufficient; VoiceOver requires this notification to -+ re-query the focused element and update its internal -+ browse position. */ -+ if (emacsMovedCursor -+ && granularity -+ == ns_ax_text_selection_granularity_line -+ && [self isAccessibilityFocused]) ++ /* Post FocusedUIElementChanged on the containing NSView whenever ++ Emacs moves the cursor independently of VoiceOver. Posting on ++ the view causes VoiceOver to re-query accessibilityFocusedUIElement ++ and re-anchor its browse cursor at the new selection range, without ++ re-announcing the element name. Required for all granularities. */ ++ if (emacsMovedCursor && [self isAccessibilityFocused]) + ns_ax_post_notification ( -+ self, ++ self.emacsView, + NSAccessibilityFocusedUIElementChangedNotification); + /* Post notifications for focused and non-focused elements. */