From 25e2a21245f186acf57e1be6227c9e2fdf9a4363 Mon Sep 17 00:00:00 2001 From: Daneel Date: Thu, 26 Feb 2026 16:07:42 +0100 Subject: [PATCH] v15.7: fix buffer gap corruption in visible text extraction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause: ns_ax_buffer_text used BUF_BYTE_ADDRESS + raw pointer read which crosses the buffer gap when visible runs span it. The gap follows point, so completion cycling and dired navigation reliably trigger corruption — VoiceOver reads wrong text. Fix: Replace raw pointer extraction with Fbuffer_substring_no_properties which handles the gap internally. Add ax_length field to visible run struct for accurate UTF-16 length tracking (fixes supplementary Unicode character offset drift). Secondary: ax_offset accumulation now uses NSString length (UTF-16 units) instead of Emacs char count, preventing progressive drift in index mapping for subsequent visible runs. --- ...oundsForRange-for-macOS-Zoom-cursor-.patch | 658 +++--------------- 1 file changed, 112 insertions(+), 546 deletions(-) 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 564b668..d419ec8 100644 --- a/patches/0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch +++ b/patches/0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch @@ -1,8 +1,8 @@ diff --git a/src/nsterm.h b/src/nsterm.h -index 7c1ee4c..855d302 100644 +index 7c1ee4c..2e2c80f 100644 --- a/src/nsterm.h +++ b/src/nsterm.h -@@ -453,6 +453,57 @@ enum ns_return_frame_mode +@@ -453,6 +453,58 @@ enum ns_return_frame_mode @end @@ -29,8 +29,9 @@ index 7c1ee4c..855d302 100644 +typedef struct ns_ax_visible_run +{ + ptrdiff_t charpos; /* Buffer charpos where this visible run starts. */ -+ ptrdiff_t length; /* Number of visible characters in this run. */ ++ ptrdiff_t length; /* Number of visible Emacs characters in this run. */ + NSUInteger ax_start; /* Starting index in the accessibility string. */ ++ NSUInteger ax_length; /* Length in accessibility string (UTF-16 units). */ +} ns_ax_visible_run; + +/* Virtual AXTextArea element — one per visible Emacs window (buffer). */ @@ -60,7 +61,7 @@ index 7c1ee4c..855d302 100644 /* ========================================================================== The main Emacs view -@@ -471,6 +522,14 @@ enum ns_return_frame_mode +@@ -471,6 +523,14 @@ enum ns_return_frame_mode #ifdef NS_IMPL_COCOA char *old_title; BOOL maximizing_resize; @@ -75,7 +76,7 @@ index 7c1ee4c..855d302 100644 #endif BOOL font_panel_active; NSFont *font_panel_result; -@@ -528,6 +587,13 @@ enum ns_return_frame_mode +@@ -528,6 +588,13 @@ enum ns_return_frame_mode - (void)windowWillExitFullScreen; - (void)windowDidExitFullScreen; - (void)windowDidBecomeKey; @@ -90,7 +91,7 @@ index 7c1ee4c..855d302 100644 diff --git a/src/nsterm.m b/src/nsterm.m -index 932d209..ca6b969 100644 +index 932d209..043477b 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -1104,6 +1104,11 @@ ns_update_end (struct frame *f) @@ -143,29 +144,23 @@ index 932d209..ca6b969 100644 ns_focus (f, NULL, 0); NSGraphicsContext *ctx = [NSGraphicsContext currentContext]; -@@ -6849,265 +6885,1333 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action) +@@ -6847,6 +6883,1074 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action) + } + #endif - /* ========================================================================== - -- EmacsView implementation ++/* ========================================================================== ++ + Accessibility virtual elements (macOS / Cocoa only) - - ========================================================================== */ - ++ ++ ========================================================================== */ ++ +#ifdef NS_IMPL_COCOA - --@implementation EmacsView ++ +/* ---- 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 ++ +/* 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 @@ -174,71 +169,45 @@ index 932d209..ca6b969 100644 +static NSString * +ns_ax_buffer_text (struct window *w, ptrdiff_t *out_start, + ns_ax_visible_run **out_runs, NSUInteger *out_nruns) - { -- NSTRACE ("[EmacsView setWindowClosing:%d]", closing); ++{ + *out_runs = NULL; + *out_nruns = 0; - -- windowClosing = closing; --} ++ + if (!w || !WINDOW_LEAF_P (w)) + { + *out_start = 0; + return @""; + } - ++ + struct buffer *b = XBUFFER (w->contents); + if (!b) + { + *out_start = 0; + return @""; + } - --- (void)dealloc --{ -- NSTRACE ("[EmacsView dealloc]"); ++ + ptrdiff_t begv = BUF_BEGV (b); + ptrdiff_t zv = BUF_ZV (b); - -- /* Clear the view resize notification. */ -- [[NSNotificationCenter defaultCenter] -- removeObserver:self -- name:NSViewFrameDidChangeNotification -- object:nil]; ++ + *out_start = begv; - -- if (fs_state == FULLSCREEN_BOTH) -- [nonfs_window release]; ++ + if (zv <= begv) + return @""; - --#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MIN_REQUIRED >= 101400 -- /* Release layer and menu */ -- EmacsLayer *layer = (EmacsLayer *)[self layer]; -- [layer release]; --#endif ++ + struct buffer *oldb = current_buffer; + if (b != current_buffer) + set_buffer_internal_1 (b); - -- [[self menu] release]; -- [super dealloc]; --} ++ + /* 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; - ++ + NSMutableString *result = [NSMutableString string]; + ptrdiff_t pos = begv; - --/* Called on font panel selection. */ --- (void) changeFont: (id) sender --{ -- struct font *font = FRAME_OUTPUT_DATA (emacsframe)->font; -- NSFont *nsfont; ++ + while (pos < zv) + { + /* Check invisible property (text properties + overlays). */ @@ -258,19 +227,12 @@ index 932d209..ca6b969 100644 + pos = FIXNUMP (next) ? XFIXNUM (next) : zv; + continue; + } - --#ifdef NS_IMPL_GNUSTEP -- nsfont = ((struct nsfont_info *) font)->nsfont; --#else -- nsfont = (NSFont *) macfont_get_nsctfont (font); --#endif ++ + /* 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_active) -- return; ++ + /* 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) @@ -278,22 +240,17 @@ index 932d209..ca6b969 100644 + if (run_len <= 0) + break; + run_end = pos + run_len; - -- if (font_panel_result) -- [font_panel_result release]; -+ /* Extract this visible run's text. */ -+ ptrdiff_t pos_byte = buf_charpos_to_bytepos (b, pos); -+ ptrdiff_t end_byte = buf_charpos_to_bytepos (b, run_end); -+ unsigned char *data = BUF_BYTE_ADDRESS (b, pos_byte); -+ ptrdiff_t nbytes = end_byte - pos_byte; - -- font_panel_result = (NSFont *) [sender convertFont: nsfont]; -+ Lisp_Object lstr = make_string_from_bytes ((char *) data, -+ run_len, nbytes); -+ [result appendString:[NSString stringWithLispString:lstr]]; - -- if (font_panel_result) -- [font_panel_result retain]; ++ ++ /* Extract this visible run's text. Use ++ Fbuffer_substring_no_properties which correctly handles the ++ buffer gap — raw BUF_BYTE_ADDRESS reads across the gap would ++ include garbage bytes when the run spans the gap position. */ ++ Lisp_Object lstr = Fbuffer_substring_no_properties ( ++ make_fixnum (pos), make_fixnum (run_end)); ++ NSString *nsstr = [NSString stringWithLispString:lstr]; ++ NSUInteger ns_len = [nsstr length]; ++ [result appendString:nsstr]; ++ + /* Record this visible run in the mapping. */ + if (nruns >= run_capacity) + { @@ -304,13 +261,10 @@ index 932d209..ca6b969 100644 + runs[nruns].charpos = pos; + runs[nruns].length = run_len; + runs[nruns].ax_start = ax_offset; ++ runs[nruns].ax_length = ns_len; + nruns++; - --#ifndef NS_IMPL_COCOA -- font_panel_active = NO; -- [NSApp stop: self]; --#endif -+ ax_offset += (NSUInteger) run_len; ++ ++ ax_offset += ns_len; + pos = run_end; + } + @@ -320,36 +274,26 @@ index 932d209..ca6b969 100644 + *out_runs = runs; + *out_nruns = nruns; + return result; - } - --#ifdef NS_IMPL_COCOA --- (void) noteUserSelectedFont ++} ++ + +/* ---- Helper: extract mode line text from glyph rows ---- */ + +static NSString * +ns_ax_mode_line_text (struct window *w) - { -- font_panel_active = NO; ++{ + if (!w || !w->current_matrix) + return @""; - -- /* If no font was previously selected, use the currently selected -- font. */ ++ + 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++) @@ -361,64 +305,28 @@ index 932d209..ca6b969 100644 + length:1]]; + } + } - } -- -- [NSApp stop: self]; ++ } + return text; - } - --- (void) noteUserCancelledSelection --{ -- font_panel_active = NO; -- -- if (font_panel_result) -- [font_panel_result release]; -- font_panel_result = nil; - -- [NSApp stop: self]; --} --#endif ++} ++ ++ +/* ---- Helper: screen rect for a character range via glyph matrix ---- */ - --- (Lisp_Object) showFontPanel ++ +static NSRect +ns_ax_frame_for_range (struct window *w, EmacsView *view, + ptrdiff_t text_start, NSRange range) - { -- 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 ++{ + if (!w || !w->current_matrix || !view) + return NSZeroRect; - --#ifdef NS_IMPL_GNUSTEP -- nsfont = ((struct nsfont_info *) font)->nsfont; --#else -- nsfont = (NSFont *) macfont_get_nsctfont (font); --#endif ++ + /* 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; - --#ifdef NS_IMPL_COCOA -- buttons -- = ns_create_font_panel_buttons (self, -- @selector (noteUserSelectedFont), -- @selector (noteUserCancelledSelection)); -- [[fm fontPanel: YES] setAccessoryView: buttons]; -- [buttons release]; --#endif ++ + struct glyph_matrix *matrix = w->current_matrix; + NSRect result = NSZeroRect; + BOOL found = NO; - -- [fm setSelectedFont: nsfont isMultiple: NO]; -- [fm orderFrontFontPanel: NSApp]; ++ + for (int i = 0; i < matrix->nrows; i++) + { + struct glyph_row *row = matrix->rows + i; @@ -426,22 +334,10 @@ index 932d209..ca6b969 100644 + continue; + if (!row->displays_text_p && !row->ends_at_zv_p) + continue; - -- font_panel_active = YES; -- timeout = make_timespec (0, 100000000); ++ + ptrdiff_t row_start = MATRIX_ROW_START_CHARPOS (row); + ptrdiff_t row_end = MATRIX_ROW_END_CHARPOS (row); - -- 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 (row_start < cp_end && row_end > cp_start) + { + int window_x, window_y, window_width; @@ -464,16 +360,10 @@ index 932d209..ca6b969 100644 + result = NSUnionRect (result, rowRect); + } + } - -- if (font_panel_result) -- [font_panel_result autorelease]; ++ + if (!found) + return NSZeroRect; - --#ifdef NS_IMPL_COCOA -- if (!canceled) -- font_panel_result = nil; --#endif ++ + /* Clip result to text area bounds. */ + { + int text_area_x, text_area_y, text_area_w, text_area_h; @@ -483,23 +373,15 @@ index 932d209..ca6b969 100644 + if (NSMaxY (result) > max_y) + result.size.height = max_y - result.origin.y; + } - -- result = font_panel_result; -- font_panel_result = nil; ++ + /* Convert from EmacsView (flipped) coords to screen coords. */ + NSRect winRect = [view convertRect:result toView:nil]; + return [[view window] convertRectToScreen:winRect]; +} - -- [[fm fontPanel: YES] setIsVisible: NO]; -- font_panel_active = NO; - -- if (result) -- return ns_font_desc_to_font_spec ([result fontDescriptor], -- result); ++ ++ +@implementation EmacsAccessibilityElement - -- return Qnil; ++ +- (NSRect)screenRectFromEmacsX:(int)x y:(int)y width:(int)ew height:(int)eh +{ + EmacsView *view = self.emacsView; @@ -509,55 +391,33 @@ index 932d209..ca6b969 100644 + NSRect r = NSMakeRect (x, y, ew, eh); + NSRect winRect = [view convertRect:r toView:nil]; + return [[view window] convertRectToScreen:winRect]; - } - --- (BOOL)acceptsFirstResponder ++} ++ +- (BOOL)isAccessibilityElement - { -- NSTRACE ("[EmacsView acceptsFirstResponder]"); - return YES; - } - --/* Tell NS we want to accept clicks that activate the window */ --- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent ++{ ++ return YES; ++} ++ +/* ---- Hierarchy plumbing (required for VoiceOver to find us) ---- */ + +- (id)accessibilityParent - { -- NSTRACE_MSG ("First mouse event: type=%ld, clickCount=%ld", -- [theEvent type], [theEvent clickCount]); -- return ns_click_through; ++{ + return NSAccessibilityUnignoredAncestor (self.emacsView); - } --- (void)resetCursorRects ++} + +- (id)accessibilityWindow - { -- NSRect visible = [self visibleRect]; -- NSCursor *currentCursor = FRAME_POINTER_TYPE (emacsframe); -- NSTRACE ("[EmacsView resetCursorRects]"); ++{ + return [self.emacsView window]; +} - -- if (currentCursor == nil) -- currentCursor = [NSCursor arrowCursor]; ++ +- (id)accessibilityTopLevelUIElement +{ + return [self.emacsView window]; +} - -- if (!NSIsEmptyRect (visible)) -- [self addCursorRect: visible cursor: currentCursor]; ++ +@end - --#if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 101300 --#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 -- if ([currentCursor respondsToSelector: @selector(setOnMouseEntered:)]) --#endif -- [currentCursor setOnMouseEntered: YES]; --#endif --} - ++ ++ +@implementation EmacsAccessibilityBuffer +@synthesize cachedText; +@synthesize cachedTextModiff; @@ -565,7 +425,7 @@ index 932d209..ca6b969 100644 +@synthesize cachedModiff; +@synthesize cachedPoint; +@synthesize cachedMarkActive; - ++ +- (void)dealloc +{ + [cachedText release]; @@ -573,20 +433,11 @@ index 932d209..ca6b969 100644 + xfree (visibleRuns); + [super dealloc]; +} - --/*****************************************************************************/ --/* Keyboard handling. */ --#define NS_KEYLOG 0 ++ +/* ---- Text cache ---- */ - --- (void)keyDown: (NSEvent *)theEvent ++ +- (void)invalidateTextCache - { -- 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) @@ -596,21 +447,17 @@ index 932d209..ca6b969 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; @@ -618,53 +465,31 @@ index 932d209..ca6b969 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 -- (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. */ -- [(EmacsView *)[[theEvent window] delegate] keyDown: theEvent]; -- return; -- } ++ return; ++ + ptrdiff_t start; + ns_ax_visible_run *runs = NULL; + NSUInteger nruns = 0; + NSString *text = ns_ax_buffer_text (w, &start, &runs, &nruns); - -- if (nsEvArray == nil) -- nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1]; ++ + [cachedText release]; + cachedText = [text retain]; + cachedTextModiff = modiff; + cachedTextStart = start; - -- [NSCursor setHiddenUntilMouseMoves:! NILP (Vmake_pointer_invisible)]; ++ + if (visibleRuns) + xfree (visibleRuns); + visibleRuns = runs; + visibleRunCount = nruns; +} - -- if (!hlinfo->mouse_face_hidden -- && FIXNUMP (Vmouse_highlight) -- && !EQ (emacsframe->tab_bar_window, hlinfo->mouse_face_window)) ++ +/* ---- Index mapping ---- */ + +/* Convert buffer charpos to accessibility string index. */ +- (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos +{ + for (NSUInteger i = 0; i < visibleRunCount; i++) - { -- clear_mouse_face (hlinfo); -- hlinfo->mouse_face_hidden = true; ++ { + ns_ax_visible_run *r = &visibleRuns[i]; + if (charpos >= r->charpos && charpos < r->charpos + r->length) + return r->ax_start + (NSUInteger) (charpos - r->charpos); @@ -672,17 +497,12 @@ index 932d209..ca6b969 100644 + map it to the start of the next visible run. */ + if (charpos < r->charpos) + return r->ax_start; - } -- -- if (!processingCompose) ++ } + /* Past end — return total length. */ + if (visibleRunCount > 0) - { -- /* FIXME: What should happen for key sequences with more than -- one character? */ -- code = ([[theEvent charactersIgnoringModifiers] length] == 0) ? ++ { + ns_ax_visible_run *last = &visibleRuns[visibleRunCount - 1]; -+ return last->ax_start + (NSUInteger) last->length; ++ return last->ax_start + last->ax_length; + } + return 0; +} @@ -694,7 +514,7 @@ index 932d209..ca6b969 100644 + { + ns_ax_visible_run *r = &visibleRuns[i]; + if (ax_idx >= r->ax_start -+ && ax_idx < r->ax_start + (NSUInteger) r->length) ++ && ax_idx < r->ax_start + r->ax_length) + return r->charpos + (ptrdiff_t) (ax_idx - r->ax_start); + } + /* Past end — return last charpos. */ @@ -1396,272 +1216,18 @@ index 932d209..ca6b969 100644 +#endif /* NS_IMPL_COCOA */ + + -+/* ========================================================================== -+ -+ EmacsView implementation -+ -+ ========================================================================== */ -+ -+ -+@implementation EmacsView -+ -+- (void)windowDidEndLiveResize:(NSNotification *)notification -+{ -+ [self updateFramePosition]; -+} -+ -+/* Needed to inform when window closed from lisp. */ -+- (void) setWindowClosing: (BOOL)closing -+{ -+ NSTRACE ("[EmacsView setWindowClosing:%d]", closing); -+ -+ windowClosing = closing; -+} -+ -+ -+- (void)dealloc -+{ -+ NSTRACE ("[EmacsView dealloc]"); -+ -+ /* Clear the view resize notification. */ -+ [[NSNotificationCenter defaultCenter] -+ removeObserver:self -+ name:NSViewFrameDidChangeNotification -+ object:nil]; -+ -+ if (fs_state == FULLSCREEN_BOTH) -+ [nonfs_window release]; -+ -+#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MIN_REQUIRED >= 101400 -+ /* Release layer and menu */ -+ EmacsLayer *layer = (EmacsLayer *)[self layer]; -+ [layer release]; -+#endif -+ -+ [accessibilityElements release]; -+ [[self menu] release]; -+ [super dealloc]; -+} -+ -+ -+/* Called on font panel selection. */ -+- (void) changeFont: (id) sender -+{ -+ struct font *font = FRAME_OUTPUT_DATA (emacsframe)->font; -+ NSFont *nsfont; -+ -+#ifdef NS_IMPL_GNUSTEP -+ nsfont = ((struct nsfont_info *) font)->nsfont; -+#else -+ nsfont = (NSFont *) macfont_get_nsctfont (font); -+#endif -+ -+ if (!font_panel_active) -+ return; -+ -+ if (font_panel_result) -+ [font_panel_result release]; -+ -+ font_panel_result = (NSFont *) [sender convertFont: nsfont]; -+ -+ if (font_panel_result) -+ [font_panel_result retain]; -+ -+#ifndef NS_IMPL_COCOA -+ font_panel_active = NO; -+ [NSApp stop: self]; -+#endif -+} -+ -+#ifdef NS_IMPL_COCOA -+- (void) noteUserSelectedFont -+{ -+ font_panel_active = NO; -+ -+ /* If no font was previously selected, use the currently selected -+ font. */ -+ -+ if (!font_panel_result && FRAME_FONT (emacsframe)) -+ { -+ font_panel_result -+ = macfont_get_nsctfont (FRAME_FONT (emacsframe)); -+ -+ if (font_panel_result) -+ [font_panel_result retain]; -+ } -+ -+ [NSApp stop: self]; -+} -+ -+- (void) noteUserCancelledSelection -+{ -+ font_panel_active = NO; -+ -+ if (font_panel_result) -+ [font_panel_result release]; -+ font_panel_result = nil; -+ -+ [NSApp stop: self]; -+} -+#endif -+ -+- (Lisp_Object) showFontPanel -+{ -+ 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 -+ -+#ifdef NS_IMPL_COCOA -+ buttons -+ = ns_create_font_panel_buttons (self, -+ @selector (noteUserSelectedFont), -+ @selector (noteUserCancelledSelection)); -+ [[fm fontPanel: YES] setAccessoryView: buttons]; -+ [buttons release]; -+#endif -+ -+ [fm setSelectedFont: nsfont isMultiple: NO]; -+ [fm orderFrontFontPanel: NSApp]; -+ -+ font_panel_active = YES; -+ timeout = make_timespec (0, 100000000); -+ -+ 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 (font_panel_result) -+ [font_panel_result autorelease]; -+ -+#ifdef NS_IMPL_COCOA -+ if (!canceled) -+ font_panel_result = nil; -+#endif -+ -+ result = font_panel_result; -+ font_panel_result = nil; -+ -+ [[fm fontPanel: YES] setIsVisible: NO]; -+ font_panel_active = NO; -+ -+ if (result) -+ return ns_font_desc_to_font_spec ([result fontDescriptor], -+ result); -+ -+ return Qnil; -+} -+ -+- (BOOL)acceptsFirstResponder -+{ -+ NSTRACE ("[EmacsView acceptsFirstResponder]"); -+ return YES; -+} -+ -+/* Tell NS we want to accept clicks that activate the window */ -+- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent -+{ -+ NSTRACE_MSG ("First mouse event: type=%ld, clickCount=%ld", -+ [theEvent type], [theEvent clickCount]); -+ return ns_click_through; -+} -+- (void)resetCursorRects -+{ -+ NSRect visible = [self visibleRect]; -+ NSCursor *currentCursor = FRAME_POINTER_TYPE (emacsframe); -+ NSTRACE ("[EmacsView resetCursorRects]"); -+ -+ if (currentCursor == nil) -+ currentCursor = [NSCursor arrowCursor]; -+ -+ if (!NSIsEmptyRect (visible)) -+ [self addCursorRect: visible cursor: currentCursor]; -+ -+#if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 101300 -+#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 -+ if ([currentCursor respondsToSelector: @selector(setOnMouseEntered:)]) -+#endif -+ [currentCursor setOnMouseEntered: YES]; -+#endif -+} -+ -+ -+ -+/*****************************************************************************/ -+/* Keyboard handling. */ -+#define NS_KEYLOG 0 -+ -+- (void)keyDown: (NSEvent *)theEvent -+{ -+ 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. */ -+ [(EmacsView *)[[theEvent window] delegate] keyDown: theEvent]; -+ return; -+ } -+ -+ if (nsEvArray == nil) -+ nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1]; -+ -+ [NSCursor setHiddenUntilMouseMoves:! NILP (Vmake_pointer_invisible)]; -+ -+ if (!hlinfo->mouse_face_hidden -+ && FIXNUMP (Vmouse_highlight) -+ && !EQ (emacsframe->tab_bar_window, hlinfo->mouse_face_window)) -+ { -+ clear_mouse_face (hlinfo); -+ hlinfo->mouse_face_hidden = true; -+ } -+ -+ if (!processingCompose) -+ { -+ /* FIXME: What should happen for key sequences with more than -+ one character? */ -+ code = ([[theEvent charactersIgnoringModifiers] length] == 0) ? - 0 : [[theEvent charactersIgnoringModifiers] characterAtIndex: 0]; + /* ========================================================================== - /* Is it a "function key"? */ -@@ -8237,6 +9341,27 @@ ns_in_echo_area (void) + EmacsView implementation +@@ -6889,6 +7993,7 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action) + [layer release]; + #endif + ++ [accessibilityElements release]; + [[self menu] release]; + [super dealloc]; + } +@@ -8237,6 +9342,27 @@ ns_in_echo_area (void) XSETFRAME (event.frame_or_window, emacsframe); kbd_buffer_store_event (&event); ns_send_appdefined (-1); // Kick main loop @@ -1689,7 +1255,7 @@ index 932d209..ca6b969 100644 } -@@ -9474,6 +10599,297 @@ ns_in_echo_area (void) +@@ -9474,6 +10600,297 @@ ns_in_echo_area (void) return fs_state; } @@ -1987,7 +1553,7 @@ index 932d209..ca6b969 100644 @end /* EmacsView */ -@@ -9941,6 +11357,14 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c) +@@ -9941,6 +11358,14 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c) return [super accessibilityAttributeValue:attribute]; }