v13.3: restore typing echo + add full text protocol on EmacsView
Key changes: - Typing echo notifications back in ns_draw_window_cursor (on EmacsView, synchronous with cursor draw — this is how v9 did it and it worked) - Rich userInfo: AXTextEditType=3, AXTextChangeValues with actual char - SelectedTextChangedNotification on every cursor draw - Full text protocol on EmacsView: accessibilityNumberOfCharacters, accessibilitySelectedText, accessibilityInsertionPointLineNumber, accessibilityVisibleCharacterRange, accessibilityLineForIndex:, accessibilityRangeForLine:, accessibilityAttributedStringForRange: - accessibilityAttributedStringForRange: on EmacsAccessibilityBuffer too - Virtual tree kept for VoiceOver window/buffer detection
This commit is contained in:
@@ -2,7 +2,7 @@ From: Martin Sukany <martin@sukany.cz>
|
|||||||
Date: Wed, 26 Feb 2026 00:00:00 +0100
|
Date: Wed, 26 Feb 2026 00:00:00 +0100
|
||||||
Subject: [PATCH] ns: add macOS Zoom cursor tracking and VoiceOver accessibility
|
Subject: [PATCH] ns: add macOS Zoom cursor tracking and VoiceOver accessibility
|
||||||
|
|
||||||
Dual accessibility: UAZoomChangeFocus + virtual element tree.
|
Dual accessibility: UAZoomChangeFocus + virtual element tree + typing echo.
|
||||||
MRC compatible (unsafe_unretained, proper retain/release).
|
MRC compatible (unsafe_unretained, proper retain/release).
|
||||||
---
|
---
|
||||||
--- a/src/nsterm.h 2026-02-26 08:46:18.118172281 +0100
|
--- a/src/nsterm.h 2026-02-26 08:46:18.118172281 +0100
|
||||||
@@ -69,7 +69,7 @@ MRC compatible (unsafe_unretained, proper retain/release).
|
|||||||
|
|
||||||
|
|
||||||
--- a/src/nsterm.m 2026-02-26 08:46:18.124172384 +0100
|
--- a/src/nsterm.m 2026-02-26 08:46:18.124172384 +0100
|
||||||
+++ b/src/nsterm.m 2026-02-26 09:02:44.734005575 +0100
|
+++ b/src/nsterm.m 2026-02-26 09:23:14.570493387 +0100
|
||||||
@@ -1104,6 +1104,11 @@
|
@@ -1104,6 +1104,11 @@
|
||||||
|
|
||||||
unblock_input ();
|
unblock_input ();
|
||||||
@@ -82,7 +82,7 @@ MRC compatible (unsafe_unretained, proper retain/release).
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -3232,6 +3237,38 @@
|
@@ -3232,6 +3237,75 @@
|
||||||
/* Prevent the cursor from being drawn outside the text area. */
|
/* Prevent the cursor from being drawn outside the text area. */
|
||||||
r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA));
|
r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA));
|
||||||
|
|
||||||
@@ -98,6 +98,43 @@ MRC compatible (unsafe_unretained, proper retain/release).
|
|||||||
+ /* Store cursor rect for accessibilityBoundsForRange: queries. */
|
+ /* Store cursor rect for accessibilityBoundsForRange: queries. */
|
||||||
+ view->lastAccessibilityCursorRect = r;
|
+ view->lastAccessibilityCursorRect = r;
|
||||||
+
|
+
|
||||||
|
+ struct buffer *curbuf
|
||||||
|
+ = XBUFFER (XWINDOW (f->selected_window)->contents);
|
||||||
|
+
|
||||||
|
+ if (curbuf && BUF_MODIFF (curbuf) != view->lastAccessibilityModiff)
|
||||||
|
+ {
|
||||||
|
+ /* Buffer content changed — typing echo. Post ValueChanged
|
||||||
|
+ with rich userInfo on the VIEW so VoiceOver can speak.
|
||||||
|
+ kAXTextStateChangeTypeEdit = 1, kAXTextEditTypeTyping = 3. */
|
||||||
|
+ view->lastAccessibilityModiff = BUF_MODIFF (curbuf);
|
||||||
|
+
|
||||||
|
+ NSString *changedText = @"";
|
||||||
|
+ ptrdiff_t pt = BUF_PT (curbuf);
|
||||||
|
+ if (pt > BUF_BEGV (curbuf))
|
||||||
|
+ {
|
||||||
|
+ NSRange charRange = NSMakeRange (
|
||||||
|
+ (NSUInteger)(pt - BUF_BEGV (curbuf) - 1), 1);
|
||||||
|
+ changedText = [view accessibilityStringForRange:charRange];
|
||||||
|
+ if (!changedText)
|
||||||
|
+ changedText = @"";
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ NSDictionary *change = @{
|
||||||
|
+ @"AXTextEditType": @3,
|
||||||
|
+ @"AXTextChangeValue": changedText
|
||||||
|
+ };
|
||||||
|
+ NSDictionary *userInfo = @{
|
||||||
|
+ @"AXTextStateChangeType": @1,
|
||||||
|
+ @"AXTextChangeValues": @[change]
|
||||||
|
+ };
|
||||||
|
+ NSAccessibilityPostNotificationWithUserInfo (
|
||||||
|
+ view, NSAccessibilityValueChangedNotification, userInfo);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* Always notify cursor movement. */
|
||||||
|
+ NSAccessibilityPostNotification (
|
||||||
|
+ view, NSAccessibilitySelectedTextChangedNotification);
|
||||||
|
+
|
||||||
+ /* Tell macOS Zoom where the cursor is. UAZoomChangeFocus()
|
+ /* Tell macOS Zoom where the cursor is. UAZoomChangeFocus()
|
||||||
+ expects top-left origin (CG coordinate space). */
|
+ expects top-left origin (CG coordinate space). */
|
||||||
+ if (UAZoomEnabled ())
|
+ if (UAZoomEnabled ())
|
||||||
@@ -121,7 +158,7 @@ MRC compatible (unsafe_unretained, proper retain/release).
|
|||||||
ns_focus (f, NULL, 0);
|
ns_focus (f, NULL, 0);
|
||||||
|
|
||||||
NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
|
NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
|
||||||
@@ -6849,6 +6886,631 @@
|
@@ -6849,6 +6923,637 @@
|
||||||
|
|
||||||
/* ==========================================================================
|
/* ==========================================================================
|
||||||
|
|
||||||
@@ -550,6 +587,12 @@ MRC compatible (unsafe_unretained, proper retain/release).
|
|||||||
+ return [text substringWithRange:range];
|
+ return [text substringWithRange:range];
|
||||||
+}
|
+}
|
||||||
+
|
+
|
||||||
|
+- (NSAttributedString *)accessibilityAttributedStringForRange:(NSRange)range
|
||||||
|
+{
|
||||||
|
+ NSString *str = [self accessibilityStringForRange:range];
|
||||||
|
+ return [[[NSAttributedString alloc] initWithString:str] autorelease];
|
||||||
|
+}
|
||||||
|
+
|
||||||
+- (NSInteger)accessibilityLineForIndex:(NSInteger)index
|
+- (NSInteger)accessibilityLineForIndex:(NSInteger)index
|
||||||
+{
|
+{
|
||||||
+ struct window *w = self.emacsWindow;
|
+ struct window *w = self.emacsWindow;
|
||||||
@@ -753,7 +796,7 @@ MRC compatible (unsafe_unretained, proper retain/release).
|
|||||||
EmacsView implementation
|
EmacsView implementation
|
||||||
|
|
||||||
========================================================================== */
|
========================================================================== */
|
||||||
@@ -6889,6 +7551,7 @@
|
@@ -6889,6 +7594,7 @@
|
||||||
[layer release];
|
[layer release];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -761,7 +804,7 @@ MRC compatible (unsafe_unretained, proper retain/release).
|
|||||||
[[self menu] release];
|
[[self menu] release];
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
@@ -9474,6 +10137,293 @@
|
@@ -9474,6 +10180,391 @@
|
||||||
return fs_state;
|
return fs_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1017,6 +1060,104 @@ MRC compatible (unsafe_unretained, proper retain/release).
|
|||||||
+ return [NSString stringWithLispString:str];
|
+ return [NSString stringWithLispString:str];
|
||||||
+}
|
+}
|
||||||
+
|
+
|
||||||
|
+- (NSInteger)accessibilityNumberOfCharacters
|
||||||
|
+{
|
||||||
|
+ if (!emacsframe)
|
||||||
|
+ return 0;
|
||||||
|
+ struct buffer *curbuf
|
||||||
|
+ = XBUFFER (XWINDOW (emacsframe->selected_window)->contents);
|
||||||
|
+ if (!curbuf)
|
||||||
|
+ return 0;
|
||||||
|
+ ptrdiff_t range = BUF_ZV (curbuf) - BUF_BEGV (curbuf);
|
||||||
|
+ return (NSInteger) MIN (range, 10000);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+- (NSString *)accessibilitySelectedText
|
||||||
|
+{
|
||||||
|
+ if (!emacsframe)
|
||||||
|
+ return @"";
|
||||||
|
+ struct buffer *curbuf
|
||||||
|
+ = XBUFFER (XWINDOW (emacsframe->selected_window)->contents);
|
||||||
|
+ if (!curbuf || NILP (BVAR (curbuf, mark_active)))
|
||||||
|
+ return @"";
|
||||||
|
+ return @"";
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+- (NSInteger)accessibilityInsertionPointLineNumber
|
||||||
|
+{
|
||||||
|
+ if (!emacsframe)
|
||||||
|
+ return 0;
|
||||||
|
+ struct window *w = XWINDOW (emacsframe->selected_window);
|
||||||
|
+ if (!w)
|
||||||
|
+ return 0;
|
||||||
|
+ return (NSInteger) (w->cursor.vpos);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+- (NSRange)accessibilityVisibleCharacterRange
|
||||||
|
+{
|
||||||
|
+ if (!emacsframe)
|
||||||
|
+ return NSMakeRange (0, 0);
|
||||||
|
+ struct buffer *curbuf
|
||||||
|
+ = XBUFFER (XWINDOW (emacsframe->selected_window)->contents);
|
||||||
|
+ if (!curbuf)
|
||||||
|
+ return NSMakeRange (0, 0);
|
||||||
|
+ ptrdiff_t range = BUF_ZV (curbuf) - BUF_BEGV (curbuf);
|
||||||
|
+ return NSMakeRange (0, (NSUInteger) MIN (range, 10000));
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+- (NSAttributedString *)accessibilityAttributedStringForRange:(NSRange)range
|
||||||
|
+{
|
||||||
|
+ NSString *str = [self accessibilityStringForRange:range];
|
||||||
|
+ return [[[NSAttributedString alloc] initWithString:str] autorelease];
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+- (NSInteger)accessibilityLineForIndex:(NSInteger)index
|
||||||
|
+{
|
||||||
|
+ if (!emacsframe)
|
||||||
|
+ return 0;
|
||||||
|
+ struct window *w = XWINDOW (emacsframe->selected_window);
|
||||||
|
+ if (!w || !w->current_matrix)
|
||||||
|
+ return 0;
|
||||||
|
+ struct buffer *curbuf = XBUFFER (w->contents);
|
||||||
|
+ if (!curbuf)
|
||||||
|
+ return 0;
|
||||||
|
+ ptrdiff_t charpos = BUF_BEGV (curbuf) + (ptrdiff_t) index;
|
||||||
|
+ struct glyph_matrix *matrix = w->current_matrix;
|
||||||
|
+ for (int i = 0; i < matrix->nrows; i++)
|
||||||
|
+ {
|
||||||
|
+ struct glyph_row *row = matrix->rows + i;
|
||||||
|
+ if (!row->enabled_p)
|
||||||
|
+ continue;
|
||||||
|
+ if (MATRIX_ROW_START_CHARPOS (row) <= charpos
|
||||||
|
+ && charpos < MATRIX_ROW_END_CHARPOS (row))
|
||||||
|
+ return (NSInteger) i;
|
||||||
|
+ }
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+- (NSRange)accessibilityRangeForLine:(NSInteger)line
|
||||||
|
+{
|
||||||
|
+ if (!emacsframe)
|
||||||
|
+ return NSMakeRange (0, 0);
|
||||||
|
+ struct window *w = XWINDOW (emacsframe->selected_window);
|
||||||
|
+ if (!w || !w->current_matrix)
|
||||||
|
+ return NSMakeRange (0, 0);
|
||||||
|
+ struct buffer *curbuf = XBUFFER (w->contents);
|
||||||
|
+ if (!curbuf)
|
||||||
|
+ return NSMakeRange (0, 0);
|
||||||
|
+ struct glyph_matrix *matrix = w->current_matrix;
|
||||||
|
+ if (line < 0 || line >= matrix->nrows)
|
||||||
|
+ return NSMakeRange (0, 0);
|
||||||
|
+ struct glyph_row *row = matrix->rows + line;
|
||||||
|
+ if (!row->enabled_p)
|
||||||
|
+ return NSMakeRange (0, 0);
|
||||||
|
+ ptrdiff_t start = MATRIX_ROW_START_CHARPOS (row) - BUF_BEGV (curbuf);
|
||||||
|
+ ptrdiff_t end = MATRIX_ROW_END_CHARPOS (row) - BUF_BEGV (curbuf);
|
||||||
|
+ if (start < 0) start = 0;
|
||||||
|
+ if (end < start) end = start;
|
||||||
|
+ return NSMakeRange ((NSUInteger) start, (NSUInteger) (end - start));
|
||||||
|
+}
|
||||||
|
+
|
||||||
+/* ---- Legacy parameterized attribute APIs (Zoom uses these) ---- */
|
+/* ---- Legacy parameterized attribute APIs (Zoom uses these) ---- */
|
||||||
+
|
+
|
||||||
+- (NSArray *)accessibilityParameterizedAttributeNames
|
+- (NSArray *)accessibilityParameterizedAttributeNames
|
||||||
@@ -1055,7 +1196,7 @@ MRC compatible (unsafe_unretained, proper retain/release).
|
|||||||
@end /* EmacsView */
|
@end /* EmacsView */
|
||||||
|
|
||||||
|
|
||||||
@@ -9941,6 +10891,14 @@
|
@@ -9941,6 +11032,14 @@
|
||||||
|
|
||||||
return [super accessibilityAttributeValue:attribute];
|
return [super accessibilityAttributeValue:attribute];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user