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 d419ec8..c9fbdc7 100644 --- a/patches/0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch +++ b/patches/0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch @@ -91,7 +91,7 @@ index 7c1ee4c..2e2c80f 100644 diff --git a/src/nsterm.m b/src/nsterm.m -index 932d209..043477b 100644 +index 932d209..416e5a4 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -1104,6 +1104,11 @@ ns_update_end (struct frame *f) @@ -144,70 +144,108 @@ index 932d209..043477b 100644 ns_focus (f, NULL, 0); NSGraphicsContext *ctx = [NSGraphicsContext currentContext]; -@@ -6847,6 +6883,1074 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action) - } - #endif +@@ -6849,261 +6885,1380 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action) -+/* ========================================================================== -+ + /* ========================================================================== + +- 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 +-{ +- 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) +{ + *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). */ @@ -227,12 +265,16 @@ index 932d209..043477b 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) @@ -263,37 +305,54 @@ index 932d209..043477b 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 + +/* ---- 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++) @@ -305,28 +364,64 @@ index 932d209..043477b 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; @@ -334,10 +429,22 @@ index 932d209..043477b 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; @@ -360,10 +467,16 @@ index 932d209..043477b 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; @@ -373,15 +486,23 @@ index 932d209..043477b 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; @@ -391,33 +512,55 @@ index 932d209..043477b 100644 + NSRect r = NSMakeRect (x, y, ew, eh); + NSRect winRect = [view convertRect:r toView:nil]; + return [[view window] convertRectToScreen:winRect]; -+} -+ + } + +-- (BOOL)acceptsFirstResponder +- (BOOL)isAccessibilityElement -+{ -+ return YES; -+} -+ + { +- NSTRACE ("[EmacsView acceptsFirstResponder]"); + return YES; + } + +-/* Tell NS we want to accept clicks that activate the window */ +-- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent +/* ---- 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; @@ -425,7 +568,7 @@ index 932d209..043477b 100644 +@synthesize cachedModiff; +@synthesize cachedPoint; +@synthesize cachedMarkActive; -+ + +- (void)dealloc +{ + [cachedText release]; @@ -433,11 +576,20 @@ index 932d209..043477b 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) @@ -447,17 +599,21 @@ index 932d209..043477b 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; -+ + return; + +- if (!emacs_event) + ptrdiff_t modiff = BUF_MODIFF (b); + ptrdiff_t pt = BUF_PT (b); + NSUInteger textLen = cachedText ? [cachedText length] : 0; @@ -465,31 +621,53 @@ index 932d209..043477b 100644 + && pt >= cachedTextStart + && (textLen == 0 + || [self accessibilityIndexForCharpos:pt] <= textLen)) -+ return; -+ + 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; +- } + 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); @@ -503,10 +681,11 @@ index 932d209..043477b 100644 + { + ns_ax_visible_run *last = &visibleRuns[visibleRunCount - 1]; + return last->ax_start + last->ax_length; -+ } + } + return 0; +} -+ + +- if (!processingCompose) +/* Convert accessibility string index to buffer charpos. */ +- (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx +{ @@ -701,8 +880,9 @@ index 932d209..043477b 100644 + unblock_input (); + + /* Post SelectedTextChanged so VoiceOver reads the current line -+ upon entering text interaction mode. */ -+ NSDictionary *info = @{@"AXTextStateChangeType": @2}; ++ upon entering text interaction mode. ++ kAXTextStateChangeTypeSelectionMove = 1. */ ++ NSDictionary *info = @{@"AXTextStateChangeType": @1}; + NSAccessibilityPostNotificationWithUserInfo ( + self, NSAccessibilitySelectedTextChangedNotification, info); +} @@ -956,7 +1136,7 @@ index 932d209..043477b 100644 + BOOL markActive = !NILP (BVAR (b, mark_active)); + + /* --- Text changed → typing echo --- -+ kAXTextStateChangeTypeEdit = 1, kAXTextEditTypeTyping = 3. */ ++ kAXTextStateChangeTypeEdit = 0, kAXTextEditTypeTyping = 3. */ + if (modiff != self.cachedModiff) + { + /* Capture changed char before invalidating cache. */ @@ -993,7 +1173,7 @@ index 932d209..043477b 100644 + @"AXTextChangeValueLength": @([changedChar length]) + }; + NSDictionary *userInfo = @{ -+ @"AXTextStateChangeType": @1, ++ @"AXTextStateChangeType": @0, + @"AXTextChangeValues": @[change] + }; + NSAccessibilityPostNotificationWithUserInfo ( @@ -1001,7 +1181,7 @@ index 932d209..043477b 100644 + } + + /* --- Cursor moved or selection changed → line reading --- -+ kAXTextStateChangeTypeSelectionMove = 2. ++ kAXTextStateChangeTypeSelectionMove = 1. + Use 'else if' — edits and selection moves are mutually exclusive + per the WebKit/Chromium pattern. VoiceOver gets confused if + both notifications arrive in the same runloop iteration. */ @@ -1050,7 +1230,7 @@ index 932d209..043477b 100644 + } + + NSDictionary *moveInfo = @{ -+ @"AXTextStateChangeType": @2, ++ @"AXTextStateChangeType": @1, + @"AXTextSelectionDirection": @(direction), + @"AXTextSelectionGranularity": @(granularity) + }; @@ -1072,54 +1252,103 @@ index 932d209..043477b 100644 + { + NSString *announceText = nil; + -+ /* Check for completions-highlight overlay at point. */ -+ { -+ struct buffer *oldb2 = current_buffer; -+ if (b != current_buffer) -+ set_buffer_internal_1 (b); ++ struct buffer *oldb2 = current_buffer; ++ if (b != current_buffer) ++ set_buffer_internal_1 (b); + -+ Lisp_Object overlays = Foverlays_at (make_fixnum (point), Qnil); -+ Lisp_Object tail; -+ for (tail = overlays; CONSP (tail); tail = XCDR (tail)) -+ { -+ Lisp_Object ov = XCAR (tail); -+ Lisp_Object face = Foverlay_get (ov, Qface); -+ /* The completions-highlight face is applied as the -+ symbol `completions-highlight`. */ -+ if (EQ (face, intern ("completions-highlight")) -+ || (CONSP (face) -+ && !NILP (Fmemq (intern ("completions-highlight"), -+ face)))) -+ { -+ ptrdiff_t ov_start = OVERLAY_START (ov); -+ ptrdiff_t ov_end = OVERLAY_END (ov); -+ if (ov_end > ov_start) -+ { -+ /* Extract overlay text from visible cached text. */ -+ NSUInteger ax_s = [self accessibilityIndexForCharpos: -+ ov_start]; -+ NSUInteger ax_e = [self accessibilityIndexForCharpos: -+ ov_end]; -+ if (ax_e > ax_s && ax_e <= [cachedText length]) -+ announceText = [cachedText substringWithRange: -+ NSMakeRange (ax_s, ax_e - ax_s)]; -+ } -+ break; -+ } -+ } ++ /* 1) Prefer explicit completion candidate property when present. */ ++ Lisp_Object cstr = Fget_char_property (make_fixnum (point), ++ intern ("completion--string"), ++ Qnil); ++ if (STRINGP (cstr)) ++ announceText = [NSString stringWithLispString:cstr]; + -+ if (b != oldb2) -+ set_buffer_internal_1 (oldb2); -+ } ++ /* 2) Fallback: announce the mouse-face span at point. ++ completion-list-mode often marks the active candidate this way. */ ++ if (!announceText) ++ { ++ Lisp_Object mf = Fget_char_property (make_fixnum (point), ++ Qmouse_face, Qnil); ++ if (!NILP (mf)) ++ { ++ ptrdiff_t begv2 = BUF_BEGV (b); ++ ptrdiff_t zv2 = BUF_ZV (b); ++ ptrdiff_t s2 = point; ++ ptrdiff_t e2 = point; + -+ /* Fallback: read the current line at point. */ ++ while (s2 > begv2) ++ { ++ Lisp_Object prev = Fget_char_property ( ++ make_fixnum (s2 - 1), Qmouse_face, Qnil); ++ if (!NILP (Fequal (prev, mf))) ++ s2--; ++ else ++ break; ++ } ++ ++ while (e2 < zv2) ++ { ++ Lisp_Object cur = Fget_char_property ( ++ make_fixnum (e2), Qmouse_face, Qnil); ++ if (!NILP (Fequal (cur, mf))) ++ e2++; ++ else ++ break; ++ } ++ ++ if (e2 > s2) ++ { ++ NSUInteger ax_s = [self accessibilityIndexForCharpos:s2]; ++ NSUInteger ax_e = [self accessibilityIndexForCharpos:e2]; ++ if (ax_e > ax_s && ax_e <= [cachedText length]) ++ announceText = [cachedText substringWithRange: ++ NSMakeRange (ax_s, ax_e - ax_s)]; ++ } ++ } ++ } ++ ++ /* 3) Fallback: check completions-highlight overlay span. */ ++ if (!announceText) ++ { ++ Lisp_Object overlays = Foverlays_at (make_fixnum (point), Qnil); ++ Lisp_Object tail; ++ for (tail = overlays; CONSP (tail); tail = XCDR (tail)) ++ { ++ Lisp_Object ov = XCAR (tail); ++ Lisp_Object face = Foverlay_get (ov, Qface); ++ if (EQ (face, intern ("completions-highlight")) ++ || (CONSP (face) ++ && !NILP (Fmemq (intern ("completions-highlight"), ++ face)))) ++ { ++ ptrdiff_t ov_start = OVERLAY_START (ov); ++ ptrdiff_t ov_end = OVERLAY_END (ov); ++ if (ov_end > ov_start) ++ { ++ NSUInteger ax_s = [self accessibilityIndexForCharpos: ++ ov_start]; ++ NSUInteger ax_e = [self accessibilityIndexForCharpos: ++ ov_end]; ++ if (ax_e > ax_s && ax_e <= [cachedText length]) ++ announceText = [cachedText substringWithRange: ++ NSMakeRange (ax_s, ax_e - ax_s)]; ++ } ++ break; ++ } ++ } ++ } ++ ++ if (b != oldb2) ++ set_buffer_internal_1 (oldb2); ++ ++ /* Final fallback: read the current line at point. */ + if (!announceText) + { + NSUInteger point_idx = [self accessibilityIndexForCharpos:point]; + if (point_idx <= [cachedText length]) + { + NSInteger lineNum = [self accessibilityLineForIndex: -+ point_idx]; ++ point_idx]; + NSRange lineRange = [self accessibilityRangeForLine:lineNum]; + if (lineRange.location != NSNotFound + && lineRange.length > 0 @@ -1131,9 +1360,8 @@ index 932d209..043477b 100644 + + if (announceText) + { -+ /* Trim trailing newline for cleaner speech. */ + announceText = [announceText stringByTrimmingCharactersInSet: -+ [NSCharacterSet newlineCharacterSet]]; ++ [NSCharacterSet whitespaceAndNewlineCharacterSet]]; + if ([announceText length] > 0) + { + NSDictionary *annInfo = @{ @@ -1148,6 +1376,7 @@ index 932d209..043477b 100644 + } + } + } ++ + } +} + @@ -1216,18 +1445,268 @@ index 932d209..043477b 100644 +#endif /* NS_IMPL_COCOA */ + + - /* ========================================================================== - - EmacsView implementation -@@ -6889,6 +7993,7 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action) - [layer release]; - #endif - ++/* ========================================================================== ++ ++ 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]; - } -@@ -8237,6 +9342,27 @@ ns_in_echo_area (void) ++ [[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? */ +@@ -8237,6 +9392,27 @@ ns_in_echo_area (void) XSETFRAME (event.frame_or_window, emacsframe); kbd_buffer_store_event (&event); ns_send_appdefined (-1); // Kick main loop @@ -1243,7 +1722,7 @@ index 932d209..043477b 100644 + { + NSAccessibilityPostNotification (focused, + NSAccessibilityFocusedUIElementChangedNotification); -+ NSDictionary *info = @{@"AXTextStateChangeType": @2}; ++ NSDictionary *info = @{@"AXTextStateChangeType": @1}; + NSAccessibilityPostNotificationWithUserInfo (focused, + NSAccessibilitySelectedTextChangedNotification, info); + } @@ -1255,7 +1734,7 @@ index 932d209..043477b 100644 } -@@ -9474,6 +10600,297 @@ ns_in_echo_area (void) +@@ -9474,6 +10650,297 @@ ns_in_echo_area (void) return fs_state; } @@ -1473,7 +1952,7 @@ index 932d209..043477b 100644 + { + NSAccessibilityPostNotification (focused, + NSAccessibilityFocusedUIElementChangedNotification); -+ NSDictionary *info = @{@"AXTextStateChangeType": @2}; ++ NSDictionary *info = @{@"AXTextStateChangeType": @1}; + NSAccessibilityPostNotificationWithUserInfo (focused, + NSAccessibilitySelectedTextChangedNotification, info); + } @@ -1553,7 +2032,7 @@ index 932d209..043477b 100644 @end /* EmacsView */ -@@ -9941,6 +11358,14 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c) +@@ -9941,6 +11408,14 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c) return [super accessibilityAttributeValue:attribute]; }