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 dc5fb8a..1c9e4f3 100644 --- a/patches/0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch +++ b/patches/0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch @@ -1,6 +1,6 @@ -From 09f293daca75996e7348c89e2b0d31a16c1c2500 Mon Sep 17 00:00:00 2001 +From cb2c19955d681dd5fa6e865a8ddb553efa5a9ead Mon Sep 17 00:00:00 2001 From: Daneel -Date: Mon, 23 Feb 2026 10:22:47 +0100 +Date: Mon, 23 Feb 2026 10:27:44 +0100 Subject: [PATCH] ns: implement UAZoomChangeFocus + NSAccessibility for macOS Zoom @@ -8,33 +8,34 @@ Add full cursor tracking support for macOS Zoom 'Follow keyboard focus' and other assistive technology tools. ROOT CAUSE: NSAccessibility notifications alone are insufficient for -custom-drawn views. macOS Zoom requires an explicit call to -UAZoomChangeFocus() from HIServices/UniversalAccess.h. This is the -same mechanism used by iTerm2 (PTYTextView.m:refreshAccessibility) and -Chromium (render_widget_host_view_mac.mm:OnSelectionBoundsChanged). +custom-drawn views. macOS Zoom 'Follow keyboard focus' requires an +explicit call to UAZoomChangeFocus() from HIServices/UniversalAccess.h. +This is the same mechanism used by iTerm2 (PTYTextView.m:refreshAccessibility) +and Chromium (render_widget_host_view_mac.mm:OnSelectionBoundsChanged). Changes to src/nsterm.h: - Add lastAccessibilityCursorRect ivar to EmacsView Changes to src/nsterm.m: - ns_draw_window_cursor: store cursor rect, post SelectedTextChanged, - call UAZoomChangeFocus() with cursor rect in AX screen coordinates + call UAZoomChangeFocus() with cursor position in AX screen coordinates + (y-flipped: UAZoomChangeFocus expects origin at top-left of screen) - windowDidBecomeKey: post FocusedUIElementChangedNotification -- EmacsView: add accessibilityIsIgnored (NO), isAccessibilityElement (YES), - accessibilityFocusedUIElement (self), accessibilityRole (TextAreaRole), - accessibilityAttributeNames, accessibilityAttributeValue: (responds to - NSAccessibilitySelectedTextRangeAttribute with {0,0}), - accessibilityBoundsForRange: (new NSAccessibilityProtocol, macOS 10.10+), - accessibilityParameterizedAttributeNames + old AXBoundsForRange fallback +- EmacsView: accessibilityIsIgnored=NO, isAccessibilityElement=YES, + accessibilityFocusedUIElement=self, accessibilityRole=TextAreaRole, + accessibilityAttributeNames, accessibilityAttributeValue: for + NSAccessibilitySelectedTextRangeAttribute -> {0,0}, + accessibilityBoundsForRange: (new API, macOS 10.10+), + accessibilityAttributeValue:forParameter: (old AXBoundsForRange fallback) -Carbon/Carbon.h (already included in NS_IMPL_COCOA) provides UAZoomEnabled(), -UAZoomChangeFocus(), and kUAZoomFocusTypeInsertionPoint. +Carbon/Carbon.h (already included) provides UAZoomEnabled(), +UAZoomChangeFocus(), kUAZoomFocusTypeInsertionPoint. See also: https://github.com/nicowillis/Ghostty/issues/4053 --- src/nsterm.h | 3 + - src/nsterm.m | 156 +++++++++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 159 insertions(+) + src/nsterm.m | 164 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 167 insertions(+) diff --git a/src/nsterm.h b/src/nsterm.h index 7c1ee4cf535..6c1ff3434a3 100644 @@ -51,10 +52,10 @@ index 7c1ee4cf535..6c1ff3434a3 100644 /* AppKit-side interface. */ diff --git a/src/nsterm.m b/src/nsterm.m -index 932d209f56b..38d946f1d9f 100644 +index 932d209f56b..a377d70c6fb 100644 --- a/src/nsterm.m +++ b/src/nsterm.m -@@ -3232,6 +3232,40 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors. +@@ -3232,6 +3232,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)); @@ -84,8 +85,16 @@ index 932d209f56b..38d946f1d9f 100644 + { + NSRect windowRect = [view convertRect:r toView:nil]; + NSRect screenRect = [[view window] convertRectToScreen:windowRect]; -+ CGRect cgRect = [view accessibilityConvertScreenRect: -+ NSRectToCGRect (screenRect)]; ++ CGRect cgRect = NSRectToCGRect (screenRect); ++ ++ /* UAZoomChangeFocus expects coordinates with origin at the ++ top-left of the primary screen (NSAccessibility coordinate ++ space). [window convertRectToScreen:] returns Quartz screen ++ coordinates with origin at the bottom-left of the primary ++ screen, so we flip the y axis manually. */ ++ CGFloat primaryH = [[[NSScreen screens] firstObject] frame].size.height; ++ cgRect.origin.y = primaryH - cgRect.origin.y - cgRect.size.height; ++ + UAZoomChangeFocus (&cgRect, &cgRect, kUAZoomFocusTypeInsertionPoint); + } + } @@ -95,7 +104,7 @@ index 932d209f56b..38d946f1d9f 100644 ns_focus (f, NULL, 0); NSGraphicsContext *ctx = [NSGraphicsContext currentContext]; -@@ -8237,6 +8271,14 @@ - (void)windowDidBecomeKey /* for direct calls */ +@@ -8237,6 +8279,14 @@ - (void)windowDidBecomeKey /* for direct calls */ XSETFRAME (event.frame_or_window, emacsframe); kbd_buffer_store_event (&event); ns_send_appdefined (-1); // Kick main loop @@ -110,7 +119,7 @@ index 932d209f56b..38d946f1d9f 100644 } -@@ -9474,6 +9516,120 @@ - (int) fullscreenState +@@ -9474,6 +9524,120 @@ - (int) fullscreenState return fs_state; }