From 9d963a6ab1a9d1ce4dbb9edf108b2a633a31f0a6 Mon Sep 17 00:00:00 2001 From: Daneel Date: Thu, 26 Feb 2026 14:11:16 +0100 Subject: [PATCH] v15.2: fix all AXTextStateChange enum values (off-by-one from Unknown=0) Root cause: Apple's AXTextStateChange enums start with Unknown=0, shifting all named constants by +1 vs our values. WebKit source (AXObjectCacheMac.mm) confirms: - kAXTextStateChangeTypeEdit = 1 (was 0) - kAXTextStateChangeTypeSelectionMove = 2 (was 1) - kAXTextSelectionDirectionPrevious = 3 (was 2) - kAXTextSelectionDirectionNext = 4 (was 3) - kAXTextSelectionDirectionDiscontiguous = 5 (was 4) - kAXTextSelectionGranularityCharacter = 1 (was 0) - kAXTextSelectionGranularityLine = 3 (was 2) Typing echo worked by coincidence (kAXTextEditTypeTyping=3 unchanged). SelectionMove=1 mapped to Edit, so VoiceOver ignored cursor movement. Completions two-line reading is expected (columnar buffer layout). --- ...oundsForRange-for-macOS-Zoom-cursor-.patch | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 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 468de4b..6f8d678 100644 --- a/patches/0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch +++ b/patches/0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch @@ -73,7 +73,7 @@ index 7c1ee4c..4abeafe 100644 diff --git a/src/nsterm.m b/src/nsterm.m -index 932d209..e830f54 100644 +index 932d209..792f7c5 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -1104,6 +1104,11 @@ ns_update_end (struct frame *f) @@ -718,7 +718,7 @@ index 932d209..e830f54 100644 + BOOL markActive = !NILP (BVAR (b, mark_active)); + + /* --- Text changed → typing echo --- -+ kAXTextStateChangeTypeEdit = 0, kAXTextEditTypeTyping = 3. */ ++ kAXTextStateChangeTypeEdit = 1, kAXTextEditTypeTyping = 3. */ + if (modiff != self.cachedModiff) + { + /* Capture changed char before invalidating cache. */ @@ -750,7 +750,7 @@ index 932d209..e830f54 100644 + @"AXTextChangeValueLength": @([changedChar length]) + }; + NSDictionary *userInfo = @{ -+ @"AXTextStateChangeType": @0, ++ @"AXTextStateChangeType": @1, + @"AXTextChangeValues": @[change] + }; + NSAccessibilityPostNotificationWithUserInfo ( @@ -758,30 +758,30 @@ index 932d209..e830f54 100644 + } + + /* --- Cursor moved or selection changed → line reading --- -+ kAXTextStateChangeTypeSelectionMove = 1. */ ++ kAXTextStateChangeTypeSelectionMove = 2. */ + if (point != self.cachedPoint || markActive != self.cachedMarkActive) + { + ptrdiff_t oldPoint = self.cachedPoint; + self.cachedPoint = point; + self.cachedMarkActive = markActive; + -+ /* Compute direction: 2=Previous, 3=Next, 4=Discontiguous. */ -+ NSInteger direction = 4; ++ /* Compute direction: 3=Previous, 4=Next, 5=Discontiguous. */ ++ NSInteger direction = 5; + if (point > oldPoint) -+ direction = 3; ++ direction = 4; + else if (point < oldPoint) -+ direction = 2; ++ direction = 3; + + /* Compute granularity from movement distance. -+ Check if we crossed a newline → line movement (2). -+ Otherwise single char (0) or discontiguous (5=unknown). */ -+ NSInteger granularity = 5; ++ Check if we crossed a newline → line movement (3). ++ Otherwise single char (1) or unknown (0). */ ++ NSInteger granularity = 0; + [self ensureTextCache]; + if (cachedText && oldPoint > 0) + { + ptrdiff_t delta = point - oldPoint; + if (delta == 1 || delta == -1) -+ granularity = 0; /* Character. */ ++ granularity = 1; /* Character. */ + else + { + /* Check for line crossing by looking for newlines @@ -798,13 +798,13 @@ index 932d209..e830f54 100644 + options:0 + range:searchRange]; + if (nl.location != NSNotFound) -+ granularity = 2; /* Line. */ ++ granularity = 3; /* Line. */ + } + } + } + + NSDictionary *moveInfo = @{ -+ @"AXTextStateChangeType": @1, ++ @"AXTextStateChangeType": @2, + @"AXTextSelectionDirection": @(direction), + @"AXTextSelectionGranularity": @(granularity) + };