From 8ae3f0b367be737572a1f94e80cc7ea9338d59ab Mon Sep 17 00:00:00 2001 From: Daneel Date: Thu, 26 Feb 2026 17:29:33 +0100 Subject: [PATCH] patches: update VoiceOver patch (v15.9 candidate) --- ...oundsForRange-for-macOS-Zoom-cursor-.patch | 236 ++++++++++++++---- 1 file changed, 191 insertions(+), 45 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 c9fbdc7..ec2561f 100644 --- a/patches/0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch +++ b/patches/0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch @@ -1,8 +1,19 @@ -diff --git a/src/nsterm.h b/src/nsterm.h -index 7c1ee4c..2e2c80f 100644 ---- a/src/nsterm.h -+++ b/src/nsterm.h -@@ -453,6 +453,58 @@ enum ns_return_frame_mode +From 0084027a7680cbfb1449f091312ac21ed3794a6c Mon Sep 17 00:00:00 2001 +From: Daneel +Date: Thu, 26 Feb 2026 17:30:09 +0100 +Subject: [PATCH] ns: implement AXBoundsForRange and VoiceOver interaction + fixes + +--- + nsterm.h | 68 ++ + nsterm.m | 1968 +++++++++++++++++++++++++++++++++++++++++++++++++----- + 2 files changed, 1855 insertions(+), 181 deletions(-) + +diff --git a/nsterm.h b/nsterm.h +index 7c1ee4c..719eeba 100644 +--- a/nsterm.h ++++ b/nsterm.h +@@ -453,6 +453,59 @@ enum ns_return_frame_mode @end @@ -46,6 +57,7 @@ index 7c1ee4c..2e2c80f 100644 +@property (nonatomic, assign) ptrdiff_t cachedModiff; +@property (nonatomic, assign) ptrdiff_t cachedPoint; +@property (nonatomic, assign) BOOL cachedMarkActive; ++@property (nonatomic, copy) NSString *cachedCompletionAnnouncement; +- (void)invalidateTextCache; +- (void)postAccessibilityNotificationsForFrame:(struct frame *)f; +- (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx; @@ -61,7 +73,7 @@ index 7c1ee4c..2e2c80f 100644 /* ========================================================================== The main Emacs view -@@ -471,6 +523,14 @@ enum ns_return_frame_mode +@@ -471,6 +524,14 @@ enum ns_return_frame_mode #ifdef NS_IMPL_COCOA char *old_title; BOOL maximizing_resize; @@ -76,7 +88,7 @@ index 7c1ee4c..2e2c80f 100644 #endif BOOL font_panel_active; NSFont *font_panel_result; -@@ -528,6 +588,13 @@ enum ns_return_frame_mode +@@ -528,6 +589,13 @@ enum ns_return_frame_mode - (void)windowWillExitFullScreen; - (void)windowDidExitFullScreen; - (void)windowDidBecomeKey; @@ -90,10 +102,10 @@ index 7c1ee4c..2e2c80f 100644 @end -diff --git a/src/nsterm.m b/src/nsterm.m -index 932d209..416e5a4 100644 ---- a/src/nsterm.m -+++ b/src/nsterm.m +diff --git a/nsterm.m b/nsterm.m +index 932d209..336150a 100644 +--- a/nsterm.m ++++ b/nsterm.m @@ -1104,6 +1104,11 @@ ns_update_end (struct frame *f) unblock_input (); @@ -144,7 +156,7 @@ index 932d209..416e5a4 100644 ns_focus (f, NULL, 0); NSGraphicsContext *ctx = [NSGraphicsContext currentContext]; -@@ -6849,261 +6885,1380 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action) +@@ -6849,261 +6885,1511 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action) /* ========================================================================== @@ -568,10 +580,12 @@ index 932d209..416e5a4 100644 +@synthesize cachedModiff; +@synthesize cachedPoint; +@synthesize cachedMarkActive; ++@synthesize cachedCompletionAnnouncement; +- (void)dealloc +{ + [cachedText release]; ++ [cachedCompletionAnnouncement release]; + if (visibleRuns) + xfree (visibleRuns); + [super dealloc]; @@ -1199,8 +1213,8 @@ index 932d209..416e5a4 100644 + direction = 3; + + /* Compute granularity from movement distance. -+ Check if we crossed a newline → line movement (3). -+ Otherwise single char (1) or unknown (0). */ ++ Prefer robust line-range comparison for vertical movement, ++ otherwise single char (1) or unknown (0). */ + NSInteger granularity = 0; + [self ensureTextCache]; + if (cachedText && oldPoint > 0) @@ -1210,22 +1224,21 @@ index 932d209..416e5a4 100644 + granularity = 1; /* Character. */ + else + { -+ /* Check for line crossing by looking for newlines -+ between old and new position. */ -+ NSUInteger lo = [self accessibilityIndexForCharpos: -+ MIN (oldPoint, point)]; -+ NSUInteger hi = [self accessibilityIndexForCharpos: -+ MAX (oldPoint, point)]; + NSUInteger tlen = [cachedText length]; -+ if (lo < tlen && hi <= tlen) -+ { -+ NSRange searchRange = NSMakeRange (lo, hi - lo); -+ NSRange nl = [cachedText rangeOfString:@"\n" -+ options:0 -+ range:searchRange]; -+ if (nl.location != NSNotFound) -+ granularity = 3; /* Line. */ -+ } ++ NSUInteger oldIdx = [self accessibilityIndexForCharpos:oldPoint]; ++ NSUInteger newIdx = [self accessibilityIndexForCharpos:point]; ++ if (oldIdx > tlen) ++ oldIdx = tlen; ++ if (newIdx > tlen) ++ newIdx = tlen; ++ ++ NSRange oldLine = [cachedText lineRangeForRange: ++ NSMakeRange (oldIdx, 0)]; ++ NSRange newLine = [cachedText lineRangeForRange: ++ NSMakeRange (newIdx, 0)]; ++ if (oldLine.location != newLine.location) ++ granularity = 3; /* Line. */ ++ + } + } + @@ -1307,19 +1320,19 @@ index 932d209..416e5a4 100644 + } + } + -+ /* 3) Fallback: check completions-highlight overlay span. */ ++ /* 3) Fallback: check completions-highlight overlay span at point. */ + if (!announceText) + { ++ Lisp_Object faceSym = intern ("completions-highlight"); + Lisp_Object overlays = Foverlays_at (make_fixnum (point), Qnil); + Lisp_Object tail; + for (tail = overlays; CONSP (tail); tail = XCDR (tail)) + { + Lisp_Object ov = XCAR (tail); + Lisp_Object face = Foverlay_get (ov, Qface); -+ if (EQ (face, intern ("completions-highlight")) ++ if (EQ (face, faceSym) + || (CONSP (face) -+ && !NILP (Fmemq (intern ("completions-highlight"), -+ face)))) ++ && !NILP (Fmemq (faceSym, face)))) + { + ptrdiff_t ov_start = OVERLAY_START (ov); + ptrdiff_t ov_end = OVERLAY_END (ov); @@ -1338,6 +1351,47 @@ index 932d209..416e5a4 100644 + } + } + ++ /* 4) Fallback: scan for completions-highlight anywhere in buffer. ++ TAB cycling can move highlight without moving point. */ ++ if (!announceText) ++ { ++ Lisp_Object faceSym = intern ("completions-highlight"); ++ ptrdiff_t begv2 = BUF_BEGV (b); ++ ptrdiff_t zv2 = BUF_ZV (b); ++ ptrdiff_t scanPos; ++ BOOL found = NO; ++ ++ for (scanPos = begv2; scanPos < zv2 && !found; scanPos++) ++ { ++ Lisp_Object overlays = Foverlays_at (make_fixnum (scanPos), Qnil); ++ Lisp_Object tail; ++ for (tail = overlays; CONSP (tail); tail = XCDR (tail)) ++ { ++ Lisp_Object ov = XCAR (tail); ++ Lisp_Object face = Foverlay_get (ov, Qface); ++ if (EQ (face, faceSym) ++ || (CONSP (face) ++ && !NILP (Fmemq (faceSym, face)))) ++ { ++ ptrdiff_t ov_start = OVERLAY_START (ov); ++ ptrdiff_t ov_end = OVERLAY_END (ov); ++ if (ov_end > ov_start) ++ { ++ NSUInteger ax_s = [self accessibilityIndexForCharpos: ++ ov_start]; ++ NSUInteger ax_e = [self accessibilityIndexForCharpos: ++ ov_end]; ++ if (ax_e > ax_s && ax_e <= [cachedText length]) ++ announceText = [cachedText substringWithRange: ++ NSMakeRange (ax_s, ax_e - ax_s)]; ++ } ++ found = YES; ++ break; ++ } ++ } ++ } ++ } ++ + if (b != oldb2) + set_buffer_internal_1 (oldb2); + @@ -1364,20 +1418,109 @@ index 932d209..416e5a4 100644 + [NSCharacterSet whitespaceAndNewlineCharacterSet]]; + if ([announceText length] > 0) + { -+ NSDictionary *annInfo = @{ -+ NSAccessibilityAnnouncementKey: announceText, -+ NSAccessibilityPriorityKey: -+ @(NSAccessibilityPriorityHigh) -+ }; -+ NSAccessibilityPostNotificationWithUserInfo ( -+ NSApp, -+ NSAccessibilityAnnouncementRequestedNotification, -+ annInfo); ++ if (![announceText isEqualToString:self.cachedCompletionAnnouncement]) ++ { ++ NSDictionary *annInfo = @{ ++ NSAccessibilityAnnouncementKey: announceText, ++ NSAccessibilityPriorityKey: ++ @(NSAccessibilityPriorityHigh) ++ }; ++ NSAccessibilityPostNotificationWithUserInfo ( ++ NSApp, ++ NSAccessibilityAnnouncementRequestedNotification, ++ annInfo); ++ } ++ self.cachedCompletionAnnouncement = announceText; + } ++ else ++ self.cachedCompletionAnnouncement = nil; + } ++ else ++ self.cachedCompletionAnnouncement = nil; + } + + } ++ else ++ { ++ if ([self isAccessibilityFocused]) ++ self.cachedCompletionAnnouncement = nil; ++ else ++ { ++ [self ensureTextCache]; ++ if (cachedText) ++ { ++ NSString *announceText = nil; ++ struct buffer *oldb2 = current_buffer; ++ if (b != current_buffer) ++ set_buffer_internal_1 (b); ++ ++ Lisp_Object faceSym = intern ("completions-highlight"); ++ ptrdiff_t begv2 = BUF_BEGV (b); ++ ptrdiff_t zv2 = BUF_ZV (b); ++ ptrdiff_t scanPos; ++ BOOL found = NO; ++ ++ for (scanPos = begv2; scanPos < zv2 && !found; scanPos++) ++ { ++ Lisp_Object overlays = Foverlays_at (make_fixnum (scanPos), Qnil); ++ Lisp_Object tail; ++ for (tail = overlays; CONSP (tail); tail = XCDR (tail)) ++ { ++ Lisp_Object ov = XCAR (tail); ++ Lisp_Object face = Foverlay_get (ov, Qface); ++ if (EQ (face, faceSym) ++ || (CONSP (face) ++ && !NILP (Fmemq (faceSym, face)))) ++ { ++ ptrdiff_t ov_start = OVERLAY_START (ov); ++ ptrdiff_t ov_end = OVERLAY_END (ov); ++ if (ov_end > ov_start) ++ { ++ NSUInteger ax_s = [self accessibilityIndexForCharpos: ++ ov_start]; ++ NSUInteger ax_e = [self accessibilityIndexForCharpos: ++ ov_end]; ++ if (ax_e > ax_s && ax_e <= [cachedText length]) ++ announceText = [cachedText substringWithRange: ++ NSMakeRange (ax_s, ax_e - ax_s)]; ++ } ++ found = YES; ++ break; ++ } ++ } ++ } ++ ++ if (b != oldb2) ++ set_buffer_internal_1 (oldb2); ++ ++ if (announceText) ++ { ++ announceText = [announceText stringByTrimmingCharactersInSet: ++ [NSCharacterSet whitespaceAndNewlineCharacterSet]]; ++ if ([announceText length] > 0) ++ { ++ if (![announceText isEqualToString:self.cachedCompletionAnnouncement]) ++ { ++ NSDictionary *annInfo = @{ ++ NSAccessibilityAnnouncementKey: announceText, ++ NSAccessibilityPriorityKey: ++ @(NSAccessibilityPriorityHigh) ++ }; ++ NSAccessibilityPostNotificationWithUserInfo ( ++ NSApp, ++ NSAccessibilityAnnouncementRequestedNotification, ++ annInfo); ++ } ++ self.cachedCompletionAnnouncement = announceText; ++ } ++ else ++ self.cachedCompletionAnnouncement = nil; ++ } ++ else ++ self.cachedCompletionAnnouncement = nil; ++ } ++ } ++ } +} + +@end @@ -1706,7 +1849,7 @@ index 932d209..416e5a4 100644 { /* FIXME: What should happen for key sequences with more than one character? */ -@@ -8237,6 +9392,27 @@ ns_in_echo_area (void) +@@ -8237,6 +9523,27 @@ ns_in_echo_area (void) XSETFRAME (event.frame_or_window, emacsframe); kbd_buffer_store_event (&event); ns_send_appdefined (-1); // Kick main loop @@ -1734,7 +1877,7 @@ index 932d209..416e5a4 100644 } -@@ -9474,6 +10650,297 @@ ns_in_echo_area (void) +@@ -9474,6 +10781,297 @@ ns_in_echo_area (void) return fs_state; } @@ -2032,7 +2175,7 @@ index 932d209..416e5a4 100644 @end /* EmacsView */ -@@ -9941,6 +11408,14 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c) +@@ -9941,6 +11539,14 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c) return [super accessibilityAttributeValue:attribute]; } @@ -2047,3 +2190,6 @@ index 932d209..416e5a4 100644 #endif /* NS_IMPL_COCOA */ /* Constrain size and placement of a frame. +-- +2.43.0 +