Subject: Re: bug#32932: 27.0.50; render bugs on macOS Mojave

From: Alan Third <[email-masked]>
Date: Sun, 4 Nov 2018 13:24:04 +0000

On Sat, Nov 03, 2018 at 11:03:27PM +0200, Eli Zaretskii wrote:
> > Or perhaps expose_frame actually thinks it should be blank at that
> > moment, but for some reason we’ve not marked the whole window or
> > whatever as dirty?
> > 
> > So we’re to display an image but it’s not loaded yet, so redisplay
> > blanks the window for the time being, but we fail to mark it dirty or
> > garbaged. Expose_frame comes along and draws the bit we’ve previously
> > asked it to draw. Finally the image loads and redisplay marks the
> > whole window as dirty leading to everything catching up at the next
> > expose_frame.
> 
> I'm puzzled by this description, and actually by the whole larger
> picture.  You see, I originally thought you had a problem of
> flickering caused by redrawing the cursor, which was said to trigger
> redrawing of the entire screen line where the cursor was, instead of
> redrawing just the character under the cursor.  Is that still the
> problem we are discussing?  If so, how does visiting the image file
> come into play, and where is cursor positioned in this scenario?

Apologies, Aaron’s repeatable test case involves a dired buffer of
images and hitting return on one of the images. There’s a pause while
it loads, during which the line with the cursor on it sometimes
blanks. Then the image loads.

I think what’s probably happening is that when the image begins to
load the emacs window containing the dired buffer is marked as
garbaged as it’s going to be replaced by the buffer containing the
image, however because there’s a reasonably long gap between the user
requesting the opening of the image, and the image actually loading
redisplay and expose_frame have time to run.

Because the window is marked as garbaged expose_window doesn’t do
anything.

This would be fine except we seem to have a rogue clear_area
somewhere. NSTrace doesn’t show it running, and they happen too often
for me to reasonably use a breakpoint to find it.

Here’s some output from NSTrace:

nsterm.m  : 4441: [53008]  ns_select
nsterm.m  : 5391: [53009]  | [EmacsApp run]
nsterm.m  : 5458: [53010]  | | [EmacsApp sendEvent:]
nsterm.m  : 5459: [53011]  | | +--- Type: 10

Here we have the user hitting return in the image file in dired.

nsterm.m  : 6076: [53012]  | | | [EmacsView keyDown:]
nsterm.m  : 4195: [53013]  | | | | ns_send_appdefined(-1)
nsterm.m  : 5458: [53014]  | | [EmacsApp sendEvent:]
nsterm.m  : 5459: [53015]  | | +--- Type: 15
nsterm.m  : 5429: [53016]  | | | [EmacsApp stop:]
nsterm.m  : 4359: [53017]  | ns_read_socket
nsterm.m  : 4359: [53018]  | ns_read_socket
nsterm.m  : 4195: [53019]  | | ns_send_appdefined(-1)
nsterm.m  : 5391: [53020]  | | [EmacsApp run]
nsterm.m  : 5458: [53021]  | | | [EmacsApp sendEvent:]
nsterm.m  : 5459: [53022]  | | | +--- Type: 15
nsterm.m  : 5429: [53023]  | | | | [EmacsApp stop:]
nsterm.m  : 4359: [53024]  | ns_read_socket
nsterm.m  : 4195: [53025]  | | ns_send_appdefined(-1)
nsterm.m  : 5391: [53026]  | | [EmacsApp run]
nsterm.m  : 5458: [53027]  | | | [EmacsApp sendEvent:]
nsterm.m  : 5459: [53028]  | | | +--- Type: 15
nsterm.m  : 5429: [53029]  | | | | [EmacsApp stop:]
nsterm.m  : 4359: [53030]  ns_read_socket
nsterm.m  : 4195: [53031]  | ns_send_appdefined(-1)
nsterm.m  : 5391: [53032]  | [EmacsApp run]
nsterm.m  : 5458: [53033]  | | [EmacsApp sendEvent:]
nsterm.m  : 5459: [53034]  | | +--- Type: 15
nsterm.m  : 5429: [53035]  | | | [EmacsApp stop:]

I believe this is now Emacs trying to modify the cursor. The cursor is
at pixel position (381, 268).

nsterm.m  : 2299: [53036]  ns_lisp_to_color
nsterm.m  : 2177: [53037]  | ns_get_color(LightGoldenrod3, **)
nsterm.m  : 4035: [53038]  ns_draw_glyph_string
nsterm.m  : 1191: [53039]  | ns_clip_to_rect
ns_clip_to_rect
nsterm.m  : 1195: [53040]  | +--- r: (X:10 Y:268)/(W:560 H:14)
nsterm.m  : 1214: [53041]  | +--- New dirty rect: (X:10 Y:268)/(W:560 H:14)
nsterm.m  : 3048: [53042]  ns_draw_window_cursor
nsterm.m  : 3048: [53043]  ns_draw_window_cursor
nsterm.m  : 3048: [53044]  ns_draw_window_cursor
nsterm.m  : 1191: [53045]  | ns_clip_to_rect
ns_clip_to_rect
nsterm.m  : 1195: [53046]  | +--- r: (X:381 Y:268)/(W:7 H:14)
nsterm.m  : 1214: [53047]  | +--- New dirty rect: (X:381 Y:268)/(W:7 H:14)
nsterm.m  : 3048: [53048]  ns_draw_window_cursor
nsimage.m :   61: [53049]  ns_image_for_XPM

