unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* update_window:  w->desired_matrix is a partial representation.
@ 2019-01-17  8:05 Keith David Bershatsky
  2019-01-17 13:56 ` Eli Zaretskii
  0 siblings, 1 reply; 5+ messages in thread
From: Keith David Bershatsky @ 2019-01-17  8:05 UTC (permalink / raw)
  To: Emacs Devel

I am working on optimizing redrawing fake cursors in relation to implementing proposed feature requests #22873 (multiple fake cursors); and, #17684 (crosshairs / visible fill-column).

HYPOTHETICAL:

*  The header-line-format is non-nil.

*  Line numbering is turned on.

*  The window contains a word-wrapped line consisting of 4 screen lines; followed by a hard return; followed by miscellaneous lines of text.

0.  My header-line format.
1.  12345⤸
2.  67890⤸
3.  12345⤸
4.  67890
5.
6.  Every good boy deserves fudge.
7.  Once upon a time there lived a ....

*  The user interactively calls undo, and text (!) is inserted into the middle of the first screen line.

*  The word-wrapped line in the first step increases in length from 4 screen lines to 5 screen lines; followed by a hard return; followed by miscellaneous lines.

0.  My header-line format.
1.  12!34⤸
2.  56789⤸
3.  01234⤸
4.  56789⤸
5.  0
6.  
7.  Every good boy deserves fudge.
8.  Once upon a time there lived a ....

RESULT:

* All subsequent screen lines get pushed down one row to make room for the new row (VPOS 5) that was inserted by the undo.

* update_window_line reports that VPOS lines 1 to 5 are changed_p.

* If we dump_glyph_row for each line of w->desired_matrix from within update_window, we observe that the empty line between the first 6 screen lines (including the header-line) and the subsequent miscellaneous lines is _not_ present.  VPOS 6 is "Every good by deserves fudge."

ERRONEOUS EXPECTATION:  I expected VPOS 6 of w->desired_matrix to be the line containing a visible line number 6, _not_ the subsequent line that contains "Every good by deserves fudge."

QUESTION #1:  From within update_window, is it possible to programmatically access the glyph_row containing the visible line number 6?

QUESTION #2:  From within update_window, is it possible to programmatically test to see whether VPOS 6 and all subsequent screen lines have been pushed downwards to make room for the new VPOS 5?



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: update_window:  w->desired_matrix is a partial representation.
  2019-01-17  8:05 update_window: w->desired_matrix is a partial representation Keith David Bershatsky
@ 2019-01-17 13:56 ` Eli Zaretskii
  0 siblings, 0 replies; 5+ messages in thread
From: Eli Zaretskii @ 2019-01-17 13:56 UTC (permalink / raw)
  To: Keith David Bershatsky; +Cc: emacs-devel

> Date: Thu, 17 Jan 2019 00:05:28 -0800
> From: Keith David Bershatsky <esq@lawlist.com>
> 
> *  The word-wrapped line in the first step increases in length from 4 screen lines to 5 screen lines; followed by a hard return; followed by miscellaneous lines.
> 
> 0.  My header-line format.
> 1.  12!34⤸
> 2.  56789⤸
> 3.  01234⤸
> 4.  56789⤸
> 5.  0
> 6.  
> 7.  Every good boy deserves fudge.
> 8.  Once upon a time there lived a ....
> 
> RESULT:
> 
> * All subsequent screen lines get pushed down one row to make room for the new row (VPOS 5) that was inserted by the undo.
> 
> * update_window_line reports that VPOS lines 1 to 5 are changed_p.
> 
> * If we dump_glyph_row for each line of w->desired_matrix from within update_window, we observe that the empty line between the first 6 screen lines (including the header-line) and the subsequent miscellaneous lines is _not_ present.  VPOS 6 is "Every good by deserves fudge."
> 
> ERRONEOUS EXPECTATION:  I expected VPOS 6 of w->desired_matrix to be the line containing a visible line number 6, _not_ the subsequent line that contains "Every good by deserves fudge."

