patches: fix VoiceOver deadlock — async AX notification posting
NSAccessibilityPostNotification may synchronously invoke VoiceOver callbacks from a background AX server thread. Those callbacks call dispatch_sync(main_queue) to read buffer state. If the main thread is still inside the notification-posting method (postAccessibilityUpdates, windowDidBecomeKey, or postAccessibilityNotificationsForFrame), the dispatch_sync deadlocks. Symptom: Emacs hangs on C-x o after M-x list-buffers from Completions buffer, but only with VoiceOver enabled. Fix: introduce ns_ax_post_notification() and ns_ax_post_notification_with_info() wrappers that defer notification posting via dispatch_async(main_queue). This lets the current method return and frees the main queue for VoiceOver's dispatch_sync calls. All 14 notification-posting sites now use the async wrappers.
This commit is contained in:
@@ -310,7 +310,7 @@ index 932d209f..ea2de6f2 100644
|
||||
ns_focus (f, NULL, 0);
|
||||
|
||||
NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
|
||||
@@ -6849,218 +6891,2387 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
|
||||
@@ -6849,218 +6891,2414 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
|
||||
|
||||
/* ==========================================================================
|
||||
|
||||
@@ -939,6 +939,33 @@ index 932d209f..ea2de6f2 100644
|
||||
+ return @"";
|
||||
}
|
||||
|
||||
+/* Post AX notifications asynchronously to prevent deadlock.
|
||||
+ NSAccessibilityPostNotification may synchronously invoke VoiceOver
|
||||
+ callbacks that dispatch_sync back to the main queue. If we are
|
||||
+ already on the main queue (e.g., inside postAccessibilityUpdates
|
||||
+ called from ns_update_end), that dispatch_sync deadlocks.
|
||||
+ Deferring via dispatch_async lets the current method return first,
|
||||
+ freeing the main queue for VoiceOver's dispatch_sync calls. */
|
||||
+
|
||||
+static inline void
|
||||
+ns_ax_post_notification (id element,
|
||||
+ NSAccessibilityNotificationName name)
|
||||
+{
|
||||
+ dispatch_async (dispatch_get_main_queue (), ^{
|
||||
+ NSAccessibilityPostNotification (element, name);
|
||||
+ });
|
||||
+}
|
||||
+
|
||||
+static inline void
|
||||
+ns_ax_post_notification_with_info (id element,
|
||||
+ NSAccessibilityNotificationName name,
|
||||
+ NSDictionary *info)
|
||||
+{
|
||||
+ dispatch_async (dispatch_get_main_queue (), ^{
|
||||
+ NSAccessibilityPostNotificationWithUserInfo (element, name, info);
|
||||
+ });
|
||||
+}
|
||||
+
|
||||
+/* Scan visible range of window W for interactive spans.
|
||||
+ Returns NSArray<EmacsAccessibilityInteractiveSpan *>.
|
||||
|
||||
@@ -1758,7 +1785,7 @@ index 932d209f..ea2de6f2 100644
|
||||
+ @(ns_ax_text_state_change_selection_move),
|
||||
+ @"AXTextChangeElement": self
|
||||
+ };
|
||||
+ NSAccessibilityPostNotificationWithUserInfo (
|
||||
+ ns_ax_post_notification_with_info (
|
||||
+ self, NSAccessibilitySelectedTextChangedNotification, info);
|
||||
+}
|
||||
+
|
||||
@@ -2135,7 +2162,7 @@ index 932d209f..ea2de6f2 100644
|
||||
+ @"AXTextChangeValues": @[change],
|
||||
+ @"AXTextChangeElement": self
|
||||
+ };
|
||||
+ NSAccessibilityPostNotificationWithUserInfo (
|
||||
+ ns_ax_post_notification_with_info (
|
||||
+ self, NSAccessibilityValueChangedNotification, userInfo);
|
||||
+ }
|
||||
+
|
||||
@@ -2244,7 +2271,7 @@ index 932d209f..ea2de6f2 100644
|
||||
+ if (!isCharMove)
|
||||
+ moveInfo[@"AXTextSelectionGranularity"] = @(granularity);
|
||||
+
|
||||
+ NSAccessibilityPostNotificationWithUserInfo (
|
||||
+ ns_ax_post_notification_with_info (
|
||||
+ self,
|
||||
+ NSAccessibilitySelectedTextChangedNotification,
|
||||
+ moveInfo);
|
||||
@@ -2275,7 +2302,7 @@ index 932d209f..ea2de6f2 100644
|
||||
+ NSAccessibilityPriorityKey:
|
||||
+ @(NSAccessibilityPriorityHigh)
|
||||
+ };
|
||||
+ NSAccessibilityPostNotificationWithUserInfo (
|
||||
+ ns_ax_post_notification_with_info (
|
||||
+ NSApp,
|
||||
+ NSAccessibilityAnnouncementRequestedNotification,
|
||||
+ annInfo);
|
||||
@@ -2332,7 +2359,7 @@ index 932d209f..ea2de6f2 100644
|
||||
+ NSAccessibilityPriorityKey:
|
||||
+ @(NSAccessibilityPriorityHigh)
|
||||
+ };
|
||||
+ NSAccessibilityPostNotificationWithUserInfo (
|
||||
+ ns_ax_post_notification_with_info (
|
||||
+ NSApp,
|
||||
+ NSAccessibilityAnnouncementRequestedNotification,
|
||||
+ annInfo);
|
||||
@@ -2495,7 +2522,7 @@ index 932d209f..ea2de6f2 100644
|
||||
+ NSAccessibilityPriorityKey:
|
||||
+ @(NSAccessibilityPriorityHigh)
|
||||
+ };
|
||||
+ NSAccessibilityPostNotificationWithUserInfo (
|
||||
+ ns_ax_post_notification_with_info (
|
||||
+ NSApp,
|
||||
+ NSAccessibilityAnnouncementRequestedNotification,
|
||||
+ annInfo);
|
||||
@@ -2846,7 +2873,7 @@ index 932d209f..ea2de6f2 100644
|
||||
int code;
|
||||
unsigned fnKeysym = 0;
|
||||
static NSMutableArray *nsEvArray;
|
||||
@@ -8237,6 +10448,31 @@ - (void)windowDidBecomeKey /* for direct calls */
|
||||
@@ -8237,6 +10475,31 @@ - (void)windowDidBecomeKey /* for direct calls */
|
||||
XSETFRAME (event.frame_or_window, emacsframe);
|
||||
kbd_buffer_store_event (&event);
|
||||
ns_send_appdefined (-1); // Kick main loop
|
||||
@@ -2860,25 +2887,25 @@ index 932d209f..ea2de6f2 100644
|
||||
+ if (focused
|
||||
+ && [focused isKindOfClass:[EmacsAccessibilityBuffer class]])
|
||||
+ {
|
||||
+ NSAccessibilityPostNotification (focused,
|
||||
+ ns_ax_post_notification (focused,
|
||||
+ NSAccessibilityFocusedUIElementChangedNotification);
|
||||
+ NSDictionary *info = @{
|
||||
+ @"AXTextStateChangeType":
|
||||
+ @(ns_ax_text_state_change_selection_move),
|
||||
+ @"AXTextChangeElement": focused
|
||||
+ };
|
||||
+ NSAccessibilityPostNotificationWithUserInfo (focused,
|
||||
+ ns_ax_post_notification_with_info (focused,
|
||||
+ NSAccessibilitySelectedTextChangedNotification, info);
|
||||
+ }
|
||||
+ else if (focused)
|
||||
+ NSAccessibilityPostNotification (focused,
|
||||
+ ns_ax_post_notification (focused,
|
||||
+ NSAccessibilityFocusedUIElementChangedNotification);
|
||||
+ }
|
||||
+#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -9474,6 +11710,332 @@ - (int) fullscreenState
|
||||
@@ -9474,6 +11737,332 @@ - (int) fullscreenState
|
||||
return fs_state;
|
||||
}
|
||||
|
||||
@@ -3062,13 +3089,13 @@ index 932d209f..ea2de6f2 100644
|
||||
+ for (EmacsAccessibilityElement *elem in accessibilityElements)
|
||||
+ if ([elem isKindOfClass: [EmacsAccessibilityBuffer class]])
|
||||
+ [(EmacsAccessibilityBuffer *) elem invalidateInteractiveSpans];
|
||||
+ NSAccessibilityPostNotification (self,
|
||||
+ ns_ax_post_notification (self,
|
||||
+ NSAccessibilityLayoutChangedNotification);
|
||||
+
|
||||
+ /* Post focus change so VoiceOver picks up the new tree. */
|
||||
+ id focused = [self accessibilityFocusedUIElement];
|
||||
+ if (focused && focused != self)
|
||||
+ NSAccessibilityPostNotification (focused,
|
||||
+ ns_ax_post_notification (focused,
|
||||
+ NSAccessibilityFocusedUIElementChangedNotification);
|
||||
+
|
||||
+ lastSelectedWindow = emacsframe->selected_window;
|
||||
@@ -3101,18 +3128,18 @@ index 932d209f..ea2de6f2 100644
|
||||
+ if (focused && focused != self
|
||||
+ && [focused isKindOfClass:[EmacsAccessibilityBuffer class]])
|
||||
+ {
|
||||
+ NSAccessibilityPostNotification (focused,
|
||||
+ ns_ax_post_notification (focused,
|
||||
+ NSAccessibilityFocusedUIElementChangedNotification);
|
||||
+ NSDictionary *info = @{
|
||||
+ @"AXTextStateChangeType":
|
||||
+ @(ns_ax_text_state_change_selection_move),
|
||||
+ @"AXTextChangeElement": focused
|
||||
+ };
|
||||
+ NSAccessibilityPostNotificationWithUserInfo (focused,
|
||||
+ ns_ax_post_notification_with_info (focused,
|
||||
+ NSAccessibilitySelectedTextChangedNotification, info);
|
||||
+ }
|
||||
+ else if (focused && focused != self)
|
||||
+ NSAccessibilityPostNotification (focused,
|
||||
+ ns_ax_post_notification (focused,
|
||||
+ NSAccessibilityFocusedUIElementChangedNotification);
|
||||
+ }
|
||||
+
|
||||
@@ -3211,7 +3238,7 @@ index 932d209f..ea2de6f2 100644
|
||||
@end /* EmacsView */
|
||||
|
||||
|
||||
@@ -11303,6 +13865,18 @@ Convert an X font name (XLFD) to an NS font name.
|
||||
@@ -11303,6 +13892,18 @@ Convert an X font name (XLFD) to an NS font name.
|
||||
DEFSYM (Qns_drag_operation_generic, "ns-drag-operation-generic");
|
||||
DEFSYM (Qns_handle_drag_motion, "ns-handle-drag-motion");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user