Fix three VoiceOver bugs: crash, cursor sync, word announcement
Bug 1 (crash): postEchoAreaAnnouncementIfNeeded called Fbuffer_string() without block_input, allowing timer events to interleave with Lisp calls and corrupt buffer state. Added block_input/unblock_input via record_unwind_protect_void. Also removed unpaired unblock_input() in postCompletionAnnouncementForBuffer (patch 0003) that became a double-unblock after patch 0008 added block_input + unwind protect. Bug 2 (cursor sync): VoiceOver browse cursor did not follow Emacs keyboard cursor because SelectedTextChanged with discontiguous direction alone is not sufficient for VoiceOver to re-anchor. Added NSAccessibilityFocusedUIElementChangedNotification post when Emacs moves the cursor across a line boundary, forcing VoiceOver to re-query accessibilitySelectedTextRange. Bug 3 (word off-by-one): Evil w (next-word) read the previous word instead of the destination word. VO auto-speech from SelectedTextChanged with direction=next+granularity=word reads the traversed word, not the arrived-at word. Added explicit word announcement (like char moves) that reads the word AT the new cursor position using whitespace-delimited word boundary scan.
This commit is contained in:
@@ -435,9 +435,9 @@ index 8d44b5f..29b646d 100644
|
||||
specpdl_ref count2 = SPECPDL_INDEX ();
|
||||
+ /* Register unblock_input as an unwind action so that if any Lisp
|
||||
+ call below signals (triggering a longjmp through unbind_to),
|
||||
+ block_input is always paired with an unblock_input. The explicit
|
||||
+ unblock_input() at the end of the function is still needed for
|
||||
+ the normal (non-signal) path. */
|
||||
+ block_input is always paired with an unblock_input. The
|
||||
+ unbind_to call at the end of the function unwinds this.
|
||||
+ record_unwind_protect_void plus unbind_to is idempotent. */
|
||||
+ record_unwind_protect_void (unblock_input);
|
||||
record_unwind_current_buffer ();
|
||||
if (b != current_buffer)
|
||||
@@ -518,7 +518,7 @@ index 8d44b5f..29b646d 100644
|
||||
NSInteger direction = ns_ax_text_selection_direction_discontiguous;
|
||||
if (point > oldPoint)
|
||||
direction = ns_ax_text_selection_direction_next;
|
||||
@@ -9488,6 +9670,26 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property
|
||||
@@ -9488,6 +9670,41 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property
|
||||
granularity = ns_ax_text_selection_granularity_line;
|
||||
}
|
||||
|
||||
@@ -541,6 +541,21 @@ index 8d44b5f..29b646d 100644
|
||||
+ handles them correctly. */
|
||||
+ if (emacsMovedCursor && !isCtrlNP)
|
||||
+ direction = ns_ax_text_selection_direction_discontiguous;
|
||||
+
|
||||
+ /* Post FocusedUIElementChanged when Emacs moved the cursor
|
||||
+ across a line boundary so VoiceOver re-anchors its browse
|
||||
+ cursor at the new accessibilitySelectedTextRange.
|
||||
+ SelectedTextChanged with discontiguous direction alone is
|
||||
+ not sufficient; VoiceOver requires this notification to
|
||||
+ re-query the focused element and update its internal
|
||||
+ browse position. */
|
||||
+ if (emacsMovedCursor
|
||||
+ && granularity
|
||||
+ == ns_ax_text_selection_granularity_line
|
||||
+ && [self isAccessibilityFocused])
|
||||
+ ns_ax_post_notification (
|
||||
+ self,
|
||||
+ NSAccessibilityFocusedUIElementChangedNotification);
|
||||
+
|
||||
/* Post notifications for focused and non-focused elements. */
|
||||
if ([self isAccessibilityFocused])
|
||||
@@ -592,7 +607,7 @@ index 8d44b5f..29b646d 100644
|
||||
scrollbarsNeedingUpdate = 0;
|
||||
fs_state = FULLSCREEN_NONE;
|
||||
fs_before_fs = next_maximized = -1;
|
||||
@@ -12688,6 +12909,152 @@ - (id)accessibilityFocusedUIElement
|
||||
@@ -12688,6 +12909,154 @@ - (id)accessibilityFocusedUIElement
|
||||
The existing elements carry cached state (modiff, point) from the
|
||||
previous redisplay cycle. Rebuilding first would create fresh
|
||||
elements with current values, making change detection impossible. */
|
||||
@@ -631,7 +646,9 @@ index 8d44b5f..29b646d 100644
|
||||
+ set_buffer_internal_1 is preferred over set_buffer_internal in
|
||||
+ a redisplay context: it skips point-motion hooks that could
|
||||
+ trigger further redisplay or modify buffer state unexpectedly. */
|
||||
+ block_input ();
|
||||
+ specpdl_ref count = SPECPDL_INDEX ();
|
||||
+ record_unwind_protect_void (unblock_input);
|
||||
+ record_unwind_current_buffer ();
|
||||
+ set_buffer_internal_1 (eb);
|
||||
+ Lisp_Object ls = Fbuffer_string ();
|
||||
|
||||
Reference in New Issue
Block a user