>From d0d919061c71bd22f9e6193e68b8632e88598d18 Mon Sep 17 00:00:00 2001 From: Alan Third Date: Sat, 9 Feb 2019 08:57:02 +0000 Subject: [PATCH 1/4] Revert "Fix some NS drawing issues (bug#32932)" This reverts commit 7e8eee60a9dbb0c59cf26f237b21efe7fd1043c9. --- src/nsterm.m | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/nsterm.m b/src/nsterm.m index bbd2c84214..0cf16642bd 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -277,6 +277,7 @@ - (NSColor *)colorUsingDefaultColorSpace /* display update */ static int ns_window_num = 0; +static BOOL gsaved = NO; static BOOL ns_fake_keydown = NO; #ifdef NS_IMPL_COCOA static BOOL ns_menu_bar_is_hidden = NO; @@ -1177,6 +1178,7 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) NSRectClipList (r, 2); else NSRectClip (*r); + gsaved = YES; return YES; } @@ -1200,7 +1202,11 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) { NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_reset_clipping"); - [[NSGraphicsContext currentContext] restoreGraphicsState]; + if (gsaved) + { + [[NSGraphicsContext currentContext] restoreGraphicsState]; + gsaved = NO; + } } @@ -2667,8 +2673,6 @@ so some key presses (TAB) are swallowed by the system. */ static void ns_copy_bits (struct frame *f, NSRect src, NSRect dest) { - NSSize delta = NSMakeSize (dest.origin.x - src.origin.x, - dest.origin.y - src.origin.y); NSTRACE ("ns_copy_bits"); if (FRAME_NS_VIEW (f)) @@ -2677,21 +2681,10 @@ so some key presses (TAB) are swallowed by the system. */ /* FIXME: scrollRect:by: is deprecated in macOS 10.14. There is no obvious replacement so we may have to come up with our own. */ - [FRAME_NS_VIEW (f) scrollRect: src by: delta]; - -#ifdef NS_IMPL_COCOA - /* As far as I can tell from the documentation, scrollRect:by:, - above, should copy the dirty rectangles from our source - rectangle to our destination, however it appears it clips the - operation to src. As a result we need to use - translateRectsNeedingDisplayInRect:by: below, and we have to - union src and dest so it can pick up the dirty rectangles, - and place them, as it also clips to the rectangle. - - FIXME: We need a GNUstep equivalent. */ - [FRAME_NS_VIEW (f) translateRectsNeedingDisplayInRect:NSUnionRect (src, dest) - by:delta]; -#endif + [FRAME_NS_VIEW (f) scrollRect: src + by: NSMakeSize (dest.origin.x - src.origin.x, + dest.origin.y - src.origin.y)]; + [FRAME_NS_VIEW (f) setNeedsDisplay:YES]; } } @@ -3097,6 +3090,15 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors. else [FRAME_CURSOR_COLOR (f) set]; +#ifdef NS_IMPL_COCOA + /* TODO: This makes drawing of cursor plus that of phys_cursor_glyph + atomic. Cleaner ways of doing this should be investigated. + One way would be to set a global variable DRAWING_CURSOR + when making the call to draw_phys..(), don't focus in that + case, then move the ns_reset_clipping() here after that call. */ + NSDisableScreenUpdates (); +#endif + switch (cursor_type) { case DEFAULT_CURSOR: @@ -4962,7 +4964,7 @@ static Lisp_Object ns_string_to_lispmod (const char *s) ns_after_update_window_line, ns_update_window_begin, ns_update_window_end, - 0, /* flush_display */ + ns_flush_display, /* flush_display */ x_clear_window_mouse_face, x_get_glyph_overhangs, x_fix_overlapping_area, @@ -7031,6 +7033,7 @@ - (NSSize)windowWillResize: (NSWindow *)sender toSize: (NSSize)frameSize size_title = xmalloc (strlen (old_title) + 40); esprintf (size_title, "%s — (%d x %d)", old_title, cols, rows); [window setTitle: [NSString stringWithUTF8String: size_title]]; + [window display]; xfree (size_title); } } @@ -8087,8 +8090,8 @@ - (void)viewWillDraw - (void)drawRect: (NSRect)rect { - const NSRect *rectList; - NSInteger numRects; + int x = NSMinX (rect), y = NSMinY (rect); + int width = NSWidth (rect), height = NSHeight (rect); NSTRACE ("[EmacsView drawRect:" NSTRACE_FMT_RECT "]", NSTRACE_ARG_RECT(rect)); @@ -8096,6 +8099,7 @@ - (void)drawRect: (NSRect)rect if (!emacsframe || !emacsframe->output_data.ns) return; + ns_clear_frame_area (emacsframe, x, y, width, height); block_input (); /* Get only the precise dirty rectangles to avoid redrawing -- 2.20.1 >From 537188acb6f90c06e62acbb46be55429efa71209 Mon Sep 17 00:00:00 2001 From: Alan Third Date: Sat, 9 Feb 2019 08:57:22 +0000 Subject: [PATCH 2/4] Revert "Ensure NS frame is redrawn correctly after scroll" This reverts commit a6ab8db3a3dc5ec107ef023c6659620584309c97. --- src/nsterm.m | 1 - 1 file changed, 1 deletion(-) diff --git a/src/nsterm.m b/src/nsterm.m index 0cf16642bd..dd8755065b 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -2684,7 +2684,6 @@ so some key presses (TAB) are swallowed by the system. */ [FRAME_NS_VIEW (f) scrollRect: src by: NSMakeSize (dest.origin.x - src.origin.x, dest.origin.y - src.origin.y)]; - [FRAME_NS_VIEW (f) setNeedsDisplay:YES]; } } -- 2.20.1 >From db3a84ed1dd9172afacadfc9e457add944beb207 Mon Sep 17 00:00:00 2001 From: Alan Third Date: Sat, 9 Feb 2019 09:01:07 +0000 Subject: [PATCH 3/4] Revert "Make all NS drawing be done from drawRect" This reverts commit 7946445962372c4255180af45cb7c857f1b0b5fa. --- src/nsterm.m | 792 +++++++++++++++++++++++++++------------------------ 1 file changed, 418 insertions(+), 374 deletions(-) diff --git a/src/nsterm.m b/src/nsterm.m index dd8755065b..6529a50298 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -276,7 +276,12 @@ - (NSColor *)colorUsingDefaultColorSpace long context_menu_value = 0; /* display update */ +static struct frame *ns_updating_frame; +static NSView *focus_view = NULL; static int ns_window_num = 0; +#ifdef NS_IMPL_GNUSTEP +static NSRect uRect; // TODO: This is dead, remove it? +#endif static BOOL gsaved = NO; static BOOL ns_fake_keydown = NO; #ifdef NS_IMPL_COCOA @@ -1055,13 +1060,12 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) external (RIF) call; whole frame, called before update_window_begin -------------------------------------------------------------------------- */ { -#ifdef NS_IMPL_COCOA EmacsView *view = FRAME_NS_VIEW (f); - NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_begin"); ns_update_auto_hide_menu_bar (); +#ifdef NS_IMPL_COCOA if ([view isFullscreen] && [view fsIsNative]) { // Fix reappearing tool bar in fullscreen for Mac OS X 10.7 @@ -1071,6 +1075,36 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) [toolbar setVisible: tbar_visible]; } #endif + + ns_updating_frame = f; + [view lockFocus]; + + /* drawRect may have been called for say the minibuffer, and then clip path + is for the minibuffer. But the display engine may draw more because + we have set the frame as garbaged. So reset clip path to the whole + view. */ +#ifdef NS_IMPL_COCOA + { + NSBezierPath *bp; + NSRect r = [view frame]; + NSRect cr = [[view window] frame]; + /* If a large frame size is set, r may be larger than the window frame + before constrained. In that case don't change the clip path, as we + will clear in to the tool bar and title bar. */ + if (r.size.height + + FRAME_NS_TITLEBAR_HEIGHT (f) + + FRAME_TOOLBAR_HEIGHT (f) <= cr.size.height) + { + bp = [[NSBezierPath bezierPathWithRect: r] retain]; + [bp setClip]; + [bp release]; + } + } +#endif + +#ifdef NS_IMPL_GNUSTEP + uRect = NSMakeRect (0, 0, 0, 0); +#endif } @@ -1151,62 +1185,118 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) external (RIF) call; for whole frame, called after update_window_end -------------------------------------------------------------------------- */ { + EmacsView *view = FRAME_NS_VIEW (f); + NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_end"); /* if (f == MOUSE_HL_INFO (f)->mouse_face_mouse_frame) */ MOUSE_HL_INFO (f)->mouse_face_defer = 0; -} + block_input (); -static BOOL -ns_clip_to_rect (struct frame *f, NSRect *r, int n) + [view unlockFocus]; + [[view window] flushWindow]; + + unblock_input (); + ns_updating_frame = NULL; +} + +static void +ns_focus (struct frame *f, NSRect *r, int n) /* -------------------------------------------------------------------------- - Clip the drawing area to rectangle r in frame f. If drawing is not - currently possible mark r as dirty and return NO, otherwise return - YES. + Internal: Focus on given frame. During small local updates this is used to + draw, however during large updates, ns_update_begin and ns_update_end are + called to wrap the whole thing, in which case these calls are stubbed out. + Except, on GNUstep, we accumulate the rectangle being drawn into, because + the back end won't do this automatically, and will just end up flushing + the entire window. -------------------------------------------------------------------------- */ { - NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_clip_to_rect"); - if (r) + NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_focus"); + if (r != NULL) { NSTRACE_RECT ("r", *r); + } - if ([NSView focusView] == FRAME_NS_VIEW (f)) + if (f != ns_updating_frame) + { + NSView *view = FRAME_NS_VIEW (f); + if (view != focus_view) { - [[NSGraphicsContext currentContext] saveGraphicsState]; - if (n == 2) - NSRectClipList (r, 2); - else - NSRectClip (*r); - gsaved = YES; + if (focus_view != NULL) + { + [focus_view unlockFocus]; + [[focus_view window] flushWindow]; +/*debug_lock--; */ + } - return YES; - } - else - { - NSView *view = FRAME_NS_VIEW (f); - int i; - for (i = 0 ; i < n ; i++) - [view setNeedsDisplayInRect:r[i]]; + if (view) + [view lockFocus]; + focus_view = view; +/*if (view) debug_lock++; */ } } - return NO; + /* clipping */ + if (r) + { + [[NSGraphicsContext currentContext] saveGraphicsState]; + if (n == 2) + NSRectClipList (r, 2); + else + NSRectClip (*r); + gsaved = YES; + } } static void -ns_reset_clipping (struct frame *f) -/* Internal: Restore the previous graphics state, unsetting any - clipping areas. */ +ns_unfocus (struct frame *f) +/* -------------------------------------------------------------------------- + Internal: Remove focus on given frame + -------------------------------------------------------------------------- */ { - NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_reset_clipping"); + NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_unfocus"); if (gsaved) { [[NSGraphicsContext currentContext] restoreGraphicsState]; gsaved = NO; } + + if (f != ns_updating_frame) + { + if (focus_view != NULL) + { + [focus_view unlockFocus]; + [[focus_view window] flushWindow]; + focus_view = NULL; +/*debug_lock--; */ + } + } +} + + +static void +ns_clip_to_row (struct window *w, struct glyph_row *row, + enum glyph_row_area area, BOOL gc) +/* -------------------------------------------------------------------------- + Internal (but parallels other terms): Focus drawing on given row + -------------------------------------------------------------------------- */ +{ + struct frame *f = XFRAME (WINDOW_FRAME (w)); + NSRect clip_rect; + int window_x, window_y, window_width; + + window_box (w, area, &window_x, &window_y, &window_width, 0); + + clip_rect.origin.x = window_x; + clip_rect.origin.y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, row->y)); + clip_rect.origin.y = max (clip_rect.origin.y, window_y); + clip_rect.size.width = window_width; + clip_rect.size.height = row->visible_height; + + ns_focus (f, &clip_rect, 1); } @@ -2630,16 +2720,14 @@ so some key presses (TAB) are swallowed by the system. */ r = [view bounds]; block_input (); - if (ns_clip_to_rect (f, &r, 1)) - { - [ns_lookup_indexed_color (NS_FACE_BACKGROUND - (FACE_FROM_ID (f, DEFAULT_FACE_ID)), f) set]; - NSRectFill (r); - ns_reset_clipping (f); - - /* as of 2006/11 or so this is now needed */ - ns_redraw_scroll_bars (f); - } + ns_focus (f, &r, 1); + [ns_lookup_indexed_color (NS_FACE_BACKGROUND + (FACE_FROM_ID (f, DEFAULT_FACE_ID)), f) set]; + NSRectFill (r); + ns_unfocus (f); + + /* as of 2006/11 or so this is now needed */ + ns_redraw_scroll_bars (f); unblock_input (); } @@ -2660,14 +2748,13 @@ so some key presses (TAB) are swallowed by the system. */ NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_clear_frame_area"); r = NSIntersectionRect (r, [view frame]); - if (ns_clip_to_rect (f, &r, 1)) - { - [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), f) set]; + ns_focus (f, &r, 1); + [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), f) set]; - NSRectFill (r); + NSRectFill (r); - ns_reset_clipping (f); - } + ns_unfocus (f); + return; } static void @@ -2679,11 +2766,11 @@ so some key presses (TAB) are swallowed by the system. */ { hide_bell(); // Ensure the bell image isn't scrolled. - /* FIXME: scrollRect:by: is deprecated in macOS 10.14. There is - no obvious replacement so we may have to come up with our own. */ + ns_focus (f, &dest, 1); [FRAME_NS_VIEW (f) scrollRect: src by: NSMakeSize (dest.origin.x - src.origin.x, dest.origin.y - src.origin.y)]; + ns_unfocus (f); } } @@ -2904,95 +2991,86 @@ so some key presses (TAB) are swallowed by the system. */ nBimgs = max_used_fringe_bitmap; } - /* Work out the rectangle we will composite into. */ - if (p->which) - imageRect = NSMakeRect (p->x, p->y, p->wd, p->h); + /* Must clip because of partially visible lines. */ + ns_clip_to_row (w, row, ANY_AREA, YES); - /* Work out the rectangle we will need to clear. Because we're - compositing rather than blitting, we need to clear the area under - the image regardless of anything else. */ - if (p->bx >= 0 && !p->overlay_p) + if (!p->overlay_p) { - clearRect = NSMakeRect (p->bx, p->by, p->nx, p->ny); - clearRect = NSUnionRect (clearRect, imageRect); - } - else - { - clearRect = imageRect; - } + int bx = p->bx, by = p->by, nx = p->nx, ny = p->ny; - /* Handle partially visible rows. */ - clearRect = NSIntersectionRect (clearRect, rowRect); - - /* The visible portion of imageRect will always be contained within - clearRect. */ - if (ns_clip_to_rect (f, &clearRect, 1)) - { - if (! NSIsEmptyRect (clearRect)) + if (bx >= 0 && nx > 0) { - NSTRACE_RECT ("clearRect", clearRect); - - [ns_lookup_indexed_color(face->background, f) set]; - NSRectFill (clearRect); + NSRect r = NSMakeRect (bx, by, nx, ny); + NSRectClip (r); + [ns_lookup_indexed_color (face->background, f) set]; + NSRectFill (r); } + } - if (p->which) + if (p->which) + { + NSRect r = NSMakeRect (p->x, p->y, p->wd, p->h); + EmacsImage *img = bimgs[p->which - 1]; + + if (!img) { - EmacsImage *img = bimgs[p->which - 1]; + // Note: For "periodic" images, allocate one EmacsImage for + // the base image, and use it for all dh:s. + unsigned short *bits = p->bits; + int full_height = p->h + p->dh; + int i; + unsigned char *cbits = xmalloc (full_height); + + for (i = 0; i < full_height; i++) + cbits[i] = bits[i]; + img = [[EmacsImage alloc] initFromXBM: cbits width: 8 + height: full_height + fg: 0 bg: 0]; + bimgs[p->which - 1] = img; + xfree (cbits); + } - if (!img) - { - // Note: For "periodic" images, allocate one EmacsImage for - // the base image, and use it for all dh:s. - unsigned short *bits = p->bits; - int full_height = p->h + p->dh; - int i; - unsigned char *cbits = xmalloc (full_height); - - for (i = 0; i < full_height; i++) - cbits[i] = bits[i]; - img = [[EmacsImage alloc] initFromXBM: cbits width: 8 - height: full_height - fg: 0 bg: 0]; - bimgs[p->which - 1] = img; - xfree (cbits); - } + NSTRACE_RECT ("r", r); + NSRectClip (r); + /* Since we composite the bitmap instead of just blitting it, we need + to erase the whole background. */ + [ns_lookup_indexed_color(face->background, f) set]; + NSRectFill (r); - { - NSColor *bm_color; - if (!p->cursor_p) - bm_color = ns_lookup_indexed_color(face->foreground, f); - else if (p->overlay_p) - bm_color = ns_lookup_indexed_color(face->background, f); - else - bm_color = f->output_data.ns->cursor_color; - [img setXBMColor: bm_color]; - } + { + NSColor *bm_color; + if (!p->cursor_p) + bm_color = ns_lookup_indexed_color(face->foreground, f); + else if (p->overlay_p) + bm_color = ns_lookup_indexed_color(face->background, f); + else + bm_color = f->output_data.ns->cursor_color; + [img setXBMColor: bm_color]; + } #ifdef NS_IMPL_COCOA - // Note: For periodic images, the full image height is "h + hd". - // By using the height h, a suitable part of the image is used. - NSRect fromRect = NSMakeRect(0, 0, p->wd, p->h); - - NSTRACE_RECT ("fromRect", fromRect); - - [img drawInRect: imageRect - fromRect: fromRect - operation: NSCompositingOperationSourceOver - fraction: 1.0 - respectFlipped: YES - hints: nil]; + // Note: For periodic images, the full image height is "h + hd". + // By using the height h, a suitable part of the image is used. + NSRect fromRect = NSMakeRect(0, 0, p->wd, p->h); + + NSTRACE_RECT ("fromRect", fromRect); + + [img drawInRect: r + fromRect: fromRect + operation: NSCompositingOperationSourceOver + fraction: 1.0 + respectFlipped: YES + hints: nil]; #else - { - NSPoint pt = imageRect.origin; - pt.y += p->h; - [img compositeToPoint: pt operation: NSCompositingOperationSourceOver]; - } + { + NSPoint pt = r.origin; + pt.y += p->h; + [img compositeToPoint: pt operation: NSCompositingOperationSourceOver]; + } #endif - } - ns_reset_clipping (f); } + ns_unfocus (f); } @@ -3074,71 +3152,67 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors. r.size.height = h; r.size.width = w->phys_cursor_width; - /* Prevent the cursor from being drawn outside the text area. */ - r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA)); + /* Prevent the cursor from being drawn outside the text area. */ + ns_clip_to_row (w, glyph_row, TEXT_AREA, NO); /* do ns_focus(f, &r, 1); if remove */ - if (ns_clip_to_rect (f, &r, 1)) + + face = FACE_FROM_ID_OR_NULL (f, phys_cursor_glyph->face_id); + if (face && NS_FACE_BACKGROUND (face) + == ns_index_color (FRAME_CURSOR_COLOR (f), f)) { - face = FACE_FROM_ID_OR_NULL (f, phys_cursor_glyph->face_id); - if (face && NS_FACE_BACKGROUND (face) - == ns_index_color (FRAME_CURSOR_COLOR (f), f)) - { - [ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), f) set]; - hollow_color = FRAME_CURSOR_COLOR (f); - } - else - [FRAME_CURSOR_COLOR (f) set]; + [ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), f) set]; + hollow_color = FRAME_CURSOR_COLOR (f); + } + else + [FRAME_CURSOR_COLOR (f) set]; #ifdef NS_IMPL_COCOA - /* TODO: This makes drawing of cursor plus that of phys_cursor_glyph - atomic. Cleaner ways of doing this should be investigated. - One way would be to set a global variable DRAWING_CURSOR - when making the call to draw_phys..(), don't focus in that - case, then move the ns_reset_clipping() here after that call. */ - NSDisableScreenUpdates (); + /* TODO: This makes drawing of cursor plus that of phys_cursor_glyph + atomic. Cleaner ways of doing this should be investigated. + One way would be to set a global variable DRAWING_CURSOR + when making the call to draw_phys..(), don't focus in that + case, then move the ns_unfocus() here after that call. */ + NSDisableScreenUpdates (); #endif - switch (cursor_type) - { - case DEFAULT_CURSOR: - case NO_CURSOR: - break; - case FILLED_BOX_CURSOR: - NSRectFill (r); - break; - case HOLLOW_BOX_CURSOR: - NSRectFill (r); - [hollow_color set]; - NSRectFill (NSInsetRect (r, 1, 1)); - [FRAME_CURSOR_COLOR (f) set]; - break; - case HBAR_CURSOR: - NSRectFill (r); - break; - case BAR_CURSOR: - s = r; - /* If the character under cursor is R2L, draw the bar cursor - on the right of its glyph, rather than on the left. */ - cursor_glyph = get_phys_cursor_glyph (w); - if ((cursor_glyph->resolved_level & 1) != 0) - s.origin.x += cursor_glyph->pixel_width - s.size.width; - - NSRectFill (s); - break; - } - - /* draw the character under the cursor */ - if (cursor_type != NO_CURSOR) - draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR); - - ns_reset_clipping (f); - } - else if (! redisplaying_p) + switch (cursor_type) { - /* If this function is called outside redisplay, it probably - means we need an immediate update. */ - [FRAME_NS_VIEW (f) display]; + case DEFAULT_CURSOR: + case NO_CURSOR: + break; + case FILLED_BOX_CURSOR: + NSRectFill (r); + break; + case HOLLOW_BOX_CURSOR: + NSRectFill (r); + [hollow_color set]; + NSRectFill (NSInsetRect (r, 1, 1)); + [FRAME_CURSOR_COLOR (f) set]; + break; + case HBAR_CURSOR: + NSRectFill (r); + break; + case BAR_CURSOR: + s = r; + /* If the character under cursor is R2L, draw the bar cursor + on the right of its glyph, rather than on the left. */ + cursor_glyph = get_phys_cursor_glyph (w); + if ((cursor_glyph->resolved_level & 1) != 0) + s.origin.x += cursor_glyph->pixel_width - s.size.width; + + NSRectFill (s); + break; } + ns_unfocus (f); + + /* draw the character under the cursor */ + if (cursor_type != NO_CURSOR) + draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR); + +#ifdef NS_IMPL_COCOA + NSEnableScreenUpdates (); +#endif + } @@ -3156,14 +3230,12 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors. face = FACE_FROM_ID_OR_NULL (f, VERTICAL_BORDER_FACE_ID); - if (ns_clip_to_rect (f, &r, 1)) - { - if (face) - [ns_lookup_indexed_color(face->foreground, f) set]; + ns_focus (f, &r, 1); + if (face) + [ns_lookup_indexed_color(face->foreground, f) set]; - NSRectFill(r); - ns_reset_clipping (f); - } + NSRectFill(r); + ns_unfocus (f); } @@ -3190,40 +3262,39 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors. NSTRACE ("ns_draw_window_divider"); - if (ns_clip_to_rect (f, ÷r, 1)) - { - if ((y1 - y0 > x1 - x0) && (x1 - x0 >= 3)) - /* A vertical divider, at least three pixels wide: Draw first and - last pixels differently. */ - { - [ns_lookup_indexed_color(color_first, f) set]; - NSRectFill(NSMakeRect (x0, y0, 1, y1 - y0)); - [ns_lookup_indexed_color(color, f) set]; - NSRectFill(NSMakeRect (x0 + 1, y0, x1 - x0 - 2, y1 - y0)); - [ns_lookup_indexed_color(color_last, f) set]; - NSRectFill(NSMakeRect (x1 - 1, y0, 1, y1 - y0)); - } - else if ((x1 - x0 > y1 - y0) && (y1 - y0 >= 3)) - /* A horizontal divider, at least three pixels high: Draw first and - last pixels differently. */ - { - [ns_lookup_indexed_color(color_first, f) set]; - NSRectFill(NSMakeRect (x0, y0, x1 - x0, 1)); - [ns_lookup_indexed_color(color, f) set]; - NSRectFill(NSMakeRect (x0, y0 + 1, x1 - x0, y1 - y0 - 2)); - [ns_lookup_indexed_color(color_last, f) set]; - NSRectFill(NSMakeRect (x0, y1 - 1, x1 - x0, 1)); - } - else - { - /* In any other case do not draw the first and last pixels - differently. */ - [ns_lookup_indexed_color(color, f) set]; - NSRectFill(divider); - } + ns_focus (f, ÷r, 1); - ns_reset_clipping (f); + if ((y1 - y0 > x1 - x0) && (x1 - x0 >= 3)) + /* A vertical divider, at least three pixels wide: Draw first and + last pixels differently. */ + { + [ns_lookup_indexed_color(color_first, f) set]; + NSRectFill(NSMakeRect (x0, y0, 1, y1 - y0)); + [ns_lookup_indexed_color(color, f) set]; + NSRectFill(NSMakeRect (x0 + 1, y0, x1 - x0 - 2, y1 - y0)); + [ns_lookup_indexed_color(color_last, f) set]; + NSRectFill(NSMakeRect (x1 - 1, y0, 1, y1 - y0)); + } + else if ((x1 - x0 > y1 - y0) && (y1 - y0 >= 3)) + /* A horizontal divider, at least three pixels high: Draw first and + last pixels differently. */ + { + [ns_lookup_indexed_color(color_first, f) set]; + NSRectFill(NSMakeRect (x0, y0, x1 - x0, 1)); + [ns_lookup_indexed_color(color, f) set]; + NSRectFill(NSMakeRect (x0, y0 + 1, x1 - x0, y1 - y0 - 2)); + [ns_lookup_indexed_color(color_last, f) set]; + NSRectFill(NSMakeRect (x0, y1 - 1, x1 - x0, 1)); + } + else + { + /* In any other case do not draw the first and last pixels + differently. */ + [ns_lookup_indexed_color(color, f) set]; + NSRectFill(divider); } + + ns_unfocus (f); } static void @@ -3807,84 +3878,83 @@ Function modeled after x_draw_glyph_string_box (). n = ns_get_glyph_string_clip_rect (s, r); *r = NSMakeRect (s->x, s->y, s->background_width, s->height); - if (ns_clip_to_rect (s->f, r, n)) + ns_focus (s->f, r, n); + + if (s->hl == DRAW_MOUSE_FACE) + { + face = FACE_FROM_ID_OR_NULL (s->f, + MOUSE_HL_INFO (s->f)->mouse_face_face_id); + if (!face) + face = FACE_FROM_ID (s->f, MOUSE_FACE_ID); + } + else + face = FACE_FROM_ID (s->f, s->first_glyph->face_id); + + bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f); + fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f); + + for (i = 0; i < n; ++i) { - if (s->hl == DRAW_MOUSE_FACE) + if (!s->row->full_width_p) { - face = FACE_FROM_ID_OR_NULL (s->f, - MOUSE_HL_INFO (s->f)->mouse_face_face_id); - if (!face) - face = FACE_FROM_ID (s->f, MOUSE_FACE_ID); - } - else - face = FACE_FROM_ID (s->f, s->first_glyph->face_id); + int overrun, leftoverrun; - bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f); - fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f); + /* truncate to avoid overwriting fringe and/or scrollbar */ + overrun = max (0, (s->x + s->background_width) + - (WINDOW_BOX_RIGHT_EDGE_X (s->w) + - WINDOW_RIGHT_FRINGE_WIDTH (s->w))); + r[i].size.width -= overrun; - for (i = 0; i < n; ++i) - { - if (!s->row->full_width_p) - { - int overrun, leftoverrun; - - /* truncate to avoid overwriting fringe and/or scrollbar */ - overrun = max (0, (s->x + s->background_width) - - (WINDOW_BOX_RIGHT_EDGE_X (s->w) - - WINDOW_RIGHT_FRINGE_WIDTH (s->w))); - r[i].size.width -= overrun; - - /* truncate to avoid overwriting to left of the window box */ - leftoverrun = (WINDOW_BOX_LEFT_EDGE_X (s->w) - + WINDOW_LEFT_FRINGE_WIDTH (s->w)) - s->x; - - if (leftoverrun > 0) - { - r[i].origin.x += leftoverrun; - r[i].size.width -= leftoverrun; - } - - /* XXX: Try to work between problem where a stretch glyph on - a partially-visible bottom row will clear part of the - modeline, and another where list-buffers headers and similar - rows erroneously have visible_height set to 0. Not sure - where this is coming from as other terms seem not to show. */ - r[i].size.height = min (s->height, s->row->visible_height); - } + /* truncate to avoid overwriting to left of the window box */ + leftoverrun = (WINDOW_BOX_LEFT_EDGE_X (s->w) + + WINDOW_LEFT_FRINGE_WIDTH (s->w)) - s->x; - [bgCol set]; + if (leftoverrun > 0) + { + r[i].origin.x += leftoverrun; + r[i].size.width -= leftoverrun; + } - /* NOTE: under NS this is NOT used to draw cursors, but we must avoid - overwriting cursor (usually when cursor on a tab). */ - if (s->hl == DRAW_CURSOR) - { - CGFloat x, width; + /* XXX: Try to work between problem where a stretch glyph on + a partially-visible bottom row will clear part of the + modeline, and another where list-buffers headers and similar + rows erroneously have visible_height set to 0. Not sure + where this is coming from as other terms seem not to show. */ + r[i].size.height = min (s->height, s->row->visible_height); + } + + [bgCol set]; - x = r[i].origin.x; - width = s->w->phys_cursor_width; - r[i].size.width -= width; - r[i].origin.x += width; + /* NOTE: under NS this is NOT used to draw cursors, but we must avoid + overwriting cursor (usually when cursor on a tab) */ + if (s->hl == DRAW_CURSOR) + { + CGFloat x, width; - NSRectFill (r[i]); + x = r[i].origin.x; + width = s->w->phys_cursor_width; + r[i].size.width -= width; + r[i].origin.x += width; - /* Draw overlining, etc. on the cursor. */ - if (s->w->phys_cursor_type == FILLED_BOX_CURSOR) - ns_draw_text_decoration (s, face, bgCol, width, x); - else - ns_draw_text_decoration (s, face, fgCol, width, x); - } - else - { - NSRectFill (r[i]); - } + NSRectFill (r[i]); - /* Draw overlining, etc. on the stretch glyph (or the part - of the stretch glyph after the cursor). */ - ns_draw_text_decoration (s, face, fgCol, r[i].size.width, - r[i].origin.x); + /* Draw overlining, etc. on the cursor. */ + if (s->w->phys_cursor_type == FILLED_BOX_CURSOR) + ns_draw_text_decoration (s, face, bgCol, width, x); + else + ns_draw_text_decoration (s, face, fgCol, width, x); + } + else + { + NSRectFill (r[i]); } - ns_reset_clipping (s->f); + + /* Draw overlining, etc. on the stretch glyph (or the part + of the stretch glyph after the cursor). */ + ns_draw_text_decoration (s, face, fgCol, r[i].size.width, + r[i].origin.x); } + ns_unfocus (s->f); s->background_filled_p = 1; } } @@ -4034,11 +4104,9 @@ overwriting cursor (usually when cursor on a tab). */ if (next->first_glyph->type != STRETCH_GLYPH) { n = ns_get_glyph_string_clip_rect (s->next, r); - if (ns_clip_to_rect (s->f, r, n)) - { - ns_maybe_dumpglyphs_background (s->next, 1); - ns_reset_clipping (s->f); - } + ns_focus (s->f, r, n); + ns_maybe_dumpglyphs_background (s->next, 1); + ns_unfocus (s->f); } else { @@ -4053,12 +4121,10 @@ overwriting cursor (usually when cursor on a tab). */ || s->first_glyph->type == COMPOSITE_GLYPH)) { n = ns_get_glyph_string_clip_rect (s, r); - if (ns_clip_to_rect (s->f, r, n)) - { - ns_maybe_dumpglyphs_background (s, 1); - ns_dumpglyphs_box_or_relief (s); - ns_reset_clipping (s->f); - } + ns_focus (s->f, r, n); + ns_maybe_dumpglyphs_background (s, 1); + ns_dumpglyphs_box_or_relief (s); + ns_unfocus (s->f); box_drawn_p = 1; } @@ -4067,11 +4133,9 @@ overwriting cursor (usually when cursor on a tab). */ case IMAGE_GLYPH: n = ns_get_glyph_string_clip_rect (s, r); - if (ns_clip_to_rect (s->f, r, n)) - { - ns_dumpglyphs_image (s, r[0]); - ns_reset_clipping (s->f); - } + ns_focus (s->f, r, n); + ns_dumpglyphs_image (s, r[0]); + ns_unfocus (s->f); break; case STRETCH_GLYPH: @@ -4081,68 +4145,66 @@ overwriting cursor (usually when cursor on a tab). */ case CHAR_GLYPH: case COMPOSITE_GLYPH: n = ns_get_glyph_string_clip_rect (s, r); - if (ns_clip_to_rect (s->f, r, n)) - { - if (s->for_overlaps || (s->cmp_from > 0 - && ! s->first_glyph->u.cmp.automatic)) - s->background_filled_p = 1; - else - ns_maybe_dumpglyphs_background - (s, s->first_glyph->type == COMPOSITE_GLYPH); + ns_focus (s->f, r, n); - if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR) - { - unsigned long tmp = NS_FACE_BACKGROUND (s->face); - NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face); - NS_FACE_FOREGROUND (s->face) = tmp; - } + if (s->for_overlaps || (s->cmp_from > 0 + && ! s->first_glyph->u.cmp.automatic)) + s->background_filled_p = 1; + else + ns_maybe_dumpglyphs_background + (s, s->first_glyph->type == COMPOSITE_GLYPH); - { - BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH; + if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR) + { + unsigned long tmp = NS_FACE_BACKGROUND (s->face); + NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face); + NS_FACE_FOREGROUND (s->face) = tmp; + } - if (isComposite) - ns_draw_composite_glyph_string_foreground (s); - else - ns_draw_glyph_string_foreground (s); - } + { + BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH; - { - NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0 - ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face), - s->f) - : FRAME_FOREGROUND_COLOR (s->f)); - [col set]; - - /* Draw underline, overline, strike-through. */ - ns_draw_text_decoration (s, s->face, col, s->width, s->x); - } + if (isComposite) + ns_draw_composite_glyph_string_foreground (s); + else + ns_draw_glyph_string_foreground (s); + } - if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR) - { - unsigned long tmp = NS_FACE_BACKGROUND (s->face); - NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face); - NS_FACE_FOREGROUND (s->face) = tmp; - } + { + NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0 + ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face), + s->f) + : FRAME_FOREGROUND_COLOR (s->f)); + [col set]; + + /* Draw underline, overline, strike-through. */ + ns_draw_text_decoration (s, s->face, col, s->width, s->x); + } - ns_reset_clipping (s->f); + if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR) + { + unsigned long tmp = NS_FACE_BACKGROUND (s->face); + NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face); + NS_FACE_FOREGROUND (s->face) = tmp; } + + ns_unfocus (s->f); break; case GLYPHLESS_GLYPH: n = ns_get_glyph_string_clip_rect (s, r); - if (ns_clip_to_rect (s->f, r, n)) - { - if (s->for_overlaps || (s->cmp_from > 0 - && ! s->first_glyph->u.cmp.automatic)) - s->background_filled_p = 1; - else - ns_maybe_dumpglyphs_background - (s, s->first_glyph->type == COMPOSITE_GLYPH); - /* ... */ - /* Not yet implemented. */ - /* ... */ - ns_reset_clipping (s->f); - } + ns_focus (s->f, r, n); + + if (s->for_overlaps || (s->cmp_from > 0 + && ! s->first_glyph->u.cmp.automatic)) + s->background_filled_p = 1; + else + ns_maybe_dumpglyphs_background + (s, s->first_glyph->type == COMPOSITE_GLYPH); + /* ... */ + /* Not yet implemented. */ + /* ... */ + ns_unfocus (s->f); break; default: @@ -4153,11 +4215,9 @@ overwriting cursor (usually when cursor on a tab). */ if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX) { n = ns_get_glyph_string_clip_rect (s, r); - if (ns_clip_to_rect (s->f, r, n)) - { - ns_dumpglyphs_box_or_relief (s); - ns_reset_clipping (s->f); - } + ns_focus (s->f, r, n); + ns_dumpglyphs_box_or_relief (s); + ns_unfocus (s->f); } s->num_clips = 0; @@ -4963,7 +5023,7 @@ static Lisp_Object ns_string_to_lispmod (const char *s) ns_after_update_window_line, ns_update_window_begin, ns_update_window_end, - ns_flush_display, /* flush_display */ + 0, /* flush_display */ x_clear_window_mouse_face, x_get_glyph_overhangs, x_fix_overlapping_area, @@ -8101,23 +8161,7 @@ - (void)drawRect: (NSRect)rect ns_clear_frame_area (emacsframe, x, y, width, height); block_input (); - /* Get only the precise dirty rectangles to avoid redrawing - potentially large areas of the frame that haven't changed. - - I'm not sure this actually provides much of a performance benefit - as it's hard to benchmark, but it certainly doesn't seem to - hurt. */ - [self getRectsBeingDrawn:&rectList count:&numRects]; - for (int i = 0 ; i < numRects ; i++) - { - NSRect r = rectList[i]; - - NSTRACE_RECT ("r", r); - - expose_frame (emacsframe, - NSMinX (r), NSMinY (r), - NSWidth (r), NSHeight (r)); - } + expose_frame (emacsframe, x, y, width, height); unblock_input (); -- 2.20.1 >From e344535dead54eb63b6c9a7d2135a005ef0181b2 Mon Sep 17 00:00:00 2001 From: Alan Third Date: Sun, 10 Feb 2019 10:59:29 +0000 Subject: [PATCH 4/4] Draw to offscreen buffer on macOS * src/nsfns.m (x_set_background_color): Clear the frame after changing the background color, not before. * src/nsterm.h (drawingBuffer, gc): New variables. ([EmacsView focusOnDrawingBuffer]): ([EmacsView copyRect:to:]): ([EmacsView createDrawingBufferWithRect:]): New methods. * src/nsterm.m (ns_update_begin): (ns_update_end): (ns_focus): (ns_unfocus): Handle drawing to offscreen buffer. (ns_clip_to_row): Use ns_row_rect. (ns_copy_bits): Remove unused function. (ns_scroll_run): (ns_shift_glyphs_for_insert): Use new scrolling method. (ns_draw_fringe_bitmap): (ns_dumpglyphs_image): When drawing to the offscreen buffer, flip images so they appear the right way up. (ns_draw_window_cursor): Don't disable screen updates. ([EmacsView updateFrameSize:]): Update the size of the offscreen buffer. ([EmacsView initFrameFromEmacs:]): Create offscreen buffer. ([EmacsView windowDidChangeBackingProperties:]): ([EmacsView createDrawingBufferWithRect:]): ([EmacsView focusOnDrawingBuffer]): ([EmacsView copyRect]): New methods. ([EmacsView viewWillDraw]): Remove method as it no longer does anything useful. ([EmacsView drawRect:]): Handle drawing from offscreen buffer. --- src/nsfns.m | 10 +-- src/nsterm.h | 10 +++ src/nsterm.m | 230 ++++++++++++++++++++++++++++++++++----------------- 3 files changed, 166 insertions(+), 84 deletions(-) diff --git a/src/nsfns.m b/src/nsfns.m index 59798d3bdd..edcdb988f7 100644 --- a/src/nsfns.m +++ b/src/nsfns.m @@ -286,11 +286,6 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side error ("Unknown color"); } - /* clear the frame; in some instances the NS-internal GC appears not to - update, or it does update and cannot clear old text properly */ - if (FRAME_VISIBLE_P (f)) - ns_clear_frame (f); - [col retain]; [f->output_data.ns->background_color release]; f->output_data.ns->background_color = col; @@ -319,7 +314,10 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side } if (FRAME_VISIBLE_P (f)) - SET_FRAME_GARBAGED (f); + { + SET_FRAME_GARBAGED (f); + ns_clear_frame (f); + } } unblock_input (); } diff --git a/src/nsterm.h b/src/nsterm.h index 35dd9b3c3b..74ffa0b564 100644 --- a/src/nsterm.h +++ b/src/nsterm.h @@ -416,6 +416,10 @@ typedef id instancetype; int maximized_width, maximized_height; NSWindow *nonfs_window; BOOL fs_is_native; +#ifdef NS_IMPL_COCOA + NSBitmapImageRep *drawingBuffer; + NSGraphicsContext *gc; +#endif @public struct frame *emacsframe; int rows, cols; @@ -456,6 +460,12 @@ typedef id instancetype; #endif - (int)fullscreenState; +#ifdef NS_IMPL_COCOA +- (void)focusOnDrawingBuffer; +#endif +- (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect; +- (void)createDrawingBufferWithRect:(NSRect)rect; + /* Non-notification versions of NSView methods. Used for direct calls. */ - (void)windowWillEnterFullScreen; - (void)windowDidEnterFullScreen; diff --git a/src/nsterm.m b/src/nsterm.m index 6529a50298..b6ef05ce27 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -1077,7 +1077,11 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) #endif ns_updating_frame = f; +#ifdef NS_IMPL_COCOA + [FRAME_NS_VIEW (f) focusOnDrawingBuffer]; +#else [view lockFocus]; +#endif /* drawRect may have been called for say the minibuffer, and then clip path is for the minibuffer. But the display engine may draw more because @@ -1192,12 +1196,16 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) /* if (f == MOUSE_HL_INFO (f)->mouse_face_mouse_frame) */ MOUSE_HL_INFO (f)->mouse_face_defer = 0; +#ifdef NS_IMPL_COCOA + [NSGraphicsContext setCurrentContext:nil]; +#else block_input (); [view unlockFocus]; [[view window] flushWindow]; unblock_input (); +#endif ns_updating_frame = NULL; } @@ -1212,6 +1220,8 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) the entire window. -------------------------------------------------------------------------- */ { + EmacsView *view = FRAME_NS_VIEW (f); + NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_focus"); if (r != NULL) { @@ -1219,27 +1229,34 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) } if (f != ns_updating_frame) +#ifdef NS_IMPL_COCOA + [view focusOnDrawingBuffer]; +#else { - NSView *view = FRAME_NS_VIEW (f); if (view != focus_view) { if (focus_view != NULL) { [focus_view unlockFocus]; [[focus_view window] flushWindow]; -/*debug_lock--; */ } if (view) [view lockFocus]; focus_view = view; -/*if (view) debug_lock++; */ } } +#endif /* clipping */ if (r) { +#ifdef NS_IMPL_COCOA + int i; + for (i = 0 ; i < n ; i++) + [view setNeedsDisplayInRect:r[i]]; +#endif + [[NSGraphicsContext currentContext] saveGraphicsState]; if (n == 2) NSRectClipList (r, 2); @@ -1264,6 +1281,7 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) gsaved = NO; } +#ifdef NS_IMPL_GNUSTEP if (f != ns_updating_frame) { if (focus_view != NULL) @@ -1271,9 +1289,9 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) [focus_view unlockFocus]; [[focus_view window] flushWindow]; focus_view = NULL; -/*debug_lock--; */ } } +#endif } @@ -1285,16 +1303,7 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) -------------------------------------------------------------------------- */ { struct frame *f = XFRAME (WINDOW_FRAME (w)); - NSRect clip_rect; - int window_x, window_y, window_width; - - window_box (w, area, &window_x, &window_y, &window_width, 0); - - clip_rect.origin.x = window_x; - clip_rect.origin.y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, row->y)); - clip_rect.origin.y = max (clip_rect.origin.y, window_y); - clip_rect.size.width = window_width; - clip_rect.size.height = row->visible_height; + NSRect clip_rect = ns_row_rect (w, row, area); ns_focus (f, &clip_rect, 1); } @@ -2757,22 +2766,6 @@ so some key presses (TAB) are swallowed by the system. */ return; } -static void -ns_copy_bits (struct frame *f, NSRect src, NSRect dest) -{ - NSTRACE ("ns_copy_bits"); - - if (FRAME_NS_VIEW (f)) - { - hide_bell(); // Ensure the bell image isn't scrolled. - - ns_focus (f, &dest, 1); - [FRAME_NS_VIEW (f) scrollRect: src - by: NSMakeSize (dest.origin.x - src.origin.x, - dest.origin.y - src.origin.y)]; - ns_unfocus (f); - } -} static void ns_scroll_run (struct window *w, struct run *run) @@ -2825,8 +2818,12 @@ so some key presses (TAB) are swallowed by the system. */ { NSRect srcRect = NSMakeRect (x, from_y, width, height); NSRect dstRect = NSMakeRect (x, to_y, width, height); + EmacsView *view = FRAME_NS_VIEW (f); - ns_copy_bits (f, srcRect , dstRect); + [view copyRect:srcRect to:dstRect]; +#ifdef NS_IMPL_COCOA + [view setNeedsDisplayInRect:srcRect]; +#endif } unblock_input (); @@ -2880,20 +2877,12 @@ so some key presses (TAB) are swallowed by the system. */ External (RIF): copy an area horizontally, don't worry about clearing src -------------------------------------------------------------------------- */ { - //NSRect srcRect = NSMakeRect (x, y, width, height); + NSRect srcRect = NSMakeRect (x, y, width, height); NSRect dstRect = NSMakeRect (x+shift_by, y, width, height); NSTRACE ("ns_shift_glyphs_for_insert"); - /* This doesn't work now as we copy the "bits" before we've had a - chance to actually draw any changes to the screen. This means in - certain circumstances we end up with copies of the cursor all - over the place. Just mark the area dirty so it is redrawn later. - - FIXME: Work out how to do this properly. */ - // ns_copy_bits (f, srcRect, dstRect); - - [FRAME_NS_VIEW (f) setNeedsDisplayInRect:dstRect]; + [FRAME_NS_VIEW (f) copyRect:srcRect to:dstRect]; } @@ -2974,9 +2963,6 @@ so some key presses (TAB) are swallowed by the system. */ struct face *face = p->face; static EmacsImage **bimgs = NULL; static int nBimgs = 0; - NSRect clearRect = NSZeroRect; - NSRect imageRect = NSZeroRect; - NSRect rowRect = ns_row_rect (w, row, ANY_AREA); NSTRACE_WHEN (NSTRACE_GROUP_FRINGE, "ns_draw_fringe_bitmap"); NSTRACE_MSG ("which:%d cursor:%d overlay:%d width:%d height:%d period:%d", @@ -3056,6 +3042,18 @@ so some key presses (TAB) are swallowed by the system. */ NSTRACE_RECT ("fromRect", fromRect); + /* Because we're drawing into an offscreen buffer which isn't + flipped, the images come out upside down. To work around it + we need to do some fancy transforms. */ + { + NSAffineTransform *transform = [NSAffineTransform transform]; + [transform translateXBy:0 yBy:NSMaxY(r)]; + [transform scaleXBy:1 yBy:-1]; + [transform concat]; + + r.origin.y = 0; + } + [img drawInRect: r fromRect: fromRect operation: NSCompositingOperationSourceOver @@ -3166,15 +3164,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors. else [FRAME_CURSOR_COLOR (f) set]; -#ifdef NS_IMPL_COCOA - /* TODO: This makes drawing of cursor plus that of phys_cursor_glyph - atomic. Cleaner ways of doing this should be investigated. - One way would be to set a global variable DRAWING_CURSOR - when making the call to draw_phys..(), don't focus in that - case, then move the ns_unfocus() here after that call. */ - NSDisableScreenUpdates (); -#endif - switch (cursor_type) { case DEFAULT_CURSOR: @@ -3208,11 +3197,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors. /* draw the character under the cursor */ if (cursor_type != NO_CURSOR) draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR); - -#ifdef NS_IMPL_COCOA - NSEnableScreenUpdates (); -#endif - } @@ -3297,6 +3281,7 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors. ns_unfocus (f); } + static void ns_show_hourglass (struct frame *f) { @@ -3794,12 +3779,29 @@ Function modeled after x_draw_glyph_string_box (). NSRect ir = NSMakeRect (s->slice.x, s->img->height - s->slice.y - s->slice.height, s->slice.width, s->slice.height); + + /* Because we're drawing into an offscreen buffer which isn't + flipped, the images come out upside down. To work around it + we need to do some fancy transforms. */ + NSAffineTransform *transform = [NSAffineTransform transform]; + + /* FIXME: It should be faster to just reverse the transform than + save and restore the graphics state. */ + [NSGraphicsContext saveGraphicsState]; + [transform translateXBy:0 yBy:NSMaxY(dr)]; + [transform scaleXBy:1 yBy:-1]; + [transform concat]; + + dr.origin.y = 0; + [img drawInRect: dr fromRect: ir operation: NSCompositingOperationSourceOver fraction: 1.0 respectFlipped: YES hints: nil]; + + [NSGraphicsContext restoreGraphicsState]; #else [img compositeToPoint: NSMakePoint (x, y + s->slice.height) operation: NSCompositingOperationSourceOver]; @@ -7010,6 +7012,7 @@ - (void) updateFrameSize: (BOOL) delay from non-native fullscreen, in other circumstances it appears to be a noop. (bug#28872) */ wr = NSMakeRect (0, 0, neww, newh); + [self createDrawingBufferWithRect:wr]; [view setFrame: wr]; // to do: consider using [NSNotificationCenter postNotificationName:]. @@ -7349,6 +7352,8 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f maximizing_resize = NO; #endif + [self createDrawingBufferWithRect:r]; + win = [[EmacsWindow alloc] initWithContentRect: r styleMask: (FRAME_UNDECORATED (f) @@ -8139,40 +8144,109 @@ - (instancetype)toggleToolbar: (id)sender } -- (void)viewWillDraw +- (void)createDrawingBufferWithRect:(NSRect)rect + /* Create and store a new NSBitmapImageRep for Emacs to draw + into. + + Drawing to an offscreen bitmap doesn't work in GNUstep as there's + a bug in graphicsContextWithBitmapImageRep + (https://savannah.gnu.org/bugs/?38405). So under GNUstep we + retain the old method of drawing direct to the EmacsView. */ { - /* If the frame has been garbaged there's no point in redrawing - anything. */ - if (FRAME_GARBAGED_P (emacsframe)) - [self setNeedsDisplay:NO]; +#ifdef NS_IMPL_COCOA + if (drawingBuffer != nil) + { + [gc release]; + [drawingBuffer release]; + } + + drawingBuffer = [[self bitmapImageRepForCachingDisplayInRect:rect] retain]; + + /* Cache the graphics context, as recreating it each time we want to + draw is very slow. */ + gc = [[NSGraphicsContext graphicsContextWithBitmapImageRep:drawingBuffer] + retain]; +#endif } -- (void)drawRect: (NSRect)rect + +#ifdef NS_IMPL_COCOA +- (void)focusOnDrawingBuffer { - int x = NSMinX (rect), y = NSMinY (rect); - int width = NSWidth (rect), height = NSHeight (rect); + [NSGraphicsContext setCurrentContext:gc]; +} + + +- (void)windowDidChangeBackingProperties:(NSNotification *)notification + /* Update the drawing buffer when the backing scale factor changes. */ +{ + CGFloat old = [[[notification userInfo] + objectForKey:@"NSBackingPropertyOldScaleFactorKey"] + doubleValue]; + CGFloat new = [[self window] backingScaleFactor]; + + if (old != new) + { + NSRect frame = [self frame]; + [self createDrawingBufferWithRect:frame]; + ns_clear_frame (emacsframe); + expose_frame (emacsframe, 0, 0, NSWidth (frame), NSHeight (frame)); + } +} +#endif + +- (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect +{ + NSTRACE ("[EmacsView copyRect:To:]"); + NSTRACE_RECT ("Source", srcRect); + NSTRACE_RECT ("Destination", dstRect); + +#ifdef NS_IMPL_COCOA + [drawingBuffer drawInRect:dstRect + fromRect:srcRect + operation:NSCompositingOperationCopy + fraction:1.0 + respectFlipped:NO + hints:nil]; + + [self setNeedsDisplayInRect:dstRect]; +#else + hide_bell(); // Ensure the bell image isn't scrolled. + + ns_focus (emacsframe, &dstRect, 1); + [self scrollRect: srcRect + by: NSMakeSize (dstRect.origin.x - srcRect.origin.x, + dstRect.origin.y - srcRect.origin.y)]; + ns_unfocus (emacsframe); +#endif +} + + +- (void)drawRect: (NSRect)rect +{ NSTRACE ("[EmacsView drawRect:" NSTRACE_FMT_RECT "]", NSTRACE_ARG_RECT(rect)); if (!emacsframe || !emacsframe->output_data.ns) return; - ns_clear_frame_area (emacsframe, x, y, width, height); - block_input (); +#ifdef NS_IMPL_COCOA + [drawingBuffer drawInRect:rect + fromRect:rect + operation:NSCompositingOperationSourceOver + fraction:1 + respectFlipped:NO + hints:nil]; +#else + int x = NSMinX (rect), y = NSMinY (rect); + int width = NSWidth (rect), height = NSHeight (rect); + block_input (); + ns_clear_frame_area (emacsframe, x, y, width, height); expose_frame (emacsframe, x, y, width, height); - unblock_input (); - - /* - drawRect: may be called (at least in Mac OS X 10.5) for invisible - views as well for some reason. Thus, do not infer visibility - here. - - emacsframe->async_visible = 1; - emacsframe->async_iconified = 0; - */ +#endif } -- 2.20.1