patches: fix all review blockers (iteration 2)

Fixes from Opus maintainer review:
1. [BLOCKER] Zoom code completely removed from ALL intermediate patches
   (0005-0007 no longer have UAZoom/overlayZoom at any commit point)
2. [BLOCKER] Unified cursor rect ivar: lastCursorRect (was split
   between lastZoomCursorRect and lastAccessibilityCursorRect)
3. [HIGH] Child frame static vars moved to EmacsView ivars
   (childFrameLastCandidate/Buffer/Modiff — no cross-frame interference)
4. [HIGH] intern_c_string replaced with Qbefore_string/Qafter_string
5. [MEDIUM] Zoom fallback gated by zoomCursorUpdated flag (no double call)
This commit is contained in:
2026-02-28 22:39:57 +01:00
parent d9b4cbb87a
commit 9d2b1da729
9 changed files with 135 additions and 317 deletions

View File

@@ -1,4 +1,4 @@
From 3304705947559e6ba83e462aed5a17f1c195c641 Mon Sep 17 00:00:00 2001
From 9e7fa018ef779610b2fb54c1ff951d0bf6bf7652 Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 12:58:11 +0100
Subject: [PATCH 5/8] ns: integrate accessibility with EmacsView and redisplay
@@ -23,8 +23,9 @@ block cursor, org-mode folded headings, indirect buffers.
Known limitations documented in patch 6 Texinfo node.
---
etc/NEWS | 13 ++
src/nsterm.m | 398 ++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 408 insertions(+), 3 deletions(-)
src/nsterm.h | 2 +-
src/nsterm.m | 373 ++++++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 384 insertions(+), 4 deletions(-)
diff --git a/etc/NEWS b/etc/NEWS
index ef36df5..e76ee93 100644
@@ -50,8 +51,21 @@ index ef36df5..e76ee93 100644
---
** Re-introduced dictation, lost in Emacs v30 (macOS).
We lost macOS dictation in v30 when migrating to NSTextInputClient.
diff --git a/src/nsterm.h b/src/nsterm.h
index 5298386..ec7b587 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -594,7 +594,7 @@ typedef NS_ENUM (NSInteger, EmacsAXSpanType)
BOOL accessibilityTreeValid;
BOOL accessibilityUpdating;
@public /* Accessed by ns_draw_phys_cursor (C function). */
- NSRect lastAccessibilityCursorRect;
+ NSRect lastCursorRect;
#endif
BOOL font_panel_active;
NSFont *font_panel_result;
diff --git a/src/nsterm.m b/src/nsterm.m
index c852929..b3bef4b 100644
index c852929..f0e8751 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1105,6 +1105,11 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
@@ -66,51 +80,26 @@ index c852929..b3bef4b 100644
}
static void
@@ -3233,6 +3238,43 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
@@ -3233,6 +3238,18 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
/* Prevent the cursor from being drawn outside the text area. */
r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA));
+#ifdef NS_IMPL_COCOA
+ /* Accessibility: store cursor rect for Zoom and bounds queries.
+ Skipped when ns-accessibility-enabled is nil to avoid overhead.
+ VoiceOver notifications are handled solely by
+ postAccessibilityUpdates (called from ns_update_end)
+ to avoid duplicate notifications and mid-redisplay fragility. */
+ /* Accessibility: store cursor rect for VoiceOver bounds queries.
+ accessibilityBoundsForRange: / accessibilityFrameForRange:
+ use this as a fallback when no valid window/glyph data is
+ available. Skipped when ns-accessibility-enabled is nil. */
+ {
+ EmacsView *view = FRAME_NS_VIEW (f);
+ if (view && on_p && active_p && ns_accessibility_enabled)
+ {
+ view->lastAccessibilityCursorRect = r;
+
+ /* Tell macOS Zoom where the cursor is. UAZoomChangeFocus()
+ expects top-left origin (CG coordinate space).
+ These APIs are available since macOS 10.4 (Universal Access
+ framework, linked via ApplicationServices umbrella). */
+#if defined (MAC_OS_X_VERSION_MIN_REQUIRED) \
+ && MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
+ if (UAZoomEnabled ())
+ {
+ NSRect windowRect = [view convertRect:r toView:nil];
+ NSRect screenRect = [[view window] convertRectToScreen:windowRect];
+ CGRect cgRect = NSRectToCGRect (screenRect);
+
+ CGFloat primaryH
+ = [[[NSScreen screens] firstObject] frame].size.height;
+ cgRect.origin.y
+ = primaryH - cgRect.origin.y - cgRect.size.height;
+
+ UAZoomChangeFocus (&cgRect, &cgRect,
+ kUAZoomFocusTypeInsertionPoint);
+ }
+#endif /* MAC_OS_X_VERSION_MIN_REQUIRED >= 101000 */
+ }
+ view->lastCursorRect = r;
+ }
+#endif
+
ns_focus (f, NULL, 0);
NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
@@ -7281,7 +7323,6 @@ - (id)accessibilityTopLevelUIElement
@@ -7281,7 +7298,6 @@ - (id)accessibilityTopLevelUIElement
@@ -118,7 +107,7 @@ index c852929..b3bef4b 100644
static BOOL
ns_ax_find_completion_overlay_range (struct buffer *b, ptrdiff_t point,
ptrdiff_t *out_start,
@@ -8375,7 +8416,6 @@ - (NSRect)accessibilityFrame
@@ -8375,7 +8391,6 @@ - (NSRect)accessibilityFrame
@end
@@ -126,7 +115,7 @@ index c852929..b3bef4b 100644
/* ===================================================================
EmacsAccessibilityBuffer (Notifications) — AX event dispatch
@@ -8920,7 +8960,6 @@ - (NSRect)accessibilityFrame
@@ -8920,7 +8935,6 @@ - (NSRect)accessibilityFrame
@end
@@ -134,7 +123,7 @@ index c852929..b3bef4b 100644
/* ===================================================================
EmacsAccessibilityInteractiveSpan — helpers and implementation
=================================================================== */
@@ -9250,6 +9289,7 @@ - (void)dealloc
@@ -9250,6 +9264,7 @@ - (void)dealloc
[layer release];
#endif
@@ -142,7 +131,7 @@ index c852929..b3bef4b 100644
[[self menu] release];
[super dealloc];
}
@@ -10598,6 +10638,32 @@ - (void)windowDidBecomeKey /* for direct calls */
@@ -10598,6 +10613,32 @@ - (void)windowDidBecomeKey /* for direct calls */
XSETFRAME (event.frame_or_window, emacsframe);
kbd_buffer_store_event (&event);
ns_send_appdefined (-1); // Kick main loop
@@ -175,7 +164,7 @@ index c852929..b3bef4b 100644
}
@@ -11835,6 +11901,332 @@ - (int) fullscreenState
@@ -11835,6 +11876,332 @@ - (int) fullscreenState
return fs_state;
}
@@ -436,7 +425,7 @@ index c852929..b3bef4b 100644
+ return bufRect;
+ }
+
+ NSRect viewRect = lastAccessibilityCursorRect;
+ NSRect viewRect = lastCursorRect;
+
+ if (viewRect.size.width < 1)
+ viewRect.size.width = 1;