When you examine glyph rows of a glyph matrix, pay attention to the
enabled_p flag of each row: a row whose enabled_p flag is reset does
not really reflect the desired contents of the screen, but some random
garbage.

If you are saying that row whose VPOS is 6 has its enabled_p flag set,
and its contents is not what eventually appears on the screen, then
I'd need a precise recipe, preferably without source-level changes to
the current code, to reproduce the situation.  In general, redisplay
in this case can update the screen via a method that scrolls the text
on the glass directly, so you might not see that if you are not
looking in the right place.  But that's just a hunch.

More generally, I don't think a display feature that relies on the
contents of the desired matrix is a good idea, because the desired
matrix is a tool for the display engine to update the screen, and is
not supposed to convey the entire contents of a window, nor even
describe reliably what will be actually updated.  Why did you need to
rely on the desired matrix for your feature?

> QUESTION #1:  From within update_window, is it possible to programmatically access the glyph_row containing the visible line number 6?

You can look at glyph_matrix->rows[6], but the result is reliable only
if the enabled_p flag of that row is set.

> QUESTION #2:  From within update_window, is it possible to programmatically test to see whether VPOS 6 and all subsequent screen lines have been pushed downwards to make room for the new VPOS 5?

You'd need to compare with the current_matrix for that.  But why would
you need to know a thing like that?  Your code shouldn't care how
exactly the display engine decided to update the screen.



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: update_window:  w->desired_matrix is a partial representation.
@ 2019-01-17 18:08 Keith David Bershatsky
  2019-01-19  9:16 ` Eli Zaretskii
  0 siblings, 1 reply; 5+ messages in thread
From: Keith David Bershatsky @ 2019-01-17 18:08 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Thank you, Eli, for reviewing and responding to this particular thread.

VPOS 6 of the desired_matrix is _not_ enabled_p.  Thus, update_window_line is not called on that particular VPOS.

The cache of fake cursors contains relevant coordinates (x, y, hpos, vpos), which are needed for erasing and/or redrawing.  Let us assume that before redisplay, VPOS 5 has a floating BAR_CURSOR (which uses the X and HPOS of the last glyph of the row as a sort of anchor to draw/erase it).

0.  My header-line format.
1.  12345⤸
2.  67890⤸
3.  12345⤸
4.  67890
5.     |
6.  Every good boy deserves fudge.
7.  Once upon a time there lived a ....

When the "!" gets inserted into VPOS 1 and pushes lines 6+ downwards, the floating BAR_CURSOR also gets pushed down.

0.  My header-line format.
1.  12!34⤸
2.  56789⤸
3.  01234⤸
4.  56789⤸
5.  0
6.     |
7.  Every good boy deserves fudge.
8.  Once upon a time there lived a ....

There may not necessarily be a need to remove the BAR_CURSOR at this time.  However, the cached coordinates are now invalid for the floating BAR_CURSOR because Y and VPOS have changed.  When it comes time to remove the BAR_CURSOR at some point in the future, the attempted removal will fail because said removal uses outdated coordinates.

For all lines that get pushed downwards, the fake cursors may not need to be removed at this point in time.  However, the Y and VPOS of all fake cursors in the cache that have VPOS 6+ need to be updated.  And, the last row of the cache that was at the bottom of the screen may no longer exist because it was pushed down beyond the bottom of the screen -- so we need to delete that last row from the cache.

I have a working draft that uses the existing function update_text_area (within dispnew.c) to track changes to each VPOS, and does the following:

-  All cached fake cursors for the changed VPOS are deleted from the cache.

-  The portion of each row that remains the same, do not need the fake cursors to be redrawn (except wherever overlaps occur when rif->write_glyphs gets called) -- instead, just the cache of fake cursors gets updated for that section of the row that remains the same.

-  Subsequent to each call of rif->write_glyphs, the fake cursors get redrawn for the length of what got rewritten by rif->write_glyphs and the cache gets updated accordingly.

-  As to the relevant section from the end of the row to the x-limit that gets cleared with rif->clear_end_of_line, fake cursors are redrawn on just the portion that got erased -- the cache is updated for the entire length between the end of the row and the right window edge (assumes no right margin).

If the above-described plan of attack sounds prudent/efficient, then perhaps the appropriate place to update VPOS 6+ of the cache in this situation is wherever redisplay "scrolls the text on the glass directly"?

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

> Date: [01-17-2019 05:56:18] <17 Jan 2019 15:56:18 +0200>
> From: Eli Zaretskii <eliz@gnu.org>
> To: Keith David Bershatsky <esq@lawlist.com>
> CC: emacs-devel@gnu.org
> Subject: Re: update_window:  w->desired_matrix is a partial representation.
> 
> * * * 
> >
> > ERRONEOUS EXPECTATION:  I expected VPOS 6 of w->desired_matrix to be the line containing a visible line number 6, _not_ the subsequent line that contains "Every good by deserves fudge."
> 
> When you examine glyph rows of a glyph matrix, pay attention to the
> enabled_p flag of each row: a row whose enabled_p flag is reset does
> not really reflect the desired contents of the screen, but some random
> garbage.
> 
> If you are saying that row whose VPOS is 6 has its enabled_p flag set,
> and its contents is not what eventually appears on the screen, then
> I'd need a precise recipe, preferably without source-level changes to
> the current code, to reproduce the situation.  In general, redisplay
> in this case can update the screen via a method that scrolls the text
> on the glass directly, so you might not see that if you are not
> looking in the right place.  But that's just a hunch.
> 
> More generally, I don't think a display feature that relies on the
> contents of the desired matrix is a good idea, because the desired
> matrix is a tool for the display engine to update the screen, and is
> not supposed to convey the entire contents of a window, nor even
> describe reliably what will be actually updated.  Why did you need to
> rely on the desired matrix for your feature?
> 
> > QUESTION #1:  From within update_window, is it possible to programmatically access the glyph_row containing the visible line number 6?
> 
> You can look at glyph_matrix->rows[6], but the result is reliable only
> if the enabled_p flag of that row is set.
> 
> > QUESTION #2:  From within update_window, is it possible to programmatically test to see whether VPOS 6 and all subsequent screen lines have been pushed downwards to make room for the new VPOS 5?
> 
> You'd need to compare with the current_matrix for that.  But why would
> you need to know a thing like that?  Your code shouldn't care how
> exactly the display engine decided to update the screen.



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: update_window:  w->desired_matrix is a partial representation.
  2019-01-17 18:08 Keith David Bershatsky
@ 2019-01-19  9:16 ` Eli Zaretskii
  0 siblings, 0 replies; 5+ messages in thread
