Move file-scope statics (lastChildFrameBuffer/Modiff/Candidate) and
ns_ax_reset_accessibility_updating before announceChildFrameCompletion.
Using them before their declaration was a forward reference (UB in C).
Remove two spurious blank lines at start of announceChildFrameCompletion
method body.
New patch 0009 fixes HIGH severity issues from Opus review:
- Announcement coalescing (50ms debounce)
- cachedText retain+autorelease in accessibilityValue
- EmacsView dealloc: nil out emacsView on all AX elements
- Nil guards on protocol methods + overlayZoomActive
0007 updated: revert accidental em-dash→triple-dash, add overlayZoomActive nil guards
0008 updated: specpdl exception safety for accessibilityUpdating, lastChildFrameBuffer staticpro
Series now 9 patches total (0001-0006 unchanged, 0007-0009 new/updated).
BLOCKER #1: accessibilityUpdating flag exception safety.
A Lisp signal (longjmp) during postAccessibilityUpdates left
the re-entrance flag permanently YES, suppressing all future
AX notifications → VoiceOver goes silent randomly.
Fix: specpdl unwind protection (record_unwind_protect_ptr)
resets the flag on any longjmp. All 3 exit points use unbind_to.
BLOCKER #2: static struct buffer *lastBuffer dangling pointer.
Raw C pointer to buffer struct has no GC protection. After
kill-buffer, the pointer dangles.
Fix: file-scope Lisp_Object lastChildFrameBuffer with staticpro.
EQ comparison instead of pointer equality.
Also: revert accidental em-dash → triple-dash in title bar (0007),
fix README factual error (BUF_OVERLAY_MODIFF cache key).
hl-line-mode (and similar) bumps BUF_MODIFF via text property
changes on every cursor movement. The else-if structure caused
the modiff branch to fire (skipping ValueChanged correctly) but
also blocked the cursor-move branch (SelectedTextChanged).
Fix: use textDidChange flag to decouple the two branches.
ValueChanged and SelectedTextChanged remain mutually exclusive
for real edits, but SelectedTextChanged now fires when only
text properties changed.
- childFrameCompletionActive flag: set by child frame handler,
cleared when no child frame visible on parent's next cycle
- Posts FocusedUIElementChanged on parent buffer element to
restore VoiceOver character echo after corfu closes
- corfu-auto-delay: 1.0 → 2.0 (reduce popup noise)
Fbuffer_substring_no_properties operates on current_buffer, not the
passed buffer. Added set_buffer_internal_1 + record_unwind_current_buffer
with unbind_to on every return path.
Added: child frame buffer scanning for Corfu/Company-box.
Fixed: Zoom rect at text area left edge (not window center).
Added: 'selection' face match for company-tooltip-selection.
Zoom rect now at text area left edge (WINDOW_TEXT_TO_FRAME_PIXEL_X)
with cursor-width (FRAME_COLUMN_WIDTH) instead of full window width.
Face matching adds 'selection' (company-tooltip-selection).
Zoom now tracks the selected candidate row via overlayZoomRect
instead of always pointing at the text cursor. Returns to cursor
on typing (chars_modiff change).
Previous two-reference algorithm failed because:
- Vertico's cursor-space line (face=nil) confused the reference
- Count overlay processed before candidates overlay
- Group titles have distinct faces too
New approach: ns_ax_face_is_selected checks if face symbol name
contains 'current' or 'selected'. Works for all major frameworks
(Vertico, Icomplete, Ivy) without framework-specific code.
Key changes from previous version:
- Remove overlay text from ns_ax_buffer_text (was causing spurious
'new line' announcements via VoiceOver text diff)
- Do NOT invalidate text cache on overlay change
- Two-reference face detection (handles selected at any position)
- SDATA scan instead of per-char Faref for newline detection
- Zoom tracking via UAZoomChangeFocus for selected candidate row
- Deduplication via cachedCompletionAnnouncement
- Appends overlay before-string/after-string to AX text
- Detects BUF_OVERLAY_MODIFF changes
- Finds highlighted candidate via face text property
- Announces selected candidate via AnnouncementRequested
(fixes 'new line' instead of reading candidate)
Split VoiceOver accessibility into 4 logical patches:
0001: Base classes + text extraction (+753)
0002: Buffer/ModeLine/InteractiveSpan implementations (+1716)
0003: EmacsView integration + cursor tracking (+395)
0004: Documentation with known limitations (+75)
Each patch is self-contained: 0001 adds infrastructure that compiles
but doesn't change behavior. 0002 adds protocol implementations.
0003 wires everything into EmacsView. 0004 documents for users.
All patches verified: apply cleanly to current Emacs master,
final state identical to original monolithic patch.
Split the monolithic 3011-line patch into logical pieces:
0001: All new accessibility code (infrastructure, no existing code modified)
0002: EmacsView integration + cursor tracking (wiring only)
0003: Documentation (expanded with known limitations)
Improvements:
- Comprehensive commit messages with testing methodology
- Known limitations documented (text cap, bidi, mode-line icons)
- Documentation expanded with Known Limitations section
- Each patch is self-contained and reviewable
M1: accessibilityRangeForPosition uses specpdl unwind protection for
block_input/unblock_input (consistent with all other methods).
M2: Track BUF_OVERLAY_MODIFF in ensureTextCache — overlay-only changes
(timer-based completion highlight) now invalidate the text cache.
M3: Detect narrowing/widening by comparing cachedTextStart vs BUF_BEGV.
m1: Binary search (O(log n)) for visible runs in both
accessibilityIndexForCharpos and charposForAccessibilityIndex.
m3: Add EmacsAXSpanTypeNone = -1 to enum instead of (EmacsAXSpanType)-1 cast.
m5: Add TODO comment in ns_ax_mode_line_text about non-CHAR_GLYPH limitation.
README: Remove resolved overlay_modiff limitation, document binary search
and narrowing detection, update architecture section.
B1: setAccessibilityFocused: on EmacsAccessibilityBuffer now checks
![NSThread isMainThread] and dispatches to main via dispatch_async.
Prevents data race + AppKit thread violation from AX server thread.
W1: accessibilityInsertionPointLineNumber and accessibilityLineForIndex:
now use lineRangeForRange iteration — O(lines) instead of O(chars).
W2: ns_ax_scan_interactive_spans skips non-interactive regions using
Fnext_single_property_change for each scannable property and
Fnext_single_char_property_change for keymap overlays.
W3: ns_ax_event_is_line_nav_key inspects Vthis_command against known
navigation command symbols (next-line, previous-line, evil variants,
dired variants) instead of raw key codes. Tab/backtab fallback
retained via last_command_event.
W4: DEFSYM symbols renamed with ns_ax_ prefix (Qns_ax_button, etc.)
to avoid linker collisions with other Emacs source files.
Lisp symbol strings unchanged.
M3: Removed dead enum values (CheckBox, TextField, PopUpButton) and
corresponding dead switch cases.
M4: Improved accessibilityStyleRangeForIndex: comment documenting the
line-granularity simplification.
README: Updated stats, KNOWN LIMITATIONS, DEFSYM docs, test numbering.
Add deadlock prevention section to THREADING MODEL, note async
posting in NOTIFICATION STRATEGY, add design decision 6a, and
add deadlock regression test case (#24) to testing checklist.