Merge remote-tracking branch 'refs/remotes/origin/master'

This commit is contained in:
Martin Sukany
2026-02-26 13:51:11 +01:00

View File

@@ -1,5 +1,5 @@
diff --git a/src/nsterm.h b/src/nsterm.h
index 7c1ee4c..8bf21f6 100644
index 7c1ee4c..4abeafe 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -453,6 +453,40 @@ enum ns_return_frame_mode
@@ -43,20 +43,22 @@ index 7c1ee4c..8bf21f6 100644
/* ==========================================================================
The main Emacs view
@@ -471,6 +505,12 @@ enum ns_return_frame_mode
@@ -471,6 +505,14 @@ enum ns_return_frame_mode
#ifdef NS_IMPL_COCOA
char *old_title;
BOOL maximizing_resize;
+ NSMutableArray *accessibilityElements;
+ Lisp_Object lastSelectedWindow;
+ Lisp_Object lastRootWindow;
+ BOOL accessibilityTreeValid;
+ BOOL accessibilityUpdating;
+ @public
+ NSRect lastAccessibilityCursorRect;
+ @protected
#endif
BOOL font_panel_active;
NSFont *font_panel_result;
@@ -528,6 +568,13 @@ enum ns_return_frame_mode
@@ -528,6 +570,13 @@ enum ns_return_frame_mode
- (void)windowWillExitFullScreen;
- (void)windowDidExitFullScreen;
- (void)windowDidBecomeKey;
@@ -71,7 +73,7 @@ index 7c1ee4c..8bf21f6 100644
diff --git a/src/nsterm.m b/src/nsterm.m
index 932d209..6543e3b 100644
index 932d209..e830f54 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1104,6 +1104,11 @@ ns_update_end (struct frame *f)
@@ -124,7 +126,7 @@ index 932d209..6543e3b 100644
ns_focus (f, NULL, 0);
NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
@@ -6847,6 +6883,717 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action)
@@ -6847,6 +6883,756 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action)
}
#endif
@@ -759,13 +761,52 @@ index 932d209..6543e3b 100644
+ kAXTextStateChangeTypeSelectionMove = 1. */
+ 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;
+ if (point > oldPoint)
+ direction = 3;
+ else if (point < oldPoint)
+ direction = 2;
+
+ /* 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;
+ [self ensureTextCache];
+ if (cachedText && oldPoint > 0)
+ {
+ ptrdiff_t delta = point - oldPoint;
+ if (delta == 1 || delta == -1)
+ granularity = 0; /* Character. */
+ else
+ {
+ /* Check for line crossing by looking for newlines
+ between old and new position. */
+ NSUInteger lo = (NSUInteger)
+ (MIN (oldPoint, point) - cachedTextStart);
+ NSUInteger hi = (NSUInteger)
+ (MAX (oldPoint, point) - cachedTextStart);
+ 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 = 2; /* Line. */
+ }
+ }
+ }
+
+ NSDictionary *moveInfo = @{
+ @"AXTextStateChangeType": @1,
+ @"AXTextSelectionDirection": @4,
+ @"AXTextSelectionGranularity": @3
+ @"AXTextSelectionDirection": @(direction),
+ @"AXTextSelectionGranularity": @(granularity)
+ };
+ NSAccessibilityPostNotificationWithUserInfo (
+ self,
@@ -842,7 +883,7 @@ index 932d209..6543e3b 100644
/* ==========================================================================
EmacsView implementation
@@ -6889,6 +7636,7 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action)
@@ -6889,6 +7675,7 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action)
[layer release];
#endif
@@ -850,7 +891,7 @@ index 932d209..6543e3b 100644
[[self menu] release];
[super dealloc];
}
@@ -8237,6 +8985,18 @@ ns_in_echo_area (void)
@@ -8237,6 +9024,18 @@ ns_in_echo_area (void)
XSETFRAME (event.frame_or_window, emacsframe);
kbd_buffer_store_event (&event);
ns_send_appdefined (-1); // Kick main loop
@@ -869,7 +910,7 @@ index 932d209..6543e3b 100644
}
@@ -9474,6 +10234,259 @@ ns_in_echo_area (void)
@@ -9474,6 +10273,290 @@ ns_in_echo_area (void)
return fs_state;
}
@@ -1028,37 +1069,68 @@ index 932d209..6543e3b 100644
+ if (!emacsframe)
+ return;
+
+ /* Re-entrance guard: VoiceOver callbacks during notification posting
+ can trigger redisplay, which calls ns_update_end, which calls us
+ again. Prevent infinite recursion. */
+ if (accessibilityUpdating)
+ return;
+ accessibilityUpdating = YES;
+
+ /* Detect window tree change (split, delete, new buffer). Compare
+ FRAME_ROOT_WINDOW — if it changed, the tree structure changed. */
+ Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe);
+ if (!EQ (curRoot, lastRootWindow))
+ {
+ lastRootWindow = curRoot;
+ accessibilityTreeValid = NO;
+ }
+
+ /* If tree is stale, rebuild FIRST so we don't iterate freed
+ window pointers. Skip notifications for this cycle — the
+ freshly-built elements have no previous state to diff against. */
+ if (!accessibilityTreeValid)
+ {
+ [self rebuildAccessibilityTree];
+
+ /* Post focus change so VoiceOver picks up the new tree. */
+ id focused = [self accessibilityFocusedUIElement];
+ if (focused && focused != self)
+ NSAccessibilityPostNotification (focused,
+ NSAccessibilityFocusedUIElementChangedNotification);
+
+ lastSelectedWindow = emacsframe->selected_window;
+ accessibilityUpdating = NO;
+ return;
+ }
+
+ /* Post per-buffer notifications using EXISTING elements that have
+ cached state from the previous cycle. */
+ cached state from the previous cycle. Validate each window
+ pointer before use. */
+ for (EmacsAccessibilityElement *elem in accessibilityElements)
+ {
+ if ([elem isKindOfClass:[EmacsAccessibilityBuffer class]])
+ {
+ struct window *w = elem.emacsWindow;
+ if (w && WINDOW_LEAF_P (w))
+ if (w && WINDOW_LEAF_P (w)
+ && BUFFERP (w->contents) && XBUFFER (w->contents))
+ [(EmacsAccessibilityBuffer *) elem
+ postAccessibilityNotificationsForFrame:emacsframe];
+ }
+ }
+
+ /* Check for window switch (C-x o) before rebuild. */
+ /* Check for window switch (C-x o). */
+ Lisp_Object curSel = emacsframe->selected_window;
+ BOOL windowSwitched = !EQ (curSel, lastSelectedWindow);
+ if (windowSwitched)
+ lastSelectedWindow = curSel;
+
+ /* Rebuild tree only if window configuration changed. */
+ if (!accessibilityTreeValid)
+ [self rebuildAccessibilityTree];
+
+ /* Post focus change AFTER rebuild so the new element exists. */
+ if (windowSwitched)
+ {
+ lastSelectedWindow = curSel;
+ id focused = [self accessibilityFocusedUIElement];
+ if (focused && focused != self)
+ NSAccessibilityPostNotification (focused,
+ NSAccessibilityFocusedUIElementChangedNotification);
+ }
+
+ accessibilityUpdating = NO;
+}
+
+/* ---- Cursor position for Zoom (via accessibilityBoundsForRange:) ----
@@ -1129,7 +1201,7 @@ index 932d209..6543e3b 100644
@end /* EmacsView */
@@ -9941,6 +10954,14 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
@@ -9941,6 +11024,14 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
return [super accessibilityAttributeValue:attribute];
}