EMACS NS ACCESSIBILITY PATCHES ================================ author: Martin Sukany This directory contains two independent patch sets for the Emacs NS (Cocoa) port: A. Standalone Zoom patch (0000) B. VoiceOver accessibility patch series (0001-0008) Each can be applied independently. They do not depend on each other. PATCH A: ZOOM CURSOR TRACKING (0000) ------------------------------------- 0000 ns: integrate with macOS Zoom for cursor tracking A minimal patch that informs macOS Zoom of the text cursor position after every physical cursor redraw. When Zoom is enabled (System Settings -> Accessibility -> Zoom -> Follow keyboard focus), the zoomed viewport automatically tracks the Emacs insertion point. Files modified: src/nsterm.h (+4 lines: lastZoomCursorRect ivar) src/nsterm.m (+66 lines: cursor store + UAZoomChangeFocus) etc/NEWS (+8 lines) Implementation: ns_draw_window_cursor stores the cursor rect in view->lastZoomCursorRect and calls UAZoomChangeFocus() with CG-space coordinates. A fallback call in ns_update_end ensures Zoom tracks the cursor even after 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. No user option is needed: UAZoomEnabled() returns false when Zoom is not active, so the overhead is a single function call per redisplay cycle. PATCH B: VOICEOVER ACCESSIBILITY (0001-0008) ---------------------------------------------- 0001 ns: add accessibility base classes and text extraction 0002 ns: implement buffer accessibility element (core protocol) 0003 ns: add buffer notification dispatch and mode-line element 0004 ns: add interactive span elements for Tab navigation 0005 ns: integrate accessibility with EmacsView and redisplay 0006 doc: add VoiceOver accessibility section to macOS appendix 0007 ns: announce overlay completion candidates for VoiceOver 0008 ns: announce child frame completion candidates for VoiceOver Files modified: src/nsterm.h (~120 lines: class declarations, ivars) src/nsterm.m (~3400 lines: implementation) doc/emacs/macos.texi (~50 lines: documentation) etc/NEWS (~8 lines) This patch series adds comprehensive VoiceOver accessibility support to the NS port. Before this patch, Emacs exposed only a minimal, largely broken accessibility interface: EmacsView identified itself as a generic NSAccessibilityGroup with no text content, no cursor tracking, and no notifications. ARCHITECTURE ------------ Virtual element tree above EmacsView: EmacsAccessibilityElement (base) +-- EmacsAccessibilityBuffer (AXTextArea; one per window) +-- EmacsAccessibilityModeLine (AXStaticText; mode line) +-- EmacsAccessibilityInteractiveSpan (AXButton/Link; Tab nav) Each buffer element maintains a text cache with visible-run mapping (O(log n) index lookup) and a precomputed line index (O(log L) line queries). Notifications are posted asynchronously via dispatch_async to prevent VoiceOver deadlocks. Full details in the commit messages of each patch. PERFORMANCE ----------- ns-accessibility-enabled (DEFVAR_BOOL, default t): When nil, no virtual elements are built, no notifications are posted, and ns_draw_window_cursor skips the cursor rect store. Zero overhead for users who do not use assistive technology. When enabled: - Text cache rebuilds only on BUF_MODIFF change (not per-keystroke) - Index lookups are O(log n) via binary search on visible runs - Line queries are O(log L) via precomputed lineStartOffsets - Interactive span scan runs only when dirty flag is set - No character cap: full buffer exposed, but cache is lazy THREADING MODEL --------------- Main thread: all Lisp calls, buffer mutations, notification posting. AX thread: VoiceOver queries dispatch_sync to main thread. Async notifications: dispatch_async prevents deadlock (same pattern as WebKit's AXObjectCacheMac). KNOWN LIMITATIONS ----------------- - Mode line: CHAR_GLYPH only (icon fonts produce incomplete text) - Overlay face matching: string containment ("current", "selected") - GNUstep excluded (#ifdef NS_IMPL_COCOA) - No multi-frame coordination - Child frame static lastCandidate leaks at exit (minor) TESTING ------- See TESTING.txt for the full test matrix and results. -- end of README --