Files
emacs-doom/patches/0000-ns-integrate-with-macOS-Zoom-for-cursor-tracking.patch
Daneel d9b4cbb87a patches: restructure per reviewer feedback
Major changes:
1. Zoom separated into standalone patch 0000
   - UAZoomChangeFocus in ns_draw_window_cursor
   - Fallback in ns_update_end for window-switch tracking
   - No overlayZoomActive (source of split/switch/move bug)

2. VoiceOver patches 0001-0008 are now Zoom-free
   - All UAZoom*, overlayZoom*, kUAZoomFocus references removed
   - lastAccessibilityCursorRect kept for VoiceOver bounds queries
   - Commit messages cleaned of Zoom references

3. README.txt and TESTING.txt rewritten for new structure

Addresses reviewer (Stéphane Marks) feedback:
- Keep Zoom patch separate from VoiceOver work
- Design discussion needed for non-Zoom patches
- Performance: ns-accessibility-enabled=nil for zero overhead
2026-02-28 22:28:35 +01:00

155 lines
5.4 KiB
Diff
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
From 402d2959cc569ebe740f07687c37283323fce314 Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 22:08:55 +0100
Subject: [PATCH] ns: integrate with macOS Zoom for cursor tracking
Inform macOS Zoom of the text cursor position so the zoomed viewport
follows keyboard focus in Emacs.
* src/nsterm.h (EmacsView): Add lastZoomCursorRect ivar.
* src/nsterm.m (ns_draw_window_cursor): Store cursor rect in
lastZoomCursorRect; call UAZoomChangeFocus with CG-space
coordinates when UAZoomEnabled returns true.
(ns_update_end): Call UAZoomChangeFocus as fallback after each
redisplay cycle to ensure Zoom tracks cursor across window
switches (C-x o) where the physical cursor may not be redrawn.
Coordinate conversion: EmacsView pixels (AppKit, flipped) ->
NSWindow -> NSScreen -> CGRect with y-flip for CoreGraphics
top-left origin. UAZoomChangeFocus is available since macOS 10.4
(ApplicationServices umbrella framework).
Tested on macOS 14 with Zoom enabled: cursor tracking works across
window splits, switches, and normal navigation.
---
etc/NEWS | 8 ++++++
src/nsterm.h | 4 +++
src/nsterm.m | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 82 insertions(+)
diff --git a/etc/NEWS b/etc/NEWS
index ef36df5..e80e124 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -82,6 +82,14 @@ other directory on your system. You can also invoke the
* Changes in Emacs 31.1
++++
+** The macOS NS port now integrates with macOS Zoom.
+When macOS Zoom is enabled (System Settings -> Accessibility -> Zoom ->
+Follow keyboard focus), Emacs informs Zoom of the text cursor position
+after every cursor redraw via 'UAZoomChangeFocus'. The zoomed viewport
+automatically tracks the insertion point across window splits and
+switches.
+
+++
** 'line-spacing' now supports specifying spacing above the line.
Previously, only spacing below the line could be specified. The user
diff --git a/src/nsterm.h b/src/nsterm.h
index 7c1ee4c..f2755e4 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -484,6 +484,10 @@ enum ns_return_frame_mode
@public
struct frame *emacsframe;
int scrollbarsNeedingUpdate;
+#ifdef NS_IMPL_COCOA
+ /* Cached cursor rect for macOS Zoom integration. */
+ NSRect lastZoomCursorRect;
+#endif
NSRect ns_userRect;
}
diff --git a/src/nsterm.m b/src/nsterm.m
index 74e4ad5..eb1649b 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1104,6 +1104,34 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
unblock_input ();
ns_updating_frame = NULL;
+
+#ifdef NS_IMPL_COCOA
+ /* Zoom fallback: ensure Zoom tracks the cursor after window
+ switches (C-x o) and other operations where the physical cursor
+ may not be redrawn but the focused window changed. This uses
+ lastZoomCursorRect which was set by ns_draw_window_cursor
+ during the current or a previous redisplay cycle. */
+#if defined (MAC_OS_X_VERSION_MIN_REQUIRED) \
+ && MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
+ if (view && UAZoomEnabled ()
+ && !NSIsEmptyRect (view->lastZoomCursorRect))
+ {
+ NSRect r = view->lastZoomCursorRect;
+ 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 */
+#endif /* NS_IMPL_COCOA */
}
static void
@@ -3232,6 +3260,48 @@ 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 macOS Zoom integration.
+ Zoom (System Settings -> Accessibility -> Zoom) tracks a focus
+ element to keep the zoomed viewport centered on the cursor.
+ UAZoomChangeFocus() informs Zoom of the cursor position after
+ every physical cursor redraw.
+
+ The coordinate conversion chain:
+ EmacsView pixels (AppKit, flipped, origin at top-left)
+ -> NSWindow coordinates (convertRect:toView:nil)
+ -> NSScreen coordinates (convertRectToScreen:)
+ -> CGRect (NSRectToCGRect, same values)
+ -> CG y-flip (CoreGraphics uses top-left origin on
+ the primary screen; AppKit uses bottom-left). */
+ {
+ EmacsView *view = FRAME_NS_VIEW (f);
+ if (view && on_p && active_p)
+ {
+ view->lastZoomCursorRect = r;
+
+#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 */
+ }
+ }
+#endif /* NS_IMPL_COCOA */
+
ns_focus (f, NULL, 0);
NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
--
2.43.0