diff --git a/patches/0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch b/patches/0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch index f8cf147..a053047 100644 --- a/patches/0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch +++ b/patches/0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch @@ -1,19 +1,19 @@ -From 5fb855925912142401db0c732fff2014d21c3362 Mon Sep 17 00:00:00 2001 +From f88649b2e09520d4c6e7ebc041e9e1a6e7ed616b Mon Sep 17 00:00:00 2001 From: Daneel -Date: Thu, 26 Feb 2026 18:06:10 +0100 +Date: Thu, 26 Feb 2026 18:24:58 +0100 Subject: [PATCH] ns: implement AXBoundsForRange and VoiceOver interaction fixes --- - nsterm.h | 70 ++ - nsterm.m | 2090 +++++++++++++++++++++++++++++++++++++++++++++++++----- - 2 files changed, 1997 insertions(+), 163 deletions(-) + nsterm.h | 71 ++ + nsterm.m | 2126 ++++++++++++++++++++++++++++++++++++++++++++++++++---- + 2 files changed, 2051 insertions(+), 146 deletions(-) diff --git a/src/nsterm.h b/src/nsterm.h -index 7c1ee4c..97da979 100644 +index 7c1ee4c..22828f2 100644 --- a/src/nsterm.h +++ b/src/nsterm.h -@@ -453,6 +453,61 @@ enum ns_return_frame_mode +@@ -453,6 +453,62 @@ enum ns_return_frame_mode @end @@ -60,6 +60,7 @@ index 7c1ee4c..97da979 100644 +@property (nonatomic, copy) NSString *cachedCompletionAnnouncement; +@property (nonatomic, assign) ptrdiff_t cachedCompletionOverlayStart; +@property (nonatomic, assign) ptrdiff_t cachedCompletionOverlayEnd; ++@property (nonatomic, assign) ptrdiff_t cachedCompletionPoint; +- (void)invalidateTextCache; +- (void)postAccessibilityNotificationsForFrame:(struct frame *)f; +- (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx; @@ -75,7 +76,7 @@ index 7c1ee4c..97da979 100644 /* ========================================================================== The main Emacs view -@@ -471,6 +526,14 @@ enum ns_return_frame_mode +@@ -471,6 +527,14 @@ enum ns_return_frame_mode #ifdef NS_IMPL_COCOA char *old_title; BOOL maximizing_resize; @@ -90,7 +91,7 @@ index 7c1ee4c..97da979 100644 #endif BOOL font_panel_active; NSFont *font_panel_result; -@@ -528,6 +591,13 @@ enum ns_return_frame_mode +@@ -528,6 +592,13 @@ enum ns_return_frame_mode - (void)windowWillExitFullScreen; - (void)windowDidExitFullScreen; - (void)windowDidBecomeKey; @@ -105,7 +106,7 @@ index 7c1ee4c..97da979 100644 diff --git a/src/nsterm.m b/src/nsterm.m -index 932d209..e7af9a3 100644 +index 932d209..add827f 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -1104,6 +1104,11 @@ ns_update_end (struct frame *f) @@ -158,7 +159,7 @@ index 932d209..e7af9a3 100644 ns_focus (f, NULL, 0); NSGraphicsContext *ctx = [NSGraphicsContext currentContext]; -@@ -6849,240 +6885,1646 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action) +@@ -6849,220 +6885,1696 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action) /* ========================================================================== @@ -173,93 +174,57 @@ index 932d209..e7af9a3 100644 +/* ---- Helper: extract buffer text for accessibility ---- */ -- (void)windowDidEndLiveResize:(NSNotification *)notification --{ -- [self updateFramePosition]; --} +/* Maximum characters exposed via accessibilityValue. */ +#define NS_AX_TEXT_CAP 100000 - --/* Needed to inform when window closed from lisp. */ --- (void) setWindowClosing: (BOOL)closing --{ -- NSTRACE ("[EmacsView setWindowClosing:%d]", closing); ++ +/* Build accessibility text for window W, skipping invisible text. + Populates *OUT_START with the buffer start charpos. + Populates *OUT_RUNS with an array of visible runs and *OUT_NRUNS + with the count. Caller must free *OUT_RUNS with xfree(). */ - -- windowClosing = closing; --} ++ +static NSString * +ns_ax_buffer_text (struct window *w, ptrdiff_t *out_start, + ns_ax_visible_run **out_runs, NSUInteger *out_nruns) -+{ + { +- [self updateFramePosition]; + *out_runs = NULL; + *out_nruns = 0; - ++ + if (!w || !WINDOW_LEAF_P (w)) + { + *out_start = 0; + return @""; + } - --- (void)dealloc --{ -- NSTRACE ("[EmacsView dealloc]"); ++ + struct buffer *b = XBUFFER (w->contents); + if (!b) + { + *out_start = 0; + return @""; + } - -- /* Clear the view resize notification. */ -- [[NSNotificationCenter defaultCenter] -- removeObserver:self -- name:NSViewFrameDidChangeNotification -- object:nil]; ++ + ptrdiff_t begv = BUF_BEGV (b); + ptrdiff_t zv = BUF_ZV (b); - -- if (fs_state == FULLSCREEN_BOTH) -- [nonfs_window release]; ++ + *out_start = begv; - --#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MIN_REQUIRED >= 101400 -- /* Release layer and menu */ -- EmacsLayer *layer = (EmacsLayer *)[self layer]; -- [layer release]; --#endif ++ + if (zv <= begv) + return @""; - -- [[self menu] release]; -- [super dealloc]; --} ++ + struct buffer *oldb = current_buffer; + if (b != current_buffer) + set_buffer_internal_1 (b); - ++ + /* First pass: count visible runs to allocate the mapping array. */ + NSUInteger run_capacity = 64; + ns_ax_visible_run *runs = xmalloc (run_capacity + * sizeof (ns_ax_visible_run)); + NSUInteger nruns = 0; + NSUInteger ax_offset = 0; - --/* Called on font panel selection. */ --- (void) changeFont: (id) sender --{ -- struct font *font = FRAME_OUTPUT_DATA (emacsframe)->font; -- NSFont *nsfont; ++ + NSMutableString *result = [NSMutableString string]; + ptrdiff_t pos = begv; - --#ifdef NS_IMPL_GNUSTEP -- nsfont = ((struct nsfont_info *) font)->nsfont; --#else -- nsfont = (NSFont *) macfont_get_nsctfont (font); --#endif ++ + while (pos < zv) + { + /* Check invisible property (text properties + overlays). */ @@ -279,16 +244,12 @@ index 932d209..e7af9a3 100644 + pos = FIXNUMP (next) ? XFIXNUM (next) : zv; + continue; + } - -- if (!font_panel_active) -- return; ++ + /* Find end of this visible run: where invisible property changes. */ + Lisp_Object next = Fnext_single_char_property_change ( + make_fixnum (pos), Qinvisible, Qnil, make_fixnum (zv)); + ptrdiff_t run_end = FIXNUMP (next) ? XFIXNUM (next) : zv; - -- if (font_panel_result) -- [font_panel_result release]; ++ + /* Cap total text at NS_AX_TEXT_CAP. */ + ptrdiff_t run_len = run_end - pos; + if (ax_offset + (NSUInteger) run_len > NS_AX_TEXT_CAP) @@ -319,54 +280,41 @@ index 932d209..e7af9a3 100644 + runs[nruns].ax_start = ax_offset; + runs[nruns].ax_length = ns_len; + nruns++; - -- font_panel_result = (NSFont *) [sender convertFont: nsfont]; ++ + ax_offset += ns_len; + pos = run_end; + } - -- if (font_panel_result) -- [font_panel_result retain]; ++ + if (b != oldb) + set_buffer_internal_1 (oldb); - --#ifndef NS_IMPL_COCOA -- font_panel_active = NO; -- [NSApp stop: self]; --#endif ++ + *out_runs = runs; + *out_nruns = nruns; + return result; } --#ifdef NS_IMPL_COCOA --- (void) noteUserSelectedFont +-/* Needed to inform when window closed from lisp. */ +-- (void) setWindowClosing: (BOOL)closing + +/* ---- Helper: extract mode line text from glyph rows ---- */ + +static NSString * +ns_ax_mode_line_text (struct window *w) { -- font_panel_active = NO; +- NSTRACE ("[EmacsView setWindowClosing:%d]", closing); + if (!w || !w->current_matrix) + return @""; -- /* If no font was previously selected, use the currently selected -- font. */ +- windowClosing = closing; + struct glyph_matrix *matrix = w->current_matrix; + NSMutableString *text = [NSMutableString string]; - -- if (!font_panel_result && FRAME_FONT (emacsframe)) ++ + for (int i = 0; i < matrix->nrows; i++) - { -- font_panel_result -- = macfont_get_nsctfont (FRAME_FONT (emacsframe)); ++ { + struct glyph_row *row = matrix->rows + i; + if (!row->enabled_p || !row->mode_line_p) + continue; - -- if (font_panel_result) -- [font_panel_result retain]; ++ + struct glyph *g = row->glyphs[TEXT_AREA]; + struct glyph *end = g + row->used[TEXT_AREA]; + for (; g < end; g++) @@ -378,36 +326,42 @@ index 932d209..e7af9a3 100644 + length:1]]; + } + } - } -- -- [NSApp stop: self]; ++ } + return text; } --- (void) noteUserCancelledSelection -+ + +-- (void)dealloc +/* ---- Helper: screen rect for a character range via glyph matrix ---- */ + +static NSRect +ns_ax_frame_for_range (struct window *w, EmacsView *view, + ptrdiff_t text_start, NSRange range) { -- font_panel_active = NO; +- NSTRACE ("[EmacsView dealloc]"); + if (!w || !w->current_matrix || !view) + return NSZeroRect; -- if (font_panel_result) -- [font_panel_result release]; -- font_panel_result = nil; +- /* Clear the view resize notification. */ +- [[NSNotificationCenter defaultCenter] +- removeObserver:self +- name:NSViewFrameDidChangeNotification +- object:nil]; + /* Convert range indices back to buffer charpos. */ + ptrdiff_t cp_start = text_start + (ptrdiff_t) range.location; + ptrdiff_t cp_end = cp_start + (ptrdiff_t) range.length; -- [NSApp stop: self]; +- if (fs_state == FULLSCREEN_BOTH) +- [nonfs_window release]; + struct glyph_matrix *matrix = w->current_matrix; + NSRect result = NSZeroRect; + BOOL found = NO; -+ + +-#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MIN_REQUIRED >= 101400 +- /* Release layer and menu */ +- EmacsLayer *layer = (EmacsLayer *)[self layer]; +- [layer release]; +-#endif + for (int i = 0; i < matrix->nrows; i++) + { + struct glyph_row *row = matrix->rows + i; @@ -415,7 +369,9 @@ index 932d209..e7af9a3 100644 + continue; + if (!row->displays_text_p && !row->ends_at_zv_p) + continue; -+ + +- [[self menu] release]; +- [super dealloc]; + ptrdiff_t row_start = MATRIX_ROW_START_CHARPOS (row); + ptrdiff_t row_end = MATRIX_ROW_END_CHARPOS (row); + @@ -459,9 +415,7 @@ index 932d209..e7af9a3 100644 + NSRect winRect = [view convertRect:result toView:nil]; + return [[view window] convertRectToScreen:winRect]; } --#endif --- (Lisp_Object) showFontPanel +/* AX enum numeric compatibility for NSAccessibility notifications. + Values match WebKit AXObjectCacheMac fallback enums + (AXTextStateChangeType / AXTextEditType / AXTextSelectionDirection / @@ -470,86 +424,75 @@ index 932d209..e7af9a3 100644 + ns_ax_text_state_change_unknown = 0, + ns_ax_text_state_change_edit = 1, + ns_ax_text_state_change_selection_move = 2, -+ -+ ns_ax_text_edit_type_typing = 3, -+ -+ ns_ax_text_selection_direction_unknown = 0, -+ ns_ax_text_selection_direction_previous = 3, -+ ns_ax_text_selection_direction_next = 4, -+ ns_ax_text_selection_direction_discontiguous = 5, -+ -+ ns_ax_text_selection_granularity_unknown = 0, -+ ns_ax_text_selection_granularity_character = 1, -+ ns_ax_text_selection_granularity_line = 3, -+}; -+ -+static NSUInteger -+ns_ax_utf16_length_for_buffer_range (struct buffer *b, ptrdiff_t start, -+ ptrdiff_t end) - { -- id fm = [NSFontManager sharedFontManager]; + +-/* Called on font panel selection. */ +-- (void) changeFont: (id) sender +-{ - struct font *font = FRAME_OUTPUT_DATA (emacsframe)->font; -- NSFont *nsfont, *result; -- struct timespec timeout; --#ifdef NS_IMPL_COCOA -- NSView *buttons; -- BOOL canceled; --#endif -+ if (!b || end <= start) -+ return 0; +- NSFont *nsfont; ++ ns_ax_text_edit_type_typing = 3, -#ifdef NS_IMPL_GNUSTEP - nsfont = ((struct nsfont_info *) font)->nsfont; -#else - nsfont = (NSFont *) macfont_get_nsctfont (font); -#endif ++ ns_ax_text_selection_direction_unknown = 0, ++ ns_ax_text_selection_direction_previous = 3, ++ ns_ax_text_selection_direction_next = 4, ++ ns_ax_text_selection_direction_discontiguous = 5, + +- if (!font_panel_active) +- return; ++ ns_ax_text_selection_granularity_unknown = 0, ++ ns_ax_text_selection_granularity_character = 1, ++ ns_ax_text_selection_granularity_line = 3, ++}; + +- if (font_panel_result) +- [font_panel_result release]; ++static NSUInteger ++ns_ax_utf16_length_for_buffer_range (struct buffer *b, ptrdiff_t start, ++ ptrdiff_t end) ++{ ++ if (!b || end <= start) ++ return 0; + +- font_panel_result = (NSFont *) [sender convertFont: nsfont]; + struct buffer *oldb = current_buffer; + if (b != current_buffer) + set_buffer_internal_1 (b); --#ifdef NS_IMPL_COCOA -- buttons -- = ns_create_font_panel_buttons (self, -- @selector (noteUserSelectedFont), -- @selector (noteUserCancelledSelection)); -- [[fm fontPanel: YES] setAccessoryView: buttons]; -- [buttons release]; --#endif +- if (font_panel_result) +- [font_panel_result retain]; + Lisp_Object lstr = Fbuffer_substring_no_properties (make_fixnum (start), + make_fixnum (end)); + NSString *nsstr = [NSString stringWithLispString:lstr]; + NSUInteger len = [nsstr length]; -- [fm setSelectedFont: nsfont isMultiple: NO]; -- [fm orderFrontFontPanel: NSApp]; +-#ifndef NS_IMPL_COCOA +- font_panel_active = NO; +- [NSApp stop: self]; +-#endif + if (b != oldb) + set_buffer_internal_1 (oldb); - -- font_panel_active = YES; -- timeout = make_timespec (0, 100000000); ++ + return len; -+} + } -- block_input (); -- while (font_panel_active -#ifdef NS_IMPL_COCOA -- && (canceled = [[fm fontPanel: YES] isVisible]) --#else -- && [[fm fontPanel: YES] isVisible] --#endif -- ) -- ns_select_1 (0, NULL, NULL, NULL, &timeout, NULL, YES); -- unblock_input (); +-- (void) noteUserSelectedFont +static BOOL +ns_ax_find_completion_overlay_range (struct buffer *b, ptrdiff_t point, + ptrdiff_t *out_start, + ptrdiff_t *out_end) -+{ + { +- font_panel_active = NO; + if (!b || !out_start || !out_end) + return NO; -- if (font_panel_result) -- [font_panel_result autorelease]; +- /* If no font was previously selected, use the currently selected +- font. */ + Lisp_Object faceSym = intern ("completions-highlight"); + ptrdiff_t begv = BUF_BEGV (b); + ptrdiff_t zv = BUF_ZV (b); @@ -558,13 +501,20 @@ index 932d209..e7af9a3 100644 + ptrdiff_t best_dist = PTRDIFF_MAX; + BOOL found = NO; --#ifdef NS_IMPL_COCOA -- if (!canceled) -- font_panel_result = nil; --#endif -+ for (ptrdiff_t scan = begv; scan < zv; scan++) -+ { -+ Lisp_Object overlays = Foverlays_at (make_fixnum (scan), Qnil); +- if (!font_panel_result && FRAME_FONT (emacsframe)) ++ /* Fast path: look at point and immediate neighbors first. */ ++ ptrdiff_t probes[3] = { point, point - 1, point + 1 }; ++ for (int i = 0; i < 3 && !found; i++) + { +- font_panel_result +- = macfont_get_nsctfont (FRAME_FONT (emacsframe)); ++ ptrdiff_t p = probes[i]; ++ if (p < begv || p > zv) ++ continue; + +- if (font_panel_result) +- [font_panel_result retain]; ++ Lisp_Object overlays = Foverlays_at (make_fixnum (p), Qnil); + Lisp_Object tail; + for (tail = overlays; CONSP (tail); tail = XCDR (tail)) + { @@ -579,49 +529,180 @@ index 932d209..e7af9a3 100644 + if (ov_end <= ov_start) + continue; + -+ ptrdiff_t dist = 0; -+ if (point < ov_start) -+ dist = ov_start - point; -+ else if (point > ov_end) -+ dist = point - ov_end; -+ -+ if (!found || dist < best_dist -+ || (dist == best_dist -+ && (ov_start < point && best_start >= point))) ++ best_start = ov_start; ++ best_end = ov_end; ++ best_dist = 0; ++ found = YES; ++ break; ++ } + } + +- [NSApp stop: self]; +-} +- +-- (void) noteUserCancelledSelection +-{ +- font_panel_active = NO; ++ if (!found) ++ { ++ for (ptrdiff_t scan = begv; scan < zv; scan++) ++ { ++ Lisp_Object overlays = Foverlays_at (make_fixnum (scan), Qnil); ++ Lisp_Object tail; ++ for (tail = overlays; CONSP (tail); tail = XCDR (tail)) + { -+ best_start = ov_start; -+ best_end = ov_end; -+ best_dist = dist; -+ found = YES; ++ Lisp_Object ov = XCAR (tail); ++ Lisp_Object face = Foverlay_get (ov, Qface); ++ if (!(EQ (face, faceSym) ++ || (CONSP (face) && !NILP (Fmemq (faceSym, face))))) ++ continue; ++ ++ ptrdiff_t ov_start = OVERLAY_START (ov); ++ ptrdiff_t ov_end = OVERLAY_END (ov); ++ if (ov_end <= ov_start) ++ continue; ++ ++ ptrdiff_t dist = 0; ++ if (point < ov_start) ++ dist = ov_start - point; ++ else if (point > ov_end) ++ dist = point - ov_end; ++ ++ if (!found || dist < best_dist ++ || (dist == best_dist ++ && (ov_start < point && best_start >= point))) ++ { ++ best_start = ov_start; ++ best_end = ov_end; ++ best_dist = dist; ++ found = YES; ++ } + } + } + } -- result = font_panel_result; +- if (font_panel_result) +- [font_panel_result release]; - font_panel_result = nil; + if (!found) + return NO; -- [[fm fontPanel: YES] setIsVisible: NO]; -- font_panel_active = NO; +- [NSApp stop: self]; + *out_start = best_start; + *out_end = best_end; + return YES; + } +-#endif + +-- (Lisp_Object) showFontPanel ++static NSString * ++ns_ax_completion_text_for_span (EmacsAccessibilityBuffer *elem, ++ struct buffer *b, ++ ptrdiff_t start, ++ ptrdiff_t end, ++ NSString *cachedText) + { +- id fm = [NSFontManager sharedFontManager]; +- struct font *font = FRAME_OUTPUT_DATA (emacsframe)->font; +- NSFont *nsfont, *result; +- struct timespec timeout; +-#ifdef NS_IMPL_COCOA +- NSView *buttons; +- BOOL canceled; +-#endif +- +-#ifdef NS_IMPL_GNUSTEP +- nsfont = ((struct nsfont_info *) font)->nsfont; +-#else +- nsfont = (NSFont *) macfont_get_nsctfont (font); +-#endif ++ if (!elem || !b || !cachedText || end <= start) ++ return nil; + +-#ifdef NS_IMPL_COCOA +- buttons +- = ns_create_font_panel_buttons (self, +- @selector (noteUserSelectedFont), +- @selector (noteUserCancelledSelection)); +- [[fm fontPanel: YES] setAccessoryView: buttons]; +- [buttons release]; +-#endif ++ NSString *text = nil; ++ struct buffer *oldb = current_buffer; ++ if (b != current_buffer) ++ set_buffer_internal_1 (b); + +- [fm setSelectedFont: nsfont isMultiple: NO]; +- [fm orderFrontFontPanel: NSApp]; ++ /* Prefer canonical completion candidate string from text property. */ ++ ptrdiff_t probes[2] = { start, end - 1 }; ++ for (int i = 0; i < 2 && !text; i++) ++ { ++ ptrdiff_t p = probes[i]; ++ Lisp_Object cstr = Fget_char_property (make_fixnum (p), ++ intern ("completion--string"), ++ Qnil); ++ if (STRINGP (cstr)) ++ text = [NSString stringWithLispString:cstr]; ++ } + +- font_panel_active = YES; +- timeout = make_timespec (0, 100000000); ++ if (!text) ++ { ++ NSUInteger ax_s = [elem accessibilityIndexForCharpos:start]; ++ NSUInteger ax_e = [elem accessibilityIndexForCharpos:end]; ++ if (ax_e > ax_s && ax_e <= [cachedText length]) ++ text = [cachedText substringWithRange:NSMakeRange (ax_s, ax_e - ax_s)]; ++ } + +- block_input (); +- while (font_panel_active +-#ifdef NS_IMPL_COCOA +- && (canceled = [[fm fontPanel: YES] isVisible]) +-#else +- && [[fm fontPanel: YES] isVisible] +-#endif +- ) +- ns_select_1 (0, NULL, NULL, NULL, &timeout, NULL, YES); +- unblock_input (); ++ if (b != oldb) ++ set_buffer_internal_1 (oldb); + +- if (font_panel_result) +- [font_panel_result autorelease]; ++ if (text) ++ { ++ text = [text stringByTrimmingCharactersInSet: ++ [NSCharacterSet whitespaceAndNewlineCharacterSet]]; ++ if ([text length] == 0) ++ text = nil; ++ } + +-#ifdef NS_IMPL_COCOA +- if (!canceled) +- font_panel_result = nil; +-#endif ++ return text; +} +- result = font_panel_result; +- font_panel_result = nil; + +- [[fm fontPanel: YES] setIsVisible: NO]; +- font_panel_active = NO; ++@implementation EmacsAccessibilityElement + - if (result) - return ns_font_desc_to_font_spec ([result fontDescriptor], - result); - -- return Qnil; -+@implementation EmacsAccessibilityElement -+ +- (NSRect)screenRectFromEmacsX:(int)x y:(int)y width:(int)ew height:(int)eh +{ + EmacsView *view = self.emacsView; + if (!view || ![view window]) + return NSZeroRect; -+ + +- return Qnil; + NSRect r = NSMakeRect (x, y, ew, eh); + NSRect winRect = [view convertRect:r toView:nil]; + return [[view window] convertRectToScreen:winRect]; @@ -684,6 +765,7 @@ index 932d209..e7af9a3 100644 +@synthesize cachedCompletionAnnouncement; +@synthesize cachedCompletionOverlayStart; +@synthesize cachedCompletionOverlayEnd; ++@synthesize cachedCompletionPoint; +- (void)dealloc +{ @@ -705,8 +787,6 @@ index 932d209..e7af9a3 100644 - Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe); - int code; - unsigned fnKeysym = 0; -- static NSMutableArray *nsEvArray; -- unsigned int flags = [theEvent modifierFlags]; + [cachedText release]; + cachedText = nil; + if (visibleRuns) @@ -716,21 +796,17 @@ index 932d209..e7af9a3 100644 + } + visibleRunCount = 0; +} - -- NSTRACE ("[EmacsView keyDown:]"); ++ +- (void)ensureTextCache +{ + struct window *w = self.emacsWindow; + if (!w || !WINDOW_LEAF_P (w)) + return; - -- /* Rhapsody and macOS give up and down events for the arrow keys. */ -- if ([theEvent type] != NSEventTypeKeyDown) ++ + struct buffer *b = XBUFFER (w->contents); + if (!b) - return; - -- if (!emacs_event) ++ return; ++ + ptrdiff_t modiff = BUF_MODIFF (b); + ptrdiff_t pt = BUF_PT (b); + NSUInteger textLen = cachedText ? [cachedText length] : 0; @@ -738,16 +814,8 @@ index 932d209..e7af9a3 100644 + && pt >= cachedTextStart + && (textLen == 0 + || [self accessibilityIndexForCharpos:pt] <= textLen)) - return; - -- if (![[self window] isKeyWindow] -- && [[theEvent window] isKindOfClass: [EmacsWindow class]] -- /* We must avoid an infinite loop here. */ -- && (EmacsView *)[[theEvent window] delegate] != self) -- { -- /* XXX: There is an occasional condition in which, when Emacs display -- updates a different frame from the current one, and temporarily -- selects it, then processes some interrupt-driven input ++ return; ++ + ptrdiff_t start; + ns_ax_visible_run *runs = NULL; + NSUInteger nruns = 0; @@ -1470,18 +1538,10 @@ index 932d209..e7af9a3 100644 + ptrdiff_t ov_end = OVERLAY_END (ov); + if (ov_end > ov_start) + { -+ NSUInteger ax_idx = [self accessibilityIndexForCharpos: -+ ov_start]; -+ if (ax_idx <= [cachedText length]) -+ { -+ NSInteger lineNum = [self accessibilityLineForIndex:ax_idx]; -+ NSRange lineRange = [self accessibilityRangeForLine:lineNum]; -+ if (lineRange.location != NSNotFound -+ && lineRange.length > 0 -+ && lineRange.location + lineRange.length -+ <= [cachedText length]) -+ announceText = [cachedText substringWithRange:lineRange]; -+ } ++ announceText = ns_ax_completion_text_for_span (self, b, ++ ov_start, ++ ov_end, ++ cachedText); + currentOverlayStart = ov_start; + currentOverlayEnd = ov_end; + } @@ -1498,17 +1558,10 @@ index 932d209..e7af9a3 100644 + ptrdiff_t ov_end = 0; + if (ns_ax_find_completion_overlay_range (b, point, &ov_start, &ov_end)) + { -+ NSUInteger ax_idx = [self accessibilityIndexForCharpos:ov_start]; -+ if (ax_idx <= [cachedText length]) -+ { -+ NSInteger lineNum = [self accessibilityLineForIndex:ax_idx]; -+ NSRange lineRange = [self accessibilityRangeForLine:lineNum]; -+ if (lineRange.location != NSNotFound -+ && lineRange.length > 0 -+ && lineRange.location + lineRange.length -+ <= [cachedText length]) -+ announceText = [cachedText substringWithRange:lineRange]; -+ } ++ announceText = ns_ax_completion_text_for_span (self, b, ++ ov_start, ++ ov_end, ++ cachedText); + currentOverlayStart = ov_start; + currentOverlayEnd = ov_end; + } @@ -1545,7 +1598,8 @@ index 932d209..e7af9a3 100644 + BOOL overlayChanged = + (currentOverlayStart != self.cachedCompletionOverlayStart + || currentOverlayEnd != self.cachedCompletionOverlayEnd); -+ if (textChanged || overlayChanged) ++ BOOL pointChanged = (point != self.cachedCompletionPoint); ++ if (textChanged || overlayChanged || pointChanged) + { + NSDictionary *annInfo = @{ + NSAccessibilityAnnouncementKey: announceText, @@ -1560,12 +1614,14 @@ index 932d209..e7af9a3 100644 + self.cachedCompletionAnnouncement = announceText; + self.cachedCompletionOverlayStart = currentOverlayStart; + self.cachedCompletionOverlayEnd = currentOverlayEnd; ++ self.cachedCompletionPoint = point; + } + else + { + self.cachedCompletionAnnouncement = nil; + self.cachedCompletionOverlayStart = 0; + self.cachedCompletionOverlayEnd = 0; ++ self.cachedCompletionPoint = 0; + } + } + else @@ -1573,6 +1629,7 @@ index 932d209..e7af9a3 100644 + self.cachedCompletionAnnouncement = nil; + self.cachedCompletionOverlayStart = 0; + self.cachedCompletionOverlayEnd = 0; ++ self.cachedCompletionPoint = 0; + } + } + @@ -1584,6 +1641,7 @@ index 932d209..e7af9a3 100644 + self.cachedCompletionAnnouncement = nil; + self.cachedCompletionOverlayStart = 0; + self.cachedCompletionOverlayEnd = 0; ++ self.cachedCompletionPoint = 0; + } + else + { @@ -1601,18 +1659,10 @@ index 932d209..e7af9a3 100644 + ¤tOverlayStart, + ¤tOverlayEnd)) + { -+ NSUInteger ax_idx = [self accessibilityIndexForCharpos: -+ currentOverlayStart]; -+ if (ax_idx <= [cachedText length]) -+ { -+ NSInteger lineNum = [self accessibilityLineForIndex:ax_idx]; -+ NSRange lineRange = [self accessibilityRangeForLine:lineNum]; -+ if (lineRange.location != NSNotFound -+ && lineRange.length > 0 -+ && lineRange.location + lineRange.length -+ <= [cachedText length]) -+ announceText = [cachedText substringWithRange:lineRange]; -+ } ++ announceText = ns_ax_completion_text_for_span (self, b, ++ currentOverlayStart, ++ currentOverlayEnd, ++ cachedText); + } + + if (b != oldb2) @@ -1629,7 +1679,8 @@ index 932d209..e7af9a3 100644 + BOOL overlayChanged = + (currentOverlayStart != self.cachedCompletionOverlayStart + || currentOverlayEnd != self.cachedCompletionOverlayEnd); -+ if (textChanged || overlayChanged) ++ BOOL pointChanged = (point != self.cachedCompletionPoint); ++ if (textChanged || overlayChanged || pointChanged) + { + NSDictionary *annInfo = @{ + NSAccessibilityAnnouncementKey: announceText, @@ -1644,12 +1695,14 @@ index 932d209..e7af9a3 100644 + self.cachedCompletionAnnouncement = announceText; + self.cachedCompletionOverlayStart = currentOverlayStart; + self.cachedCompletionOverlayEnd = currentOverlayEnd; ++ self.cachedCompletionPoint = point; + } + else + { + self.cachedCompletionAnnouncement = nil; + self.cachedCompletionOverlayStart = 0; + self.cachedCompletionOverlayEnd = 0; ++ self.cachedCompletionPoint = 0; + } + } + else @@ -1657,6 +1710,7 @@ index 932d209..e7af9a3 100644 + self.cachedCompletionAnnouncement = nil; + self.cachedCompletionOverlayStart = 0; + self.cachedCompletionOverlayEnd = 0; ++ self.cachedCompletionPoint = 0; + } + } + } @@ -1945,30 +1999,10 @@ index 932d209..e7af9a3 100644 + Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe); + int code; + unsigned fnKeysym = 0; -+ static NSMutableArray *nsEvArray; -+ unsigned int flags = [theEvent modifierFlags]; -+ -+ NSTRACE ("[EmacsView keyDown:]"); -+ -+ /* Rhapsody and macOS give up and down events for the arrow keys. */ -+ if ([theEvent type] != NSEventTypeKeyDown) -+ return; -+ -+ if (!emacs_event) -+ return; -+ -+ if (![[self window] isKeyWindow] -+ && [[theEvent window] isKindOfClass: [EmacsWindow class]] -+ /* We must avoid an infinite loop here. */ -+ && (EmacsView *)[[theEvent window] delegate] != self) -+ { -+ /* XXX: There is an occasional condition in which, when Emacs display -+ updates a different frame from the current one, and temporarily -+ selects it, then processes some interrupt-driven input - (dispnew.c:3878), OS will send the event to the correct NSWindow, but - for some reason that window has its first responder set to the NSView - most recently updated (I guess), which is not the correct one. */ -@@ -8237,6 +9679,28 @@ ns_in_echo_area (void) + static NSMutableArray *nsEvArray; + unsigned int flags = [theEvent modifierFlags]; + +@@ -8237,6 +9749,28 @@ ns_in_echo_area (void) XSETFRAME (event.frame_or_window, emacsframe); kbd_buffer_store_event (&event); ns_send_appdefined (-1); // Kick main loop @@ -1997,7 +2031,7 @@ index 932d209..e7af9a3 100644 } -@@ -9474,6 +10938,298 @@ ns_in_echo_area (void) +@@ -9474,6 +11008,298 @@ ns_in_echo_area (void) return fs_state; } @@ -2296,7 +2330,7 @@ index 932d209..e7af9a3 100644 @end /* EmacsView */ -@@ -9941,6 +11697,14 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c) +@@ -9941,6 +11767,14 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c) return [super accessibilityAttributeValue:attribute]; }