patches: address all maintainer review issues
- Issue 1: Add explicit ApplicationServices import for UAZoomEnabled/ UAZoomChangeFocus (was implicit via Carbon.h, now explicit) - Issue 2: Rename FOR_EACH_FRAME variable 'frames' -> 'frame' (plural was misleading; matches Emacs convention) - Issue 3: Move unblock_input before ObjC calls in postCompletionAnnouncementForBuffer: to avoid holding block_input during @synchronized operations - Issue 4: Fix DEFVAR_BOOL doc and Texinfo: initial value is nil, not t; auto-detection sets it at startup - Issue 5: Replace magic 10000 with NS_AX_MAX_COMPLETION_BUFFER_CHARS constant with explanatory comment - Issue 6: Add comment to lineStartOffsets loop explaining it is gated on BUF_CHARS_MODIFF and never runs on the hot path - Issue 8: Rewrite all 9 commit messages to GNU ChangeLog format with '* file (symbol): description' entries - Issue 9: Break 81-char @interface line in nsterm.h - Issue 10: Add WINDOWP/BUFFERP guards before dereferencing cf->selected_window and cw->contents in ns_zoom_find_child_frame_candidate - Issue 11: Fix @pxref -> @xref at sentence start in macos.texi
This commit is contained in:
@@ -1,45 +1,35 @@
|
||||
From 03a3e77f9ff5f46429964863a2f320e119c0686c Mon Sep 17 00:00:00 2001
|
||||
From 5538b9a843f1c56607235fe399562d48541ca4e8 Mon Sep 17 00:00:00 2001
|
||||
From: Martin Sukany <martin@sukany.cz>
|
||||
Date: Sat, 28 Feb 2026 22:39:35 +0100
|
||||
Subject: [PATCH 0/8] 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.
|
||||
follows keyboard focus in Emacs. Also track completion candidates so
|
||||
Zoom follows the selected item (Vertico, Corfu, etc.) during completion.
|
||||
|
||||
Basic cursor tracking:
|
||||
* etc/NEWS: Document Zoom integration.
|
||||
* 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 (e.g. after C-x o window switch).
|
||||
Gated by zoomCursorUpdated to avoid double calls.
|
||||
|
||||
Completion candidate tracking:
|
||||
* src/nsterm.m (ns_zoom_face_is_selected): New predicate.
|
||||
Match 'current', 'selected', and 'selection' in face symbol
|
||||
names to identify the highlighted completion candidate.
|
||||
(ns_zoom_find_overlay_candidate_line): Scan overlay
|
||||
before-string/after-string for the selected candidate line.
|
||||
Handles Vertico, Icomplete, Ivy, and similar overlay frameworks.
|
||||
(ns_zoom_find_child_frame_candidate): Scan child frame buffer
|
||||
text for the selected candidate. Handles Corfu, Company-box,
|
||||
and similar child frame frameworks.
|
||||
(ns_zoom_track_completion): Called from ns_update_end after
|
||||
cursor tracking. Overrides Zoom focus to the selected
|
||||
completion candidate when one is found.
|
||||
|
||||
Coordinate conversion: EmacsView pixels (AppKit, flipped) ->
|
||||
NSWindow -> NSScreen -> CGRect with y-flip for CoreGraphics
|
||||
top-left origin.
|
||||
|
||||
Tested on macOS 14 with Zoom enabled: cursor tracking works across
|
||||
window splits, switches (C-x o), and completion frameworks.
|
||||
* src/nsterm.m: Include ApplicationServices for UAZoomEnabled and
|
||||
UAZoomChangeFocus (UniversalAccess sub-framework).
|
||||
[NS_IMPL_COCOA]: Define NS_AX_MAX_COMPLETION_BUFFER_CHARS.
|
||||
(ns_zoom_enabled_p): New static function; caches UAZoomEnabled with
|
||||
1-second TTL to avoid per-frame Mach IPC overhead.
|
||||
(ns_zoom_face_is_selected): New static predicate; matches 'current',
|
||||
'selected', 'selection' in face symbol names.
|
||||
(ns_zoom_find_overlay_candidate_line): New static function; scans
|
||||
minibuffer overlays for the selected completion candidate line.
|
||||
(ns_zoom_find_child_frame_candidate): New static function; scans
|
||||
child frame buffers for a selected candidate; guards against partially
|
||||
initialized frames with WINDOWP and BUFFERP checks.
|
||||
(ns_zoom_track_completion): New static function; overrides Zoom focus
|
||||
to the selected completion candidate after normal cursor tracking.
|
||||
(ns_update_end): Call ns_zoom_track_completion.
|
||||
(ns_draw_window_cursor): Store cursor rect; call UAZoomChangeFocus.
|
||||
---
|
||||
etc/NEWS | 11 ++
|
||||
src/nsterm.h | 6 +
|
||||
src/nsterm.m | 336 +++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 353 insertions(+)
|
||||
src/nsterm.m | 353 +++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 370 insertions(+)
|
||||
|
||||
diff --git a/etc/NEWS b/etc/NEWS
|
||||
index ef36df5..80661a9 100644
|
||||
@@ -81,10 +71,22 @@ index 7c1ee4c..ea6e7ba 100644
|
||||
}
|
||||
|
||||
diff --git a/src/nsterm.m b/src/nsterm.m
|
||||
index 74e4ad5..5498d7a 100644
|
||||
index 74e4ad5..fc75910 100644
|
||||
--- a/src/nsterm.m
|
||||
+++ b/src/nsterm.m
|
||||
@@ -1081,6 +1081,268 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
|
||||
@@ -71,6 +71,11 @@ Updated by Christian Limpach (chris@nice.ch)
|
||||
#include "macfont.h"
|
||||
#include <Carbon/Carbon.h>
|
||||
#include <IOSurface/IOSurface.h>
|
||||
+/* ApplicationServices provides UAZoomEnabled and UAZoomChangeFocus
|
||||
+ (UniversalAccess sub-framework). Carbon.h already pulls in
|
||||
+ ApplicationServices on most SDK versions, but the explicit import
|
||||
+ makes the dependency visible and guards against SDK changes. */
|
||||
+#import <ApplicationServices/ApplicationServices.h>
|
||||
#endif
|
||||
|
||||
static EmacsMenu *dockMenu;
|
||||
@@ -1081,6 +1086,280 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
|
||||
}
|
||||
|
||||
|
||||
@@ -93,6 +95,12 @@ index 74e4ad5..5498d7a 100644
|
||||
+#if defined (MAC_OS_X_VERSION_MIN_REQUIRED) \
|
||||
+ && MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
|
||||
+
|
||||
+/* Maximum buffer size (in characters) for a window that we consider
|
||||
+ a candidate for a completion popup. Completion popups are small;
|
||||
+ if the buffer is larger than this, it is not a popup and we skip it
|
||||
+ to avoid O(buffer-size) work per redisplay cycle. */
|
||||
+#define NS_AX_MAX_COMPLETION_BUFFER_CHARS 10000
|
||||
+
|
||||
+/* Cached wrapper around ns_zoom_enabled_p ().
|
||||
+ ns_zoom_enabled_p () performs a synchronous Mach IPC roundtrip to the
|
||||
+ macOS Accessibility server (~50-200 µs per call). With call sites
|
||||
@@ -209,19 +217,25 @@ index 74e4ad5..5498d7a 100644
|
||||
+ns_zoom_find_child_frame_candidate (struct frame *f,
|
||||
+ struct frame **child_frame)
|
||||
+{
|
||||
+ Lisp_Object frames, tail;
|
||||
+ Lisp_Object frame, tail;
|
||||
+
|
||||
+ FOR_EACH_FRAME (tail, frames)
|
||||
+ FOR_EACH_FRAME (tail, frame)
|
||||
+ {
|
||||
+ struct frame *cf = XFRAME (frames);
|
||||
+ struct frame *cf = XFRAME (frame);
|
||||
+ if (!FRAME_NS_P (cf) || !FRAME_LIVE_P (cf))
|
||||
+ continue;
|
||||
+ if (FRAME_PARENT_FRAME (cf) != f)
|
||||
+ continue;
|
||||
+ /* Small buffer = likely completion popup. */
|
||||
+ /* Small buffer = likely completion popup. Guard against
|
||||
+ partially initialized frames where selected_window or its
|
||||
+ buffer may not yet be live. */
|
||||
+ if (!WINDOWP (cf->selected_window))
|
||||
+ continue;
|
||||
+ struct window *cw = XWINDOW (cf->selected_window);
|
||||
+ if (!BUFFERP (cw->contents))
|
||||
+ continue;
|
||||
+ struct buffer *b = XBUFFER (cw->contents);
|
||||
+ if (BUF_ZV (b) - BUF_BEGV (b) > 10000)
|
||||
+ if (BUF_ZV (b) - BUF_BEGV (b) > NS_AX_MAX_COMPLETION_BUFFER_CHARS)
|
||||
+ continue;
|
||||
+
|
||||
+ ptrdiff_t beg = BUF_BEGV (b);
|
||||
@@ -353,7 +367,7 @@ index 74e4ad5..5498d7a 100644
|
||||
static void
|
||||
ns_update_end (struct frame *f)
|
||||
/* --------------------------------------------------------------------------
|
||||
@@ -1104,6 +1366,41 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
|
||||
@@ -1104,6 +1383,41 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
|
||||
|
||||
unblock_input ();
|
||||
ns_updating_frame = NULL;
|
||||
@@ -395,7 +409,7 @@ index 74e4ad5..5498d7a 100644
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -3232,6 +3529,45 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
|
||||
@@ -3232,6 +3546,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));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user