From: Eli Zaretskii @ 2019-01-19  9:16 UTC (permalink / raw)
  To: Keith David Bershatsky; +Cc: emacs-devel

> Date:  Thu, 17 Jan 2019 10:08:40 -0800
> From:  Keith David Bershatsky <esq@lawlist.com>
> Cc:  emacs-devel@gnu.org
> 
> VPOS 6 of the desired_matrix is _not_ enabled_p.  Thus, update_window_line is not called on that particular VPOS.

That means the contents of desired_matrix for this VPOS is not useful.

> The cache of fake cursors contains relevant coordinates (x, y, hpos, vpos), which are needed for erasing and/or redrawing.

Why do you cache screen coordinates and not the buffer positions?  If
the screen contents around some cursor don't change, you don't need to
redraw the cursor, so you don't need the coordinates.  And if the
screen does change, more often than not the coordinates will be
inaccurate anyway, and need to be recomputed anew.  So why cache them?

> I have a working draft that uses the existing function update_text_area (within dispnew.c) to track changes to each VPOS, and does the following:
> 
> -  All cached fake cursors for the changed VPOS are deleted from the cache.
> 
> -  The portion of each row that remains the same, do not need the fake cursors to be redrawn (except wherever overlaps occur when rif->write_glyphs gets called) -- instead, just the cache of fake cursors gets updated for that section of the row that remains the same.
> 
> -  Subsequent to each call of rif->write_glyphs, the fake cursors get redrawn for the length of what got rewritten by rif->write_glyphs and the cache gets updated accordingly.
> 
> -  As to the relevant section from the end of the row to the x-limit that gets cleared with rif->clear_end_of_line, fake cursors are redrawn on just the portion that got erased -- the cache is updated for the entire length between the end of the row and the right window edge (assumes no right margin).

