Files
emacs-doom/patches/0000-ns-integrate-with-macOS-Zoom-for-cursor-tracking.patch
Daneel 74fcee0820 patches: regenerate for combined application (Zoom + VoiceOver)
VoiceOver patches 0001-0008 now apply cleanly on top of Zoom patch
0000.  The full set (git am patches/000*.patch) works without
conflicts.  Patch 0005 (integration) merges Zoom fallback and
VoiceOver postAccessibilityUpdates in ns_update_end.
2026-03-01 03:02:46 +01:00

155 lines
5.2 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 1c0ca763f978e02dd7b3968c3515dd7b41136f8f Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 22:39:35 +0100
Subject: [PATCH 1/9] 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 lastCursorRect, zoomCursorUpdated.
* src/nsterm.m (ns_draw_window_cursor): Store cursor rect in
lastCursorRect; call UAZoomChangeFocus with CG-space coordinates
when UAZoomEnabled returns true. Set zoomCursorUpdated flag.
(ns_update_end): Call UAZoomChangeFocus as fallback when cursor
was not physically redrawn in this cycle (e.g., after C-x o window
switch). Gated by zoomCursorUpdated to avoid double calls.
Coordinate conversion: EmacsView pixels (AppKit, flipped) ->
NSWindow -> NSScreen -> CGRect with y-flip for CoreGraphics
top-left origin. UAZoomEnabled returns false when Zoom is inactive,
so overhead is a single function call per redisplay cycle.
Tested on macOS 14 with Zoom enabled: cursor tracking works across
window splits, switches (C-x o), and normal navigation.
---
etc/NEWS | 8 +++++++
src/nsterm.h | 6 +++++
src/nsterm.m | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 82 insertions(+)
diff --git a/etc/NEWS b/etc/NEWS
index ef36df5..f10d17e 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..ea6e7ba 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -484,6 +484,12 @@ enum ns_return_frame_mode
@public
struct frame *emacsframe;
int scrollbarsNeedingUpdate;
+#ifdef NS_IMPL_COCOA
+ /* Cached cursor rect for macOS Zoom integration. Set by
+ ns_draw_window_cursor, used by ns_update_end fallback. */
+ NSRect lastCursorRect;
+ BOOL zoomCursorUpdated;
+#endif
NSRect ns_userRect;
}
diff --git a/src/nsterm.m b/src/nsterm.m
index 74e4ad5..cd721c8 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1104,6 +1104,35 @@ 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) where the physical cursor may not be redrawn.
+ Only fires when ns_draw_window_cursor did NOT run in this cycle
+ (zoomCursorUpdated is NO). */
+#if defined (MAC_OS_X_VERSION_MIN_REQUIRED) \
+ && MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
+ if (view && !view->zoomCursorUpdated && UAZoomEnabled ()
+ && !NSIsEmptyRect (view->lastCursorRect))
+ {
+ NSRect r = view->lastCursorRect;
+ 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);
+ }
+ if (view)
+ view->zoomCursorUpdated = NO;
+#endif
+#endif /* NS_IMPL_COCOA */
}
static void
@@ -3232,6 +3261,45 @@ 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
+ /* Zoom integration: inform macOS Zoom of the cursor position.
+ Zoom (System Settings -> Accessibility -> Zoom) tracks a focus
+ element to keep the zoomed viewport centered on the cursor.
+
+ Coordinate conversion:
+ EmacsView pixels (AppKit, flipped, top-left origin)
+ -> NSWindow (convertRect:toView:nil)
+ -> NSScreen (convertRectToScreen:)
+ -> CGRect with y-flip for CoreGraphics top-left origin. */
+ {
+ EmacsView *view = FRAME_NS_VIEW (f);
+ if (view && on_p && active_p)
+ {
+ view->lastCursorRect = r;
+ view->zoomCursorUpdated = YES;
+
+#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
+ }
+ }
+#endif /* NS_IMPL_COCOA */
+
ns_focus (f, NULL, 0);
NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
--
2.43.0