I think these are functions called by redisplay_internal. I believe
they’re updating the modeline and/or the minibuffer.

nsterm.m  : 1060: [53050]  ns_update_begin
nsterm.m  : 1015: [53051]  | ns_update_auto_hide_menu_bar
nsterm.m  : 7772: [53052]  | [EmacsView isFullscreen] ->> 0
nsterm.m  : 1109: [53053]  ns_update_window_begin
nsterm.m  : 4035: [53054]  ns_draw_glyph_string
nsterm.m  : 1191: [53055]  | ns_clip_to_rect
ns_clip_to_rect
nsterm.m  : 1195: [53056]  | +--- r: (X:10 Y:492)/(W:560 H:14)
nsterm.m  : 1214: [53057]  | +--- New dirty rect: (X:10 Y:492)/(W:560 H:14)
nsterm.m  : 2682: [53058]  ns_clear_frame_area
nsterm.m  : 1191: [53059]  | ns_clip_to_rect
ns_clip_to_rect
nsterm.m  : 1195: [53060]  | +--- r: (X:416 Y:492)/(W:154 H:14)
nsterm.m  : 1214: [53061]  | +--- New dirty rect: (X:416 Y:492)/(W:154 H:14)
nsterm.m  : 1139: [53062]  ns_update_window_end
nsterm.m  : 3048: [53063]  | ns_draw_window_cursor
nsterm.m  : 1176: [53064]  ns_update_end
nsterm.m  : 2532: [53065]  ns_frame_up_to_date
nsterm.m  : 4359: [53066]  ns_read_socket
nsterm.m  : 4195: [53067]  | ns_send_appdefined(-1)
nsterm.m  : 5391: [53068]  | [EmacsApp run]

Now drawRect is called. It currently steps through each of the unique
dirty rectangles calling expose_frame.

nsterm.m  : 8100: [53069]  | | [EmacsView drawRect:(X:10 Y:268)/(W:560
H:238)]

modeline/minibuffer:

nsterm.m  : 8117: [53070]  | | +--- Exposing rect: (X:10 Y:492)/(W:560 H:14)
nsterm.m  : 3048: [53071]  | | | ns_draw_window_cursor
nsterm.m  : 4035: [53072]  | | | ns_draw_glyph_string
nsterm.m  : 1191: [53073]  | | | | ns_clip_to_rect
ns_clip_to_rect
nsterm.m  : 1195: [53074]  | | | | +--- r: (X:10 Y:492)/(W:560 H:14)
nsterm.m  : 3624: [53075]  | | | | ns_maybe_dumpglyphs_background
nsterm.m  : 1229: [53076]  | | | | ns_reset_clipping
nsterm.m  : 2922: [53077]  | | | ns_draw_fringe_bitmap
nsterm.m  : 2924: [53078]  | | | +--- which:0 cursor:0 overlay:0 width:0 height:0 period:0
nsterm.m  : 1191: [53079]  | | | | ns_clip_to_rect
ns_clip_to_rect
nsterm.m  : 1195: [53080]  | | | | +--- r: (X:2 Y:492)/(W:8 H:14)
nsterm.m  : 2961: [53081]  | | | +--- clearRect: (X:2 Y:492)/(W:8 H:14)
nsterm.m  : 1229: [53082]  | | | | ns_reset_clipping
nsterm.m  : 2922: [53083]  | | | ns_draw_fringe_bitmap
nsterm.m  : 2924: [53084]  | | | +--- which:0 cursor:0 overlay:0 width:0 height:0 period:0
nsterm.m  : 1191: [53085]  | | | | ns_clip_to_rect
ns_clip_to_rect
nsterm.m  : 1195: [53086]  | | | | +--- r: (X:570 Y:492)/(W:8 H:14)
nsterm.m  : 2961: [53087]  | | | +--- clearRect: (X:570 Y:492)/(W:8 H:14)
nsterm.m  : 1229: [53088]  | | | | ns_reset_clipping
nsterm.m  : 3048: [53089]  | | | ns_draw_window_cursor

Here it finally reaches the rectangle that contains the cursor. It
appears to do nothing. Not even clear the area.

nsterm.m  : 8117: [53090]  | | +--- Exposing rect: (X:10 Y:268)/(W:560 H:14)
nsterm.m  : 5458: [53091]  | | [EmacsApp sendEvent:]
nsterm.m  : 5459: [53092]  | | +--- Type: 11

I’ve tried to work out if drawRect is ‘helpfully’ clearing the dirty
rectangles for us, but I don’t think it is.

Perhaps this approach is just doomed to suffer from issues like these.
-- 
Alan Third