This design assumes that update_window_line will be called for each
screen line where you have a cursor and whose contents change in some
way.  There's nothing in the display engine that makes sure this
assumption is true, since the display engine doesn't really know
anything about your fake cursors, does it?  For example, the display
engine could decide to use some optimization based on the buffer text,
which would invalidate your assumption.

Did you consider the alternative of calculating the screen coordinates
of the cursors like we do for the single "normal" cursor?  That is
done in set_cursor_from_row, which is called from display_line and
from several redisplay optimizations that avoid calling display_line.
You could call at the same places your function that recalculates the
coordinates of your cursors, if any, in that screen line, and then
store the calculated coordinates in the window structure, like we do
with the "normal" cursor.

> If the above-described plan of attack sounds prudent/efficient, then perhaps the appropriate place to update VPOS 6+ of the cache in this situation is wherever redisplay "scrolls the text on the glass directly"?

See try_window_id.  Search for "Scroll the display" to see how it
bypasses update_window for the screen lines it scrolls.  Directly
above that you will find code that updates the cursor accordingly.

If you decide to keep your current design, you should augment it so
that any future changes that introduce additional bypasses, such as
the ones in try_window_id, will be supported by fake cursors with
minimum changes.



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: update_window:  w->desired_matrix is a partial representation.
@ 2019-01-19 23:03 Keith David Bershatsky
  0 siblings, 0 replies; 5+ messages in thread
From: Keith David Bershatsky @ 2019-01-19 23:03 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

>> The cache of fake cursors contains relevant coordinates (x, y, hpos, vpos), which are needed for erasing and/or redrawing.

> Why do you cache screen coordinates and not the buffer positions?  If the screen contents around some cursor don't change, you don't need to redraw the cursor, so you don't need the coordinates.  And if the screen does change, more often than not the coordinates will be inaccurate anyway, and need to be recomputed anew.  So why cache them?

As far as I am aware, screen coordinates of a previously laid fake cursor cannot always be extrapolated from a particular buffer position.

Surgical removal of a particular GLYPH or GLYPHLESS fake cursor at the appropriate time in the near to far distant future necessitates that its screen coordinates be either known (cached) or ascertainable.  Removal is needed for a variety of reasons; e.g., PT has moved; the minor-mode for fake cursors has been turned off; etc.

Redrawing fake cursors (without the need to recalculate coordinates) can most likely be reduced to a handful of situations; e.g., Emacs fixed an overlap and erased an adjacent fake cursor in the process.

In the following example, the GLYPHLESS cursor floats in thin air to the right of buffer position 25 on VPOS 5.  [The illustration uses a few spaces before the | character so that we can create the illusion in this thread of a floating BAR_CURSOR.  In Emacs, however, the row ends immediately after the line number and there are no extra spaces filling the gap between the invisible space at the end of the glyph row (which has no buffer position) and the floating BAR_CURSOR.]

0.  My header-line format.
1.  12345⤸
2.  67890⤸
3.  12345⤸
4.  67890
5.     |
6.  Every good boy deserves fudge.
7.  Once upon a time there lived a ....

When the "!" gets inserted into VPOS 1 and pushes lines 6+ downwards, the GLYPHLESS cursor is now floating in thin air to the right of buffer position 26 on VPOS 6.

0.  My header-line format.
1.  12!34⤸
2.  56789⤸
3.  01234⤸
4.  56789⤸
5.  0
6.     |
7.  Every good boy deserves fudge.
8.  Once upon a time there lived a ....

Fake cursors are presently used for two main purposes:

1.  A fake cursor at a specified buffer position; e.g., to use in conjunction with Magnar's multiple-cursors library.  Instead of using a visual overlay such as a solid rectangle to indicate the presence of a fake cursor, Emacs can now draw a BAR_CURSOR or any other cursor-type and color.

