Files
emacs-doom/patches
Daneel 9bee2a1987 patches: address remaining medium/low review findings
M-REM-1 - Stack array bound comment:
ns_ax_selected_child_frame_text uses line_starts/line_ends[128].
Added comment documenting the 128-line bound and the 10,000-character
upstream guard that makes silent truncation safe in practice.

M-REM-2 - Remove dead BUFFER_LIVE_P check:
The second BUFFER_LIVE_P check in announceChildFrameCompletion (after
updating childFrameLastBuffer/Modiff) is unreachable: no code between
the entry check and this point can kill the buffer.  Replaced with
eassert so DEBUG builds catch violations while releasing dead code.

M-REM-3 - @interface declaration for postEchoAreaAnnouncementIfNeeded:
Added declaration to the #ifdef NS_IMPL_COCOA accessibility block in
nsterm.h alongside existing postAccessibilityUpdates and
announceChildFrameCompletion declarations.  postAccessibilityUpdates
is called from ns_update_end; all three methods should be declared.

L-REM-4 - Commit message class name:
Changed (EmacsAXBuffer) to (EmacsAccessibilityBuffer) in the subject
line.  EmacsAXBuffer does not exist; EmacsAccessibilityBuffer is the
actual class that received the voiceoverSetPoint ivar.

NEWS - Remove third-party package names:
Replaced 'Vertico, Icomplete, Ivy for overlay candidates; Corfu,
Company-box for child frame popups' with generic descriptions per GNU
Emacs NEWS convention (third-party package names avoided in NEWS).
2026-03-03 10:21:41 +01:00
..

EMACS NS ACCESSIBILITY PATCHES
================================
author: Martin Sukany <martin@sukany.cz>

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 --