2.  A horizontal ruler, and one or two vertical rulers spanning the length/height of the window.  The horizontal/vertical rulers are comprised of GLYPH and GLYPHLESS fake cursors.  One of the vertical rulers track the cursor position, and the other creates a visible fill column indicator.

    2A.  As to GLYPH cursor, Emacs draws the cursor rectangle (e.g, BAR_CURSOR / HBAR_CURSOR) on top of an existing character glyph, and then overwrites the existing character glyph with another imprint.  This is what gives the character with a cursor a bold appearance.

    2B.  As to a GLYPHLESS cursor, Emacs only draws the cursor rectangle in thin air.  These are used along or to intersect a STRETCH glyph such as a tab, and also where no glyph exists.


>> I have a working draft that uses the existing function update_text_area (within dispnew.c) to track changes to each VPOS, and does the following:
>
>> -  All cached fake cursors for the changed VPOS are deleted from the cache.
>
>> -  The portion of each row that remains the same, do not need the fake cursors to be redrawn (except wherever overlaps occur when rif->write_glyphs gets called) -- instead, just the cache of fake cursors gets updated for that section of the row that remains the same.
>
>> -  Subsequent to each call of rif->write_glyphs, the fake cursors get redrawn for the length of what got rewritten by rif->write_glyphs and the cache gets updated accordingly.
>
>> -  As to the relevant section from the end of the row to the x-limit that gets cleared with rif->clear_end_of_line, fake cursors are redrawn on just the portion that got erased -- the cache is updated for the entire length between the end of the row and the right window edge (assumes no right margin).

> This design assumes that update_window_line will be called for each screen line where you have a cursor and whose contents change in some way.  There's nothing in the display engine that makes sure this assumption is true, since the display engine doesn't really know anything about your fake cursors, does it?  For example, the display engine could decide to use some optimization based on the buffer text, which would invalidate your assumption.

> Did you consider the alternative of calculating the screen coordinates of the cursors like we do for the single "normal" cursor?  That is done in set_cursor_from_row, which is called from display_line and from several redisplay optimizations that avoid calling display_line.  You could call at the same places your function that recalculates the coordinates of your cursors, if any, in that screen line, and then store the calculated coordinates in the window structure, like we do with the "normal" cursor.

I appreciate you letting me know that Emacs may update the window text in areas other than update_window_line; e.g., due to an existing optimization.  I will need to work on tracking all of them down and chisel-away at dealing with each one.

set_cursor_from_row loops through the glyph row containing point.  When fake cursors are initially calculated and whenever a significant buffer change occurs (e.g., when PT moves), Emacs loops through all enabled rows of the current matrix, and loops through each glyph row looking for a particular buffer position and/or relative x coordinate.  Because this is somewhat cumbersome for Emacs to do every command loop, I am working on ideas to optimize the need to recalculate and draw/erase the fake cursors -- i.e., have Emacs do the bare minimum based upon areas of the window that have changed since the previous command loop.

>> If the above-described plan of attack sounds prudent/efficient, then perhaps the appropriate place to update VPOS 6+ of the cache in this situation is wherever redisplay "scrolls the text on the glass directly"?

> See try_window_id.  Search for "Scroll the display" to see how it bypasses update_window for the screen lines it scrolls.  Directly above that you will find code that updates the cursor accordingly.

> If you decide to keep your current design, you should augment it so that any future changes that introduce additional bypasses, such as the ones in try_window_id, will be supported by fake cursors with minimum changes.

AWESOME!  Thank you for pinpointing the exact location where redisplay "scrolls the text on the glass directly".  Greatly appreciated!



^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2019-01-19 23:03 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-01-17  8:05 update_window: w->desired_matrix is a partial representation Keith David Bershatsky
2019-01-17 13:56 ` Eli Zaretskii
  -- strict thread matches above, loose matches on Subject: below --
2019-01-17 18:08 Keith David Bershatsky
2019-01-19  9:16 ` Eli Zaretskii
2019-01-19 23:03 Keith David Bershatsky

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).