--- /dev/null
+/* Display generation from window structure and buffer text.
+ Copyright (C) 1994, 1995, 1996 Board of Trustees, University of Illinois.
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Copyright (C) 1995, 1996 Ben Wing.
+ Copyright (C) 1995 Sun Microsystems, Inc.
+ Copyright (C) 1996 Chuck Thompson.
+
+This file is part of XEmacs.
+
+XEmacs is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+XEmacs is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with XEmacs; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+/* Synched up with: Not in FSF. */
+
+/* Author: Chuck Thompson */
+
+/* Fixed up by Ben Wing for Mule */
+
+/* This file has been Mule-ized. */
+
+/*****************************************************************************
+ The Golden Rules of Redisplay
+
+ First: It Is Better To Be Correct Than Fast
+ Second: Thou Shalt Not Run Elisp From Within Redisplay
+ Third: It Is Better To Be Fast Than Not To Be
+ ****************************************************************************/
+
+#include <config.h>
+#include "lisp.h"
+
+#include "buffer.h"
+#include "commands.h"
+#include "debug.h"
+#include "device.h"
+#include "elhash.h"
+#include "extents.h"
+#include "faces.h"
+#include "frame.h"
+#include "glyphs.h"
+#include "gutter.h"
+#include "insdel.h"
+#include "menubar.h"
+#include "objects.h"
+#include "process.h"
+#include "redisplay.h"
+#include "toolbar.h"
+#include "window.h"
+#include "line-number.h"
+#ifdef FILE_CODING
+#include "file-coding.h"
+#endif
+
+#include "sysfile.h"
+
+#ifdef HAVE_TTY
+#include "console-tty.h"
+#endif /* HAVE_TTY */
+
+/* Note: We have to be careful throughout this code to properly handle
+ and differentiate between Bufbytes and Emchars.
+
+ Since strings are generally composed of Bufbytes, I've taken the tack
+ that any contiguous set of Bufbytes is called a "string", while
+ any contiguous set of Emchars is called an "array". */
+
+/* Return value to indicate a failure by an add_*_rune routine to add
+ a rune, but no propagation information needs to be returned. */
+#define ADD_FAILED (prop_block_dynarr *) 1
+
+#define BEGIN_GLYPHS 0
+#define END_GLYPHS 1
+#define LEFT_GLYPHS 2
+#define RIGHT_GLYPHS 3
+
+#define VERTICAL_CLIP(w, display) \
+ ((WINDOW_TTY_P (w) | (!display && scroll_on_clipped_lines)) \
+ ? INT_MAX \
+ : vertical_clip)
+
+/* The following structures are completely private to redisplay.c so
+ we put them here instead of in a header file, for modularity. */
+
+/* NOTE: Bytinds not Bufpos's in this structure. */
+
+typedef struct position_redisplay_data_type
+{
+ /* This information is normally filled in by the create_*_block
+ routines and is used by the add_*_rune routines. */
+ Lisp_Object window;
+ /* if we are working with strings rather than buffers we need a
+ handle to the string */
+ Lisp_Object string;
+ struct device *d;
+ struct display_block *db;
+ struct display_line *dl;
+ Emchar ch; /* Character that is to be added. This is
+ used to communicate this information to
+ add_emchar_rune(). */
+ Lisp_Object last_charset; /* The charset of the previous character.
+ Used to optimize some lookups -- we
+ only have to do some things when
+ the charset changes. */
+ face_index last_findex; /* The face index of the previous character.
+ Needed to ensure the validity of the
+ last_charset optimization. */
+
+ int last_char_width; /* The width of the previous character. */
+ int font_is_bogus; /* If true, it means we couldn't instantiate
+ the font for this charset, so we substitute
+ ~'s from the ASCII charset. */
+ Bytind bi_bufpos;
+ Bytind bi_endpos;
+ int pixpos;
+ int max_pixpos;
+ int blank_width; /* Width of the blank that is to be added.
+ This is used to communicate this information
+ to add_blank_rune().
+
+ This is also used rather cheesily to
+ communicate the width of the eol-cursor-size
+ blank that exists at the end of the line.
+ add_emchar_rune() is called cheesily with
+ the non-printing char '\n', which is stuck
+ in the output routines with its width being
+ BLANK_WIDTH. */
+ Bytind bi_cursor_bufpos;/* This stores the buffer position of the cursor. */
+ unsigned int cursor_type :3;
+ int cursor_x; /* rune block cursor is at */
+ int start_col; /* Number of character columns (each column has
+ a width of the default char width) that still
+ need to be skipped. This is used for horizontal
+ scrolling, where a certain number of columns
+ (those off the left side of the screen) need
+ to be skipped before anything is displayed. */
+ Bytind bi_start_col_enabled;
+ int start_col_xoffset; /* Number of pixels that still need to
+ be skipped. This is used for
+ horizontal scrolling of glyphs, where we want
+ to be able to scroll over part of the glyph. */
+
+ int hscroll_glyph_width_adjust; /* how much the width of the hscroll
+ glyph differs from space_width (w).
+ 0 if no hscroll glyph was used,
+ i.e. the window is not scrolled
+ horizontally. Used in tab
+ calculations. */
+
+ /* Information about the face the text should be displayed in and
+ any begin-glyphs and end-glyphs. */
+ struct extent_fragment *ef;
+ face_index findex;
+
+ /* The height of a pixmap may either be predetermined if the user has set a
+ baseline value, or it may be dependent on whatever the line ascent and
+ descent values end up being, based just on font and pixmap-ascent
+ information. In the first case we can immediately update the values, thus
+ their inclusion here. In the last case we cannot determine the actual
+ contribution to the line height until we have finished laying out all text
+ on the line. Thus we propagate the max height of such pixmaps and do a
+ final calculation (in calculate_baseline()) after all text has been added
+ to the line. */
+ int new_ascent;
+ int new_descent;
+ int max_pixmap_height;
+ int need_baseline_computation;
+ int end_glyph_width; /* Well, it is the kitchen sink after all ... */
+
+ Lisp_Object result_str; /* String where we put the result of
+ generating a formatted string in the modeline. */
+ int is_modeline; /* Non-zero if we're generating the modeline. */
+ Charcount modeline_charpos; /* Number of chars used in result_str so far;
+ corresponds to bytepos. */
+ Bytecount bytepos; /* Number of bytes used in result_str so far.
+ We don't actually copy the bytes into result_str
+ until the end because we don't know how big the
+ string is going to be until then. */
+} pos_data;
+
+enum prop_type
+{
+ PROP_STRING,
+ PROP_CHAR,
+ PROP_MINIBUF_PROMPT,
+ PROP_BLANK,
+ PROP_GLYPH
+};
+
+/* Data that should be propagated to the next line. Either a single
+ Emchar, a string of Bufbyte's or a glyph.
+
+ The actual data that is propagated ends up as a Dynarr of these
+ blocks.
+
+ prop_blocks are used to indicate that data that was supposed to go
+ on the previous line couldn't actually be displayed. Generally this
+ shouldn't happen if we are clipping the end of lines. If we are
+ wrapping then we need to display the propagation data before moving
+ on. Its questionable whether we should wrap or clip glyphs in this
+ instance. Most e-lisp relies on clipping so we preserve this
+ behavior.
+
+ #### It's unclean that both Emchars and Bufbytes are here.
+ */
+
+typedef struct prop_block prop_block;
+struct prop_block
+{
+ enum prop_type type;
+
+ union data
+ {
+ struct
+ {
+ Bufbyte *str;
+ Bytecount len; /* length of the string. */
+ } p_string;
+
+ struct
+ {
+ Emchar ch;
+ Bytind bi_cursor_bufpos; /* NOTE: is in Bytinds */
+ unsigned int cursor_type :3;
+ } p_char;
+
+ struct
+ {
+ int width;
+ face_index findex;
+ } p_blank;
+
+ struct
+ {
+ /* Not used as yet, but could be used to wrap rather than clip glyphs. */
+ int width;
+ Lisp_Object glyph;
+ } p_glyph;
+
+ } data;
+};
+
+typedef struct
+{
+ Dynarr_declare (prop_block);
+} prop_block_dynarr;
+
+
+static Charcount generate_fstring_runes (struct window *w, pos_data *data,
+ Charcount pos, Charcount min_pos,
+ Charcount max_pos, Lisp_Object elt,
+ int depth, int max_pixsize,
+ face_index findex, int type,
+ Charcount *offset,
+ Lisp_Object cur_ext);
+static prop_block_dynarr *add_glyph_rune (pos_data *data,
+ struct glyph_block *gb,
+ int pos_type, int allow_cursor,
+ struct glyph_cachel *cachel);
+static Bytind create_text_block (struct window *w, struct display_line *dl,
+ Bytind bi_start_pos, prop_block_dynarr **prop,
+ int type);
+static int create_overlay_glyph_block (struct window *w,
+ struct display_line *dl);
+static void create_left_glyph_block (struct window *w,
+ struct display_line *dl,
+ int overlay_width);
+static void create_right_glyph_block (struct window *w,
+ struct display_line *dl);
+static void redisplay_windows (Lisp_Object window, int skip_selected);
+static void decode_mode_spec (struct window *w, Emchar spec, int type);
+static void free_display_line (struct display_line *dl);
+static void update_line_start_cache (struct window *w, Bufpos from, Bufpos to,
+ Bufpos point, int no_regen);
+static int point_visible (struct window *w, Bufpos point, int type);
+static void calculate_yoffset (struct display_line *dl,
+ struct display_block *fixup);
+static void calculate_baseline (pos_data *data);
+
+/* This used to be 10 but 30 seems to give much better performance. */
+#define INIT_MAX_PREEMPTS 30
+static int max_preempts;
+
+#define REDISPLAY_PREEMPTION_CHECK \
+((void) \
+ (preempted = \
+ (!disable_preemption && \
+ ((preemption_count < max_preempts) || !NILP (Vexecuting_macro)) && \
+ (!INTERACTIVE || detect_input_pending ()))))
+
+/*
+ * Redisplay global variables.
+ */
+
+/* We need a third set of display structures for the cursor motion
+ routines. We used to just give each window a third set. However,
+ we always fully regenerate the structures when needed so there
+ isn't any reason we need more than a single set. */
+display_line_dynarr *cmotion_display_lines;
+
+/* We store the extents that we need to generate in a Dynarr and then
+ frob them all on at the end of generating the string. We do it
+ this way rather than adding them as we generate the string because
+ we don't store the text into the resulting string until we're done
+ (to avoid having to resize the string multiple times), and we don't
+ want to go around adding extents to a string when the extents might
+ stretch off the end of the string. */
+static EXTENT_dynarr *formatted_string_extent_dynarr;
+static Bytecount_dynarr *formatted_string_extent_start_dynarr;
+static Bytecount_dynarr *formatted_string_extent_end_dynarr;
+
+
+/* #### probably temporary */
+Fixnum cache_adjustment;
+
+/* This holds a string representing the text corresponding to a single
+ modeline % spec. */
+static Bufbyte_dynarr *mode_spec_bufbyte_string;
+
+int in_display; /* 1 if in redisplay. */
+
+int disable_preemption; /* Used for debugging redisplay and for
+ force-redisplay. */
+
+/* We only allow max_preempts preemptions before we force a redisplay. */
+static int preemption_count;
+
+/* Minimum pixel height of clipped bottom display line. */
+Fixnum vertical_clip;
+
+/* Minimum visible pixel width of clipped glyphs at right margin. */
+Fixnum horizontal_clip;
+
+/* Nonzero means reading single-character input with prompt
+ so put cursor on minibuffer after the prompt. */
+int cursor_in_echo_area;
+Lisp_Object Qcursor_in_echo_area;
+
+/* Nonzero means truncate lines in all windows less wide than the frame */
+int truncate_partial_width_windows;
+
+/* non-nil if a buffer has changed since the last time redisplay completed */
+int buffers_changed;
+int buffers_changed_set;
+
+/* non-nil if hscroll has changed somewhere or a buffer has been
+ narrowed or widened */
+int clip_changed;
+int clip_changed_set;
+
+/* non-nil if any extent has changed since the last time redisplay completed */
+int extents_changed;
+int extents_changed_set;
+
+/* non-nil if any face has changed since the last time redisplay completed */
+int faces_changed;
+
+/* Nonzero means some frames have been marked as garbaged */
+int frame_changed;
+
+/* non-zero if any of the builtin display glyphs (continuation,
+ hscroll, control-arrow, etc) is in need of updating
+ somewhere. */
+int glyphs_changed;
+int glyphs_changed_set;
+
+/* non-zero if any subwindow has been deleted. */
+int subwindows_changed;
+int subwindows_changed_set;
+
+/* non-zero if any displayed subwindow is in need of updating
+ somewhere. */
+int subwindows_state_changed;
+int subwindows_state_changed_set;
+
+/* This variable is 1 if the icon has to be updated.
+ It is set to 1 when `frame-icon-glyph' changes. */
+int icon_changed;
+int icon_changed_set;
+
+/* This variable is 1 if the menubar widget has to be updated.
+ It is set to 1 by set-menubar-dirty-flag and cleared when the widget
+ has been updated. */
+int menubar_changed;
+int menubar_changed_set;
+
+/* true iff we should redraw the modelines on the next redisplay */
+int modeline_changed;
+int modeline_changed_set;
+
+/* non-nil if point has changed in some buffer since the last time
+ redisplay completed */
+int point_changed;
+int point_changed_set;
+
+/* non-nil if some frame has changed its size */
+int size_changed;
+
+/* non-nil if some device has signaled that it wants to change size */
+int asynch_device_change_pending;
+
+/* non-nil if any toolbar has changed */
+int toolbar_changed;
+int toolbar_changed_set;
+
+/* Nonzero if some frame has changed the layout of internal elements
+ (gutters or toolbars). */
+int frame_layout_changed;
+
+/* non-nil if any gutter has changed */
+int gutter_changed;
+int gutter_changed_set;
+
+/* non-nil if any window has changed since the last time redisplay completed */
+int windows_changed;
+
+/* non-nil if any frame's window structure has changed since the last
+ time redisplay completed */
+int windows_structure_changed;
+
+/* If non-nil, use vertical bar cursor. */
+Lisp_Object Vbar_cursor;
+Lisp_Object Qbar_cursor;
+
+Lisp_Object Vvisible_bell; /* If true and the terminal will support it
+ then the frame will flash instead of
+ beeping when an error occurs */
+
+/* Nonzero means no need to redraw the entire frame on resuming
+ a suspended Emacs. This is useful on terminals with multiple pages,
+ where one page is used for Emacs and another for all else. */
+int no_redraw_on_reenter;
+
+Lisp_Object Vwindow_system; /* nil or a symbol naming the window system
+ under which emacs is running
+ ('x is the only current possibility) */
+Lisp_Object Vinitial_window_system;
+
+Lisp_Object Vglobal_mode_string;
+
+/* The number of lines scroll a window by when point leaves the window; if
+ it is <=0 then point is centered in the window */
+Fixnum scroll_step;
+
+/* Scroll up to this many lines, to bring point back on screen. */
+Fixnum scroll_conservatively;
+
+/* Marker for where to display an arrow on top of the buffer text. */
+Lisp_Object Voverlay_arrow_position;
+/* String to display for the arrow. */
+Lisp_Object Voverlay_arrow_string;
+
+Lisp_Object Vwindow_size_change_functions;
+Lisp_Object Vwindow_scroll_functions;
+Lisp_Object Qredisplay_end_trigger_functions, Vredisplay_end_trigger_functions;
+
+Lisp_Object Qbuffer_list_changed_hook, Vbuffer_list_changed_hook;
+
+
+#define INHIBIT_REDISPLAY_HOOKS /* #### Until we've thought about
+ this more. */
+#ifndef INHIBIT_REDISPLAY_HOOKS
+/* #### Chuck says: I think this needs more thought.
+ Think about this for 19.14. */
+Lisp_Object Vpre_redisplay_hook, Vpost_redisplay_hook;
+Lisp_Object Qpre_redisplay_hook, Qpost_redisplay_hook;
+#endif /* INHIBIT_REDISPLAY_HOOKS */
+
+static Fixnum last_display_warning_tick;
+static Fixnum display_warning_tick;
+Lisp_Object Qdisplay_warning_buffer;
+int inhibit_warning_display;
+
+Lisp_Object Vleft_margin_width, Vright_margin_width;
+Lisp_Object Vminimum_line_ascent, Vminimum_line_descent;
+Lisp_Object Vuse_left_overflow, Vuse_right_overflow;
+Lisp_Object Vtext_cursor_visible_p;
+
+int column_number_start_at_one;
+
+Lisp_Object Qtop_bottom;
+
+#define WINDOW_SCROLLED(w) ((w)->hscroll > 0 || (w)->left_xoffset)
+
+\f
+/***************************************************************************/
+/* */
+/* low-level interfaces onto device routines */
+/* */
+/***************************************************************************/
+
+static int
+redisplay_text_width_emchar_string (struct window *w, int findex,
+ Emchar *str, Charcount len)
+{
+ unsigned char charsets[NUM_LEADING_BYTES];
+ Lisp_Object window;
+
+ find_charsets_in_emchar_string (charsets, str, len);
+ XSETWINDOW (window, w);
+ ensure_face_cachel_complete (WINDOW_FACE_CACHEL (w, findex), window,
+ charsets);
+ return DEVMETH (XDEVICE (FRAME_DEVICE (XFRAME (WINDOW_FRAME (w)))),
+ text_width, (XFRAME (WINDOW_FRAME (w)),
+ WINDOW_FACE_CACHEL (w, findex), str, len));
+}
+
+static Emchar_dynarr *rtw_emchar_dynarr;
+
+int
+redisplay_text_width_string (struct window *w, int findex,
+ Bufbyte *nonreloc, Lisp_Object reloc,
+ Bytecount offset, Bytecount len)
+{
+ if (!rtw_emchar_dynarr)
+ rtw_emchar_dynarr = Dynarr_new (Emchar);
+ Dynarr_reset (rtw_emchar_dynarr);
+
+ fixup_internal_substring (nonreloc, reloc, offset, &len);
+ if (STRINGP (reloc))
+ nonreloc = XSTRING_DATA (reloc);
+ convert_bufbyte_string_into_emchar_dynarr (nonreloc, len, rtw_emchar_dynarr);
+ return redisplay_text_width_emchar_string
+ (w, findex, Dynarr_atp (rtw_emchar_dynarr, 0),
+ Dynarr_length (rtw_emchar_dynarr));
+}
+
+int
+redisplay_frame_text_width_string (struct frame *f, Lisp_Object face,
+ Bufbyte *nonreloc, Lisp_Object reloc,
+ Bytecount offset, Bytecount len)
+{
+ unsigned char charsets[NUM_LEADING_BYTES];
+ Lisp_Object frame;
+ struct face_cachel cachel;
+
+ if (!rtw_emchar_dynarr)
+ rtw_emchar_dynarr = Dynarr_new (Emchar);
+ Dynarr_reset (rtw_emchar_dynarr);
+
+ fixup_internal_substring (nonreloc, reloc, offset, &len);
+ if (STRINGP (reloc))
+ nonreloc = XSTRING_DATA (reloc);
+ convert_bufbyte_string_into_emchar_dynarr (nonreloc, len, rtw_emchar_dynarr);
+ find_charsets_in_bufbyte_string (charsets, nonreloc, len);
+ reset_face_cachel (&cachel);
+ cachel.face = face;
+ XSETFRAME (frame, f);
+ ensure_face_cachel_complete (&cachel, frame, charsets);
+ return DEVMETH (XDEVICE (FRAME_DEVICE (f)),
+ text_width, (f, &cachel, Dynarr_atp (rtw_emchar_dynarr, 0),
+ Dynarr_length (rtw_emchar_dynarr)));
+}
+
+/* Return the display block from DL of the given TYPE. A display line
+ can have only one display block of each possible type. If DL does
+ not have a block of type TYPE, one will be created and added to DL. */
+
+struct display_block *
+get_display_block_from_line (struct display_line *dl, enum display_type type)
+{
+ int elt;
+ struct display_block db;
+
+ /* Check if this display line already has a block of the desired type and
+ if so, return it. */
+ if (dl->display_blocks)
+ {
+ for (elt = 0; elt < Dynarr_length (dl->display_blocks); elt++)
+ {
+ if (Dynarr_at (dl->display_blocks, elt).type == type)
+ return Dynarr_atp (dl->display_blocks, elt);
+ }
+
+ /* There isn't an active block of the desired type, but there
+ might still be allocated blocks we need to reuse. */
+ if (elt < Dynarr_largest (dl->display_blocks))
+ {
+ struct display_block *dbp = Dynarr_atp (dl->display_blocks, elt);
+
+ /* 'add' the block to the list */
+ Dynarr_increment (dl->display_blocks);
+
+ /* initialize and return */
+ dbp->type = type;
+ return dbp;
+ }
+ }
+ else
+ {
+ /* This line doesn't have any display blocks, so initialize the display
+ bock array. */
+ dl->display_blocks = Dynarr_new (display_block);
+ }
+
+ /* The line doesn't have a block of the desired type so go ahead and create
+ one and add it to the line. */
+ xzero (db);
+ db.type = type;
+ db.runes = Dynarr_new (rune);
+ Dynarr_add (dl->display_blocks, db);
+
+ /* Return the newly added display block. */
+ elt = Dynarr_length (dl->display_blocks) - 1;
+
+ return Dynarr_atp (dl->display_blocks, elt);
+}
+
+static int
+tab_char_width (struct window *w)
+{
+ struct buffer *b = XBUFFER (w->buffer);
+ int char_tab_width = XINT (b->tab_width);
+
+ if (char_tab_width <= 0 || char_tab_width > 1000) char_tab_width = 8;
+
+ return char_tab_width;
+}
+
+static int
+space_width (struct window *w)
+{
+ /* While tabs are traditional composed of spaces, for variable-width
+ fonts the space character tends to give too narrow a value. So
+ we use 'n' instead. Except that we don't. We use the default
+ character width for the default face. If this is actually
+ defined by the font then it is probably the best thing to
+ actually use. If it isn't, we have assumed it is 'n' and have
+ already calculated its width. Thus we can avoid a call to
+ XTextWidth on X frames by just querying the default width. */
+ return XFONT_INSTANCE
+ (WINDOW_FACE_CACHEL_FONT (w, DEFAULT_INDEX, Vcharset_ascii))->width;
+}
+
+static int
+tab_pix_width (struct window *w)
+{
+ return space_width (w) * tab_char_width (w);
+}
+
+/* Given a pixel position in a window, return the pixel location of
+ the next tabstop. Tabs are calculated from the left window edge in
+ terms of spaces displayed in the default face. Formerly the space
+ width was determined using the currently active face. That method
+ leads to tabstops which do not line up. */
+
+static int
+next_tab_position (struct window *w, int start_pixpos, int left_pixpos)
+{
+ int n_pos = left_pixpos;
+ int pix_tab_width = tab_pix_width (w);
+
+ /* Adjust n_pos for any hscrolling which has happened. */
+ if (WINDOW_SCROLLED (w))
+ n_pos -= space_width (w) * (w->hscroll - 1) + w->left_xoffset;
+
+ while (n_pos <= start_pixpos)
+ n_pos += pix_tab_width;
+
+ return n_pos;
+}
+
+/* For the given window, calculate the outside and margin boundaries for a
+ display line. The whitespace boundaries must be calculated by the text
+ layout routines. */
+
+layout_bounds
+calculate_display_line_boundaries (struct window *w, int modeline)
+{
+ layout_bounds bounds;
+
+ /* Set the outermost boundaries which are the boundaries of the
+ window itself minus the gutters (and minus the scrollbars if this
+ is for the modeline). */
+ if (!modeline)
+ {
+ bounds.left_out = WINDOW_TEXT_LEFT (w);
+ bounds.right_out = WINDOW_TEXT_RIGHT (w);
+ }
+ else
+ {
+ bounds.left_out = WINDOW_MODELINE_LEFT (w);
+ bounds.right_out = WINDOW_MODELINE_RIGHT (w);
+ }
+
+ /* The inner boundaries mark where the glyph margins are located. */
+ bounds.left_in = bounds.left_out + window_left_margin_width (w);
+ bounds.right_in = bounds.right_out - window_right_margin_width (w);
+
+ /* We cannot fully calculate the whitespace boundaries as they
+ depend on the contents of the line being displayed. */
+ bounds.left_white = bounds.left_in;
+ bounds.right_white = bounds.right_in;
+
+ return bounds;
+}
+
+/* This takes a display_block and its containing line and corrects the yoffset
+ of each glyph in the block to cater for the ascent of the line as a
+ whole. Must be called *after* the line-ascent is known! */
+
+static void
+calculate_yoffset (struct display_line *dl, struct display_block *fixup)
+{
+ int i;
+ for (i=0; i<Dynarr_length (fixup->runes); i++)
+ {
+ struct rune *r = Dynarr_atp (fixup->runes,i);
+ if (r->type == RUNE_DGLYPH)
+ {
+ if (r->object.dglyph.ascent < dl->ascent)
+ r->object.dglyph.yoffset = dl->ascent - r->object.dglyph.ascent +
+ r->object.dglyph.descent;
+ }
+ }
+}
+
+/* Calculate the textual baseline (the ascent and descent values for the
+ display_line as a whole).
+
+ If the baseline is completely blank, or contains no manually positioned
+ glyphs, then the textual baseline is simply the baseline of the default font.
+ (The `contains no manually positioned glyphs' part is actually done for
+ us by `add_emchar_rune'.)
+
+ If the baseline contains pixmaps, and they're all manually positioned, then
+ the textual baseline location is constrained that way, and we need do no
+ work.
+
+ If the baseline contains pixmaps, and at least one is automatically
+ positioned, then the textual ascent is the largest ascent on the line, and
+ the textual descent is the largest descent (which is how things are set up at
+ entry to this function anyway): except that if the max_ascent + max_descent
+ is too small for the height of the line (say you've adjusted the baseline of
+ a short glyph, and there's a tall one next to it), then take the ascent and
+ descent for the line individually from the largest of the explicitly set
+ ascent/descent, and the rescaled ascent/descent of the default font, scaled
+ such that the largest glyph will fit.
+
+ This means that if you have a short glyph (but taller than the default
+ font's descent) forced right under the baseline, and a really tall
+ automatically positioned glyph, that the descent for the line is just big
+ enough for the manually positioned short glyph, and the tall one uses as
+ much of that space as the default font would were it as tall as the tall
+ glyph; but that the ascent is big enough for the tall glyph to fit.
+
+ This behaviour means that under no circumstances will changing the baseline
+ of a short glyph cause a tall glyph to move around; nor will it move the
+ textual baseline more than necessary. (Changing a tall glyph's baseline
+ might move the text's baseline arbitrarily, of course.) */
+
+static void
+calculate_baseline (pos_data *data)
+{
+ /* Blank line: baseline is default font's baseline. */
+
+ if (!data->new_ascent && !data->new_descent)
+ {
+ /* We've got a blank line so initialize these values from the default
+ face. */
+ default_face_font_info (data->window, &data->new_ascent,
+ &data->new_descent, 0, 0, 0);
+ }
+
+ /* No automatically positioned glyphs? Return at once. */
+ if (!data->need_baseline_computation)
+ return;
+
+ /* Is the tallest glyph on the line automatically positioned?
+ If it's manually positioned, or it's automatically positioned
+ and there's enough room for it anyway, we need do no more work. */
+ if (data->max_pixmap_height > data->new_ascent + data->new_descent)
+ {
+ int default_font_ascent, default_font_descent, default_font_height;
+ int scaled_default_font_ascent, scaled_default_font_descent;
+
+ default_face_font_info (data->window, &default_font_ascent,
+ &default_font_descent, &default_font_height,
+ 0, 0);
+
+ scaled_default_font_ascent = data->max_pixmap_height *
+ default_font_ascent / default_font_height;
+
+ data->new_ascent = max (data->new_ascent, scaled_default_font_ascent);
+
+ /* The ascent may have expanded now. Do we still need to grow the descent,
+ or are things big enough?
+
+ The +1 caters for the baseline row itself. */
+ if (data->max_pixmap_height > data->new_ascent + data->new_descent)
+ {
+ scaled_default_font_descent = (data->max_pixmap_height *
+ default_font_descent / default_font_height) + 1;
+
+ data->new_descent = max (data->new_descent, scaled_default_font_descent);
+ }
+ }
+}
+
+/* Given a display line and a starting position, ensure that the
+ contents of the display line accurately represent the visual
+ representation of the buffer contents starting from the given
+ position when displayed in the given window. The display line ends
+ when the contents of the line reach the right boundary of the given
+ window. */
+
+static Bufpos
+generate_display_line (struct window *w, struct display_line *dl, int bounds,
+ Bufpos start_pos, prop_block_dynarr **prop,
+ int type)
+{
+ Bufpos ret_bufpos;
+ int overlay_width;
+ struct buffer *b = XBUFFER (WINDOW_BUFFER (w));
+
+ /* If our caller hasn't already set the boundaries, then do so now. */
+ if (!bounds)
+ dl->bounds = calculate_display_line_boundaries (w, 0);
+
+ /* Reset what this line is using. */
+ if (dl->display_blocks)
+ Dynarr_reset (dl->display_blocks);
+ if (dl->left_glyphs)
+ {
+ Dynarr_free (dl->left_glyphs);
+ dl->left_glyphs = 0;
+ }
+ if (dl->right_glyphs)
+ {
+ Dynarr_free (dl->right_glyphs);
+ dl->right_glyphs = 0;
+ }
+
+ /* We aren't generating a modeline at the moment. */
+ dl->modeline = 0;
+
+ /* Create a display block for the text region of the line. */
+ {
+ /* #### urk urk urk!!! Chuck fix this shit! */
+ Bytind hacked_up_bytind =
+ create_text_block (w, dl, bufpos_to_bytind (b, start_pos),
+ prop, type);
+ if (hacked_up_bytind > BI_BUF_ZV (b))
+ ret_bufpos = BUF_ZV (b) + 1;
+ else
+ ret_bufpos = bytind_to_bufpos (b, hacked_up_bytind);
+ }
+ dl->bufpos = start_pos;
+ if (dl->end_bufpos < dl->bufpos)
+ dl->end_bufpos = dl->bufpos;
+
+ if (MARKERP (Voverlay_arrow_position)
+ && EQ (w->buffer, Fmarker_buffer (Voverlay_arrow_position))
+ && start_pos == marker_position (Voverlay_arrow_position)
+ && (STRINGP (Voverlay_arrow_string)
+ || GLYPHP (Voverlay_arrow_string)))
+ {
+ overlay_width = create_overlay_glyph_block (w, dl);
+ }
+ else
+ overlay_width = 0;
+
+ /* If there are left glyphs associated with any character in the
+ text block, then create a display block to handle them. */
+ if (dl->left_glyphs != NULL && Dynarr_length (dl->left_glyphs))
+ create_left_glyph_block (w, dl, overlay_width);
+
+ /* If there are right glyphs associated with any character in the
+ text block, then create a display block to handle them. */
+ if (dl->right_glyphs != NULL && Dynarr_length (dl->right_glyphs))
+ create_right_glyph_block (w, dl);
+
+ /* In the future additional types of display blocks may be generated
+ here. */
+
+ w->last_redisplay_pos = ret_bufpos;
+
+ return ret_bufpos;
+}
+
+/* Adds an hscroll glyph to a display block. If this is called, then
+ the block had better be empty.
+
+ Yes, there are multiple places where this function is called but
+ that is the way it has to be. Each calling function has to deal
+ with bi_start_col_enabled a little differently depending on the
+ object being worked with. */
+
+static prop_block_dynarr *
+add_hscroll_rune (pos_data *data)
+{
+ struct glyph_block gb;
+ prop_block_dynarr *retval;
+ Bytind bi_old_cursor_bufpos = data->bi_cursor_bufpos;
+ unsigned int old_cursor_type = data->cursor_type;
+ Bytind bi_old_bufpos = data->bi_bufpos;
+
+ if (data->cursor_type == CURSOR_ON
+ && data->bi_cursor_bufpos >= data->bi_start_col_enabled
+ && data->bi_cursor_bufpos <= data->bi_bufpos)
+ {
+ data->bi_cursor_bufpos = data->bi_start_col_enabled;
+ }
+ else
+ {
+ data->cursor_type = NO_CURSOR;
+ }
+
+ data->bi_endpos = data->bi_bufpos;
+ data->bi_bufpos = data->bi_start_col_enabled;
+
+ gb.extent = Qnil;
+ gb.glyph = Vhscroll_glyph;
+ {
+ int oldpixpos = data->pixpos;
+ retval = add_glyph_rune (data, &gb, BEGIN_GLYPHS, 0,
+ GLYPH_CACHEL (XWINDOW (data->window),
+ HSCROLL_GLYPH_INDEX));
+ data->hscroll_glyph_width_adjust =
+ data->pixpos - oldpixpos - space_width (XWINDOW (data->window));
+ }
+ data->bi_endpos = 0;
+ data->bi_cursor_bufpos = bi_old_cursor_bufpos;
+ data->cursor_type = old_cursor_type;
+ data->bi_bufpos = bi_old_bufpos;
+
+ data->bi_start_col_enabled = 0;
+ return retval;
+}
+
+/* Adds a character rune to a display block. If there is not enough room
+ to fit the rune on the display block (as determined by the MAX_PIXPOS)
+ then it adds nothing and returns ADD_FAILED. If
+ NO_CONTRIBUTE_TO_LINE_HEIGHT is non-zero, don't allow the char's height
+ to affect the total line height. (See add_intbyte_string_runes()). */
+
+static prop_block_dynarr *
+add_emchar_rune_1 (pos_data *data, int no_contribute_to_line_height)
+{
+ struct rune rb, *crb;
+ int width, local;
+
+ if (data->start_col)
+ {
+ data->start_col--;
+
+ if (data->start_col)
+ return NULL;
+ }
+
+ if (data->bi_start_col_enabled)
+ {
+ return add_hscroll_rune (data);
+ }
+
+ if (data->ch == '\n')
+ {
+ data->font_is_bogus = 0;
+ /* Cheesy end-of-line pseudo-character. */
+ width = data->blank_width;
+ }
+ else
+ {
+ Lisp_Object charset = CHAR_CHARSET (data->ch);
+ if (!EQ (charset, data->last_charset) ||
+ data->findex != data->last_findex)
+ {
+ /* OK, we need to do things the hard way. */
+ struct window *w = XWINDOW (data->window);
+ struct face_cachel *cachel = WINDOW_FACE_CACHEL (w, data->findex);
+ Lisp_Object font_instance =
+ ensure_face_cachel_contains_charset (cachel, data->window,
+ charset);
+ Lisp_Font_Instance *fi;
+
+ if (EQ (font_instance, Vthe_null_font_instance))
+ {
+ font_instance = FACE_CACHEL_FONT (cachel, Vcharset_ascii);
+ data->font_is_bogus = 1;
+ }
+ else
+ data->font_is_bogus = 0;
+
+ fi = XFONT_INSTANCE (font_instance);
+ if (!fi->proportional_p)
+ /* sweetness and light. */
+ data->last_char_width = fi->width;
+ else
+ data->last_char_width = -1;
+
+ if (!no_contribute_to_line_height)
+ {
+ data->new_ascent = max (data->new_ascent, (int) fi->ascent);
+ data->new_descent = max (data->new_descent, (int) fi->descent);
+ }
+
+ data->last_charset = charset;
+ data->last_findex = data->findex;
+ }
+
+ width = data->last_char_width;
+ if (width < 0)
+ {
+ /* bummer. Proportional fonts. */
+ width = redisplay_text_width_emchar_string (XWINDOW (data->window),
+ data->findex,
+ &data->ch, 1);
+ }
+ }
+
+ if (data->max_pixpos != -1 && (data->pixpos + width > data->max_pixpos))
+ {
+ return ADD_FAILED;
+ }
+
+ if (Dynarr_length (data->db->runes) < Dynarr_largest (data->db->runes))
+ {
+ crb = Dynarr_atp (data->db->runes, Dynarr_length (data->db->runes));
+ local = 0;
+ }
+ else
+ {
+ crb = &rb;
+ local = 1;
+ }
+
+ crb->findex = data->findex;
+ crb->xpos = data->pixpos;
+ crb->width = width;
+ if (data->bi_bufpos)
+ {
+ if (NILP (data->string))
+ crb->bufpos =
+ bytind_to_bufpos (XBUFFER (WINDOW_BUFFER (XWINDOW (data->window))),
+ data->bi_bufpos);
+ else
+ crb->bufpos =
+ bytecount_to_charcount (XSTRING_DATA (data->string), data->bi_bufpos);
+ }
+ else if (data->is_modeline)
+ crb->bufpos = data->modeline_charpos;
+ else
+ /* Text but not in buffer */
+ crb->bufpos = 0;
+ crb->type = RUNE_CHAR;
+ crb->object.chr.ch = data->font_is_bogus ? '~' : data->ch;
+ crb->endpos = 0;
+
+ if (data->cursor_type == CURSOR_ON)
+ {
+ if (data->bi_bufpos == data->bi_cursor_bufpos)
+ {
+ crb->cursor_type = CURSOR_ON;
+ data->cursor_x = Dynarr_length (data->db->runes);
+ }
+ else
+ crb->cursor_type = CURSOR_OFF;
+ }
+ else if (data->cursor_type == NEXT_CURSOR)
+ {
+ crb->cursor_type = CURSOR_ON;
+ data->cursor_x = Dynarr_length (data->db->runes);
+ data->cursor_type = NO_CURSOR;
+ }
+ else if (data->cursor_type == IGNORE_CURSOR)
+ crb->cursor_type = IGNORE_CURSOR;
+ else
+ crb->cursor_type = CURSOR_OFF;
+
+ if (local)
+ Dynarr_add (data->db->runes, *crb);
+ else
+ Dynarr_increment (data->db->runes);
+
+ data->pixpos += width;
+
+ return NULL;
+}
+
+static prop_block_dynarr *
+add_emchar_rune (pos_data *data)
+{
+ return add_emchar_rune_1 (data, 0);
+}
+
+/* Given a string C_STRING of length C_LENGTH, call add_emchar_rune for
+ each character in the string. Propagate any left-over data unless
+ NO_PROP is non-zero. If NO_CONTRIBUTE_TO_LINE_HEIGHT is non-zero, don't
+ allow this character to increase the total height of the line. (This is
+ used when the character is part of a text glyph. In that case, the
+ glyph code itself adjusts the line height as necessary, depending on
+ whether glyph-contrib-p is true.) */
+
+static prop_block_dynarr *
+add_bufbyte_string_runes (pos_data *data, Bufbyte *c_string,
+ Bytecount c_length, int no_prop,
+ int no_contribute_to_line_height)
+{
+ Bufbyte *pos, *end = c_string + c_length;
+ prop_block_dynarr *prop;
+
+ /* #### This function is too simplistic. It needs to do the same
+ sort of character interpretation (display-table lookup,
+ ctl-arrow checking), etc. that create_text_block() does.
+ The functionality to do this in that routine needs to be
+ modularized. */
+
+ for (pos = c_string; pos < end;)
+ {
+ Bufbyte *old_pos = pos;
+
+ data->ch = charptr_emchar (pos);
+
+ prop = add_emchar_rune_1 (data, no_contribute_to_line_height);
+
+ if (prop)
+ {
+ if (no_prop)
+ return ADD_FAILED;
+ else
+ {
+ struct prop_block pb;
+ Bytecount len = end - pos;
+ prop = Dynarr_new (prop_block);
+
+ pb.type = PROP_STRING;
+ pb.data.p_string.str = xnew_array (Bufbyte, len);
+ strncpy ((char *) pb.data.p_string.str, (char *) pos, len);
+ pb.data.p_string.len = len;
+
+ Dynarr_add (prop, pb);
+ return prop;
+ }
+ }
+ INC_CHARPTR (pos);
+ assert (pos <= end);
+ /* #### Duplicate code from add_string_to_fstring_db_runes
+ should we do more?*/
+ data->bytepos += pos - old_pos;
+ }
+
+ return NULL;
+}
+
+/* Add a single rune of the specified width. The area covered by this
+ rune will be displayed in the foreground color of the associated
+ face. */
+
+static prop_block_dynarr *
+add_blank_rune (pos_data *data, struct window *w, int char_tab_width)
+{
+ struct rune rb;
+
+ /* If data->start_col is not 0 then this call to add_blank_rune must have
+ been to add it as a tab. */
+ if (data->start_col)
+ {
+ /* assert (w != NULL) */
+ prop_block_dynarr *retval;
+
+ /* If we have still not fully scrolled horizontally, subtract
+ the width of this tab and return. */
+ if (char_tab_width < data->start_col)
+ {
+ data->start_col -= char_tab_width;
+ return NULL;
+ }
+ else if (char_tab_width == data->start_col)
+ data->blank_width = 0;
+ else
+ {
+ int spcwid = space_width (w);
+
+ if (spcwid >= data->blank_width)
+ data->blank_width = 0;
+ else
+ data->blank_width -= spcwid;
+ }
+
+ data->start_col = 0;
+ retval = add_hscroll_rune (data);
+
+ /* Could be caused by the handling of the hscroll rune. */
+ if (retval != NULL || !data->blank_width)
+ return retval;
+ }
+
+ /* Blank runes are always calculated to fit. */
+ assert (data->pixpos + data->blank_width <= data->max_pixpos);
+
+ rb.findex = data->findex;
+ rb.xpos = data->pixpos;
+ rb.width = data->blank_width;
+ if (data->bi_bufpos)
+ rb.bufpos =
+ bytind_to_bufpos (XBUFFER (WINDOW_BUFFER (XWINDOW (data->window))),
+ data->bi_bufpos);
+ else
+ /* #### and this is really correct too? */
+ rb.bufpos = 0;
+ rb.endpos = 0;
+ rb.type = RUNE_BLANK;
+
+ if (data->cursor_type == CURSOR_ON)
+ {
+ if (data->bi_bufpos == data->bi_cursor_bufpos)
+ {
+ rb.cursor_type = CURSOR_ON;
+ data->cursor_x = Dynarr_length (data->db->runes);
+ }
+ else
+ rb.cursor_type = CURSOR_OFF;
+ }
+ else if (data->cursor_type == NEXT_CURSOR)
+ {
+ rb.cursor_type = CURSOR_ON;
+ data->cursor_x = Dynarr_length (data->db->runes);
+ data->cursor_type = NO_CURSOR;
+ }
+ else
+ rb.cursor_type = CURSOR_OFF;
+
+ Dynarr_add (data->db->runes, rb);
+ data->pixpos += data->blank_width;
+
+ return NULL;
+}
+
+/* Add runes representing a character in octal. */
+
+#define ADD_NEXT_OCTAL_RUNE_CHAR do \
+{ \
+ if (add_failed || (add_failed = add_emchar_rune (data))) \
+ { \
+ struct prop_block pb; \
+ if (!prop) \
+ prop = Dynarr_new (prop_block); \
+ \
+ pb.type = PROP_CHAR; \
+ pb.data.p_char.ch = data->ch; \
+ pb.data.p_char.cursor_type = data->cursor_type; \
+ Dynarr_add (prop, pb); \
+ } \
+} while (0)
+
+static prop_block_dynarr *
+add_octal_runes (pos_data *data)
+{
+ prop_block_dynarr *add_failed, *prop = 0;
+ Emchar orig_char = data->ch;
+ unsigned int orig_cursor_type = data->cursor_type;
+
+ /* Initialize */
+ add_failed = NULL;
+
+ if (data->start_col)
+ data->start_col--;
+
+ if (!data->start_col)
+ {
+ if (data->bi_start_col_enabled)
+ {
+ add_failed = add_hscroll_rune (data);
+ }
+ else
+ {
+ struct glyph_block gb;
+ struct window *w = XWINDOW (data->window);
+
+ gb.extent = Qnil;
+ gb.glyph = Voctal_escape_glyph;
+ add_failed =
+ add_glyph_rune (data, &gb, BEGIN_GLYPHS, 1,
+ GLYPH_CACHEL (w, OCT_ESC_GLYPH_INDEX));
+ }
+ }
+
+ /* We only propagate information if the glyph was partially
+ added. */
+ if (add_failed)
+ return add_failed;
+
+ data->cursor_type = IGNORE_CURSOR;
+
+ if (data->ch >= 0x100)
+ {
+ /* If the character is an extended Mule character, it could have
+ up to 19 bits. For the moment, we treat it as a seven-digit
+ octal number. This is not that pretty, but whatever. */
+ data->ch = (7 & (orig_char >> 18)) + '0';
+ ADD_NEXT_OCTAL_RUNE_CHAR;
+
+ data->ch = (7 & (orig_char >> 15)) + '0';
+ ADD_NEXT_OCTAL_RUNE_CHAR;
+
+ data->ch = (7 & (orig_char >> 12)) + '0';
+ ADD_NEXT_OCTAL_RUNE_CHAR;
+
+ data->ch = (7 & (orig_char >> 9)) + '0';
+ ADD_NEXT_OCTAL_RUNE_CHAR;
+ }
+
+ data->ch = (7 & (orig_char >> 6)) + '0';
+ ADD_NEXT_OCTAL_RUNE_CHAR;
+
+ data->ch = (7 & (orig_char >> 3)) + '0';
+ ADD_NEXT_OCTAL_RUNE_CHAR;
+
+ data->ch = (7 & orig_char) + '0';
+ ADD_NEXT_OCTAL_RUNE_CHAR;
+
+ data->cursor_type = orig_cursor_type;
+ return NULL;
+}
+
+#undef ADD_NEXT_OCTAL_RUNE_CHAR
+
+/* Add runes representing a control character to a display block. */
+
+static prop_block_dynarr *
+add_control_char_runes (pos_data *data, struct buffer *b)
+{
+ if (!NILP (b->ctl_arrow))
+ {
+ prop_block_dynarr *prop;
+ Emchar orig_char = data->ch;
+ unsigned int old_cursor_type = data->cursor_type;
+
+ /* Initialize */
+ prop = NULL;
+
+ if (data->start_col)
+ data->start_col--;
+
+ if (!data->start_col)
+ {
+ if (data->bi_start_col_enabled)
+ {
+ prop_block_dynarr *retval;
+
+ retval = add_hscroll_rune (data);
+ if (retval)
+ return retval;
+ }
+ else
+ {
+ struct glyph_block gb;
+ struct window *w = XWINDOW (data->window);
+
+ gb.extent = Qnil;
+ gb.glyph = Vcontrol_arrow_glyph;
+
+ /* We only propagate information if the glyph was partially
+ added. */
+ if (add_glyph_rune (data, &gb, BEGIN_GLYPHS, 1,
+ GLYPH_CACHEL (w, CONTROL_GLYPH_INDEX)))
+ return ADD_FAILED;
+ }
+ }
+
+ if (orig_char == 0177)
+ data->ch = '?';
+ else
+ data->ch = orig_char ^ 0100;
+ data->cursor_type = IGNORE_CURSOR;
+
+ if (add_emchar_rune (data))
+ {
+ struct prop_block pb;
+ if (!prop)
+ prop = Dynarr_new (prop_block);
+
+ pb.type = PROP_CHAR;
+ pb.data.p_char.ch = data->ch;
+ pb.data.p_char.cursor_type = data->cursor_type;
+ Dynarr_add (prop, pb);
+ }
+
+ data->cursor_type = old_cursor_type;
+ return prop;
+ }
+ else
+ {
+ return add_octal_runes (data);
+ }
+}
+
+static prop_block_dynarr *
+add_disp_table_entry_runes_1 (pos_data *data, Lisp_Object entry)
+{
+ prop_block_dynarr *prop = NULL;
+
+ if (STRINGP (entry))
+ {
+ prop = add_bufbyte_string_runes (data,
+ XSTRING_DATA (entry),
+ XSTRING_LENGTH (entry),
+ 0, 0);
+ }
+ else if (GLYPHP (entry))
+ {
+ if (data->start_col)
+ data->start_col--;
+
+ if (!data->start_col && data->bi_start_col_enabled)
+ {
+ prop = add_hscroll_rune (data);
+ }
+ else
+ {
+ struct glyph_block gb;
+
+ gb.glyph = entry;
+ gb.extent = Qnil;
+ prop = add_glyph_rune (data, &gb, BEGIN_GLYPHS, 0, 0);
+ }
+ }
+ else if (CHAR_OR_CHAR_INTP (entry))
+ {
+ data->ch = XCHAR_OR_CHAR_INT (entry);
+ prop = add_emchar_rune (data);
+ }
+ else if (CONSP (entry))
+ {
+ if (EQ (XCAR (entry), Qformat)
+ && CONSP (XCDR (entry))
+ && STRINGP (XCAR (XCDR (entry))))
+ {
+ Lisp_Object format = XCAR (XCDR (entry));
+ Bytind len = XSTRING_LENGTH (format);
+ Bufbyte *src = XSTRING_DATA (format), *end = src + len;
+ Bufbyte *result = alloca_array (Bufbyte, len);
+ Bufbyte *dst = result;
+
+ while (src < end)
+ {
+ Emchar c = charptr_emchar (src);
+ INC_CHARPTR (src);
+ if (c != '%' || src == end)
+ dst += set_charptr_emchar (dst, c);
+ else
+ {
+ c = charptr_emchar (src);
+ INC_CHARPTR (src);
+ switch (c)
+ {
+ /*case 'x':
+ dst += long_to_string_base ((char *)dst, data->ch, 16);
+ break;*/
+ case '%':
+ dst += set_charptr_emchar (dst, '%');
+ break;
+ /* #### unimplemented */
+ }
+ }
+ }
+ prop = add_bufbyte_string_runes (data, result, dst - result, 0, 0);
+ }
+ }
+
+ /* Else blow it off because someone added a bad entry and we don't
+ have any safe way of signaling an error. */
+ return prop;
+}
+
+/* Given a display table entry, call the appropriate functions to
+ display each element of the entry. */
+
+static prop_block_dynarr *
+add_disp_table_entry_runes (pos_data *data, Lisp_Object entry)
+{
+ prop_block_dynarr *prop = NULL;
+ if (VECTORP (entry))
+ {
+ Lisp_Vector *de = XVECTOR (entry);
+ EMACS_INT len = vector_length (de);
+ int elt;
+
+ for (elt = 0; elt < len; elt++)
+ {
+ if (NILP (vector_data (de)[elt]))
+ continue;
+ else
+ prop = add_disp_table_entry_runes_1 (data, vector_data (de)[elt]);
+ /* Else blow it off because someone added a bad entry and we
+ don't have any safe way of signaling an error. Hey, this
+ comment sounds familiar. */
+
+ /* #### Still need to add any remaining elements to the
+ propagation information. */
+ if (prop)
+ return prop;
+ }
+ }
+ else
+ prop = add_disp_table_entry_runes_1 (data, entry);
+ return prop;
+}
+
+/* Add runes which were propagated from the previous line. */
+
+static prop_block_dynarr *
+add_propagation_runes (prop_block_dynarr **prop, pos_data *data)
+{
+ /* #### Remember to handle start_col parameter of data when the rest of
+ this is finished. */
+ /* #### Chuck -- I've redone this function a bit. It looked like the
+ case of not all the propagation blocks being added was not handled
+ well. */
+ /* #### Chuck -- I also think the double indirection of PROP is kind
+ of bogus. A cleaner solution is just to check for
+ Dynarr_length (prop) > 0. */
+ /* #### This function also doesn't even pay attention to ADD_FAILED!
+ This is seriously fucked! Seven ####'s in 130 lines -- is that a
+ record? */
+ int elt;
+ prop_block_dynarr *add_failed;
+ Bytind bi_old_cursor_bufpos = data->bi_cursor_bufpos;
+ unsigned int old_cursor_type = data->cursor_type;
+
+ for (elt = 0; elt < Dynarr_length (*prop); elt++)
+ {
+ struct prop_block *pb = Dynarr_atp (*prop, elt);
+
+ switch (pb->type)
+ {
+ case PROP_CHAR:
+ data->ch = pb->data.p_char.ch;
+ data->bi_cursor_bufpos = pb->data.p_char.bi_cursor_bufpos;
+ data->cursor_type = pb->data.p_char.cursor_type;
+ add_failed = add_emchar_rune (data);
+
+ if (add_failed)
+ goto oops_no_more_space;
+ break;
+ case PROP_STRING:
+ if (pb->data.p_string.str)
+ xfree (pb->data.p_string.str);
+ /* #### bogus bogus -- this doesn't do anything!
+ Should probably call add_bufbyte_string_runes(),
+ once that function is fixed. */
+ break;
+ case PROP_MINIBUF_PROMPT:
+ {
+ face_index old_findex = data->findex;
+ Bytind bi_old_bufpos = data->bi_bufpos;
+
+ data->findex = DEFAULT_INDEX;
+ data->bi_bufpos = 0;
+ data->cursor_type = NO_CURSOR;
+
+ while (pb->data.p_string.len > 0)
+ {
+ data->ch = charptr_emchar (pb->data.p_string.str);
+ add_failed = add_emchar_rune (data);
+
+ if (add_failed)
+ {
+ data->findex = old_findex;
+ data->bi_bufpos = bi_old_bufpos;
+ goto oops_no_more_space;
+ }
+ else
+ {
+ /* Complicated equivalent of ptr++, len-- */
+ Bufbyte *oldpos = pb->data.p_string.str;
+ INC_CHARPTR (pb->data.p_string.str);
+ pb->data.p_string.len -= pb->data.p_string.str - oldpos;
+ }
+ }
+
+ data->findex = old_findex;
+ /* ##### FIXME FIXME FIXME -- Upon successful return from
+ this function, data->bi_bufpos is automatically incremented.
+ However, we don't want that to happen if we were adding
+ the minibuffer prompt. */
+ {
+ struct buffer *buf =
+ XBUFFER (WINDOW_BUFFER (XWINDOW (data->window)));
+ /* #### Chuck fix this shit or I'm gonna scream! */
+ if (bi_old_bufpos > BI_BUF_BEGV (buf))
+ data->bi_bufpos = prev_bytind (buf, bi_old_bufpos);
+ else
+ /* #### is this correct? Does anyone know?
+ Does anyone care? Is this a cheesy hack or what? */
+ data->bi_bufpos = BI_BUF_BEGV (buf) - 1;
+ }
+ }
+ break;
+ case PROP_BLANK:
+ {
+ /* #### I think it's unnecessary and misleading to preserve
+ the blank_width, as it implies that the value carries
+ over from one rune to the next, which is wrong. */
+ int old_width = data->blank_width;
+ face_index old_findex = data->findex;
+
+ data->findex = pb->data.p_blank.findex;
+ data->blank_width = pb->data.p_blank.width;
+ data->bi_cursor_bufpos = 0;
+ data->cursor_type = IGNORE_CURSOR;
+
+ if (data->pixpos + data->blank_width > data->max_pixpos)
+ data->blank_width = data->max_pixpos - data->pixpos;
+
+ /* We pass a bogus value of char_tab_width. It shouldn't
+ matter because unless something is really screwed up
+ this call won't cause that arg to be used. */
+ add_failed = add_blank_rune (data, XWINDOW (data->window), 0);
+
+ /* This can happen in the case where we have a tab which
+ is wider than the window. */
+ if (data->blank_width != pb->data.p_blank.width)
+ {
+ pb->data.p_blank.width -= data->blank_width;
+ add_failed = ADD_FAILED;
+ }
+
+ data->findex = old_findex;
+ data->blank_width = old_width;
+
+ if (add_failed)
+ goto oops_no_more_space;
+ }
+ break;
+ default:
+ ABORT ();
+ }
+ }
+
+ oops_no_more_space:
+
+ data->bi_cursor_bufpos = bi_old_cursor_bufpos;
+ data->cursor_type = old_cursor_type;
+ if (elt < Dynarr_length (*prop))
+ {
+ Dynarr_delete_many (*prop, 0, elt);
+ return *prop;
+ }
+ else
+ {
+ Dynarr_free (*prop);
+ return NULL;
+ }
+}
+
+/* Add 'text layout glyphs at position POS_TYPE that are contained to
+ the display block, but add all other types to the appropriate list
+ of the display line. They will be added later by different
+ routines. */
+
+static prop_block_dynarr *
+add_glyph_rune (pos_data *data, struct glyph_block *gb, int pos_type,
+ int allow_cursor, struct glyph_cachel *cachel)
+{
+ struct window *w = XWINDOW (data->window);
+
+ /* If window faces changed, and glyph instance is text, then
+ glyph sizes might have changed too */
+ invalidate_glyph_geometry_maybe (gb->glyph, w);
+
+ /* This makes sure the glyph is in the cachels.
+
+ #### We do this to make sure the glyph is in the glyph cachels,
+ so that the dirty flag can be reset after redisplay has
+ finished. We should do this some other way, maybe by iterating
+ over the window cache of subwindows. */
+ get_glyph_cachel_index (w, gb->glyph);
+
+ /* A nil extent indicates a special glyph (ex. truncator). */
+ if (NILP (gb->extent)
+ || (pos_type == BEGIN_GLYPHS &&
+ extent_begin_glyph_layout (XEXTENT (gb->extent)) == GL_TEXT)
+ || (pos_type == END_GLYPHS &&
+ extent_end_glyph_layout (XEXTENT (gb->extent)) == GL_TEXT)
+ || pos_type == LEFT_GLYPHS || pos_type == RIGHT_GLYPHS)
+ {
+ struct rune rb;
+ int width;
+ int xoffset = 0;
+ int ascent, descent;
+ Lisp_Object baseline;
+ Lisp_Object face;
+ Lisp_Object instance;
+ face_index findex;
+ prop_block_dynarr *retval = 0;
+
+ if (cachel)
+ width = cachel->width;
+ else
+ width = glyph_width (gb->glyph, data->window);
+
+ if (!width)
+ return NULL;
+
+ if (data->start_col || data->start_col_xoffset)
+ {
+ int glyph_char_width = width / space_width (w);
+
+ /* If we still have not fully scrolled horizontally after
+ taking into account the width of the glyph, subtract its
+ width and return. */
+ if (glyph_char_width < data->start_col)
+ {
+ data->start_col -= glyph_char_width;
+ return NULL;
+ }
+ else if (glyph_char_width == data->start_col)
+ width = 0;
+ else
+ {
+ xoffset = space_width (w) * data->start_col;
+ width -= xoffset;
+
+ /* #### Can this happen? */
+ if (width < 0)
+ width = 0;
+ }
+
+ data->start_col = 0;
+ retval = add_hscroll_rune (data);
+
+ /* Could be caused by the handling of the hscroll rune. */
+ if (retval != NULL || !width)
+ return retval;
+ }
+ else
+ xoffset = 0;
+
+ if (data->pixpos + width > data->max_pixpos)
+ {
+ /* If this is the first object we are attempting to add to
+ the line then we ignore the horizontal_clip threshold.
+ Otherwise we will loop until the bottom of the window
+ continually failing to add this glyph because it is wider
+ than the window. We could alternatively just completely
+ ignore the glyph and proceed from there but I think that
+ this is a better solution.
+
+ This does, however, create a different problem in that we
+ can end up adding the object to every single line, never
+ getting any further - for instance an extent with a long
+ start-glyph that covers multitple following
+ characters. */
+ if (Dynarr_length (data->db->runes)
+ && data->max_pixpos - data->pixpos < horizontal_clip)
+ return ADD_FAILED;
+ else {
+ struct prop_block pb;
+
+ /* We need to account for the width of the end-of-line
+ glyph if there is nothing more in the line to display,
+ since we will not display it in this instance. It seems
+ kind of gross doing it here, but otherwise we have to
+ search the runes in create_text_block(). */
+ if (data->ch == '\n')
+ data->max_pixpos += data->end_glyph_width;
+ width = data->max_pixpos - data->pixpos;
+ /* Add the glyph we are displaying, but clipping, to the
+ propagation data so that we don't try and do it
+ again. */
+ retval = Dynarr_new (prop_block);
+ pb.type = PROP_GLYPH;
+ pb.data.p_glyph.glyph = gb->glyph;
+ pb.data.p_glyph.width = width;
+ Dynarr_add (retval, pb);
+ }
+ }
+
+ if (cachel)
+ {
+ ascent = cachel->ascent;
+ descent = cachel->descent;
+ }
+ else
+ {
+ ascent = glyph_ascent (gb->glyph, data->window);
+ descent = glyph_descent (gb->glyph, data->window);
+ }
+
+ baseline = glyph_baseline (gb->glyph, data->window);
+
+ rb.object.dglyph.descent = 0; /* Gets reset lower down, if it is known. */
+
+ if (glyph_contrib_p (gb->glyph, data->window))
+ {
+ /* A pixmap that has not had a baseline explicitly set. Its
+ contribution will be determined later. */
+ if (NILP (baseline))
+ {
+ int height = ascent + descent;
+ data->need_baseline_computation = 1;
+ data->max_pixmap_height = max (data->max_pixmap_height, height);
+ }
+
+ /* A string so determine contribution normally. */
+ else if (EQ (baseline, Qt))
+ {
+ data->new_ascent = max (data->new_ascent, ascent);
+ data->new_descent = max (data->new_descent, descent);
+ }
+
+ /* A pixmap with an explicitly set baseline. We determine the
+ contribution here. */
+ else if (INTP (baseline))
+ {
+ int height = ascent + descent;
+ int pix_ascent, pix_descent;
+
+ pix_ascent = height * XINT (baseline) / 100;
+ pix_descent = height - pix_ascent;
+
+ data->new_ascent = max (data->new_ascent, pix_ascent);
+ data->new_descent = max (data->new_descent, pix_descent);
+ data->max_pixmap_height = max (data->max_pixmap_height, height);
+
+ rb.object.dglyph.descent = pix_descent;
+ }
+
+ /* Otherwise something is screwed up. */
+ else
+ ABORT ();
+ }
+
+ face = glyph_face (gb->glyph, data->window);
+ if (NILP (face))
+ findex = data->findex;
+ else
+ findex = get_builtin_face_cache_index (w, face);
+
+ instance = glyph_image_instance (gb->glyph, data->window,
+ ERROR_ME_NOT, 1);
+ if (TEXT_IMAGE_INSTANCEP (instance))
+ {
+ Lisp_Object string = XIMAGE_INSTANCE_TEXT_STRING (instance);
+ face_index orig_findex = data->findex;
+ Bytind orig_bufpos = data->bi_bufpos;
+ Bytind orig_start_col_enabled = data->bi_start_col_enabled;
+
+ data->findex = findex;
+ data->bi_start_col_enabled = 0;
+ if (!allow_cursor)
+ data->bi_bufpos = 0;
+ add_bufbyte_string_runes (data, XSTRING_DATA (string),
+ XSTRING_LENGTH (string), 0, 1);
+ data->findex = orig_findex;
+ data->bi_bufpos = orig_bufpos;
+ data->bi_start_col_enabled = orig_start_col_enabled;
+ return retval;
+ }
+
+ rb.findex = findex;
+ rb.xpos = data->pixpos;
+ rb.width = width;
+ rb.bufpos = 0; /* glyphs are never "at" anywhere */
+ if (data->bi_endpos)
+ /* #### is this necessary at all? */
+ rb.endpos = bytind_to_bufpos (XBUFFER (WINDOW_BUFFER (w)),
+ data->bi_endpos);
+ else
+ rb.endpos = 0;
+ rb.type = RUNE_DGLYPH;
+ rb.object.dglyph.glyph = gb->glyph;
+ rb.object.dglyph.extent = gb->extent;
+ rb.object.dglyph.xoffset = xoffset;
+ rb.object.dglyph.ascent = ascent;
+ rb.object.dglyph.yoffset = 0; /* Until we know better, assume that it has
+ a normal (textual) baseline. */
+
+ if (allow_cursor)
+ {
+ rb.bufpos = bytind_to_bufpos (XBUFFER (WINDOW_BUFFER (w)),
+ data->bi_bufpos);
+
+ if (data->cursor_type == CURSOR_ON)
+ {
+ if (data->bi_bufpos == data->bi_cursor_bufpos)
+ {
+ rb.cursor_type = CURSOR_ON;
+ data->cursor_x = Dynarr_length (data->db->runes);
+ }
+ else
+ rb.cursor_type = CURSOR_OFF;
+ }
+ else if (data->cursor_type == NEXT_CURSOR)
+ {
+ rb.cursor_type = CURSOR_ON;
+ data->cursor_x = Dynarr_length (data->db->runes);
+ data->cursor_type = NO_CURSOR;
+ }
+ else if (data->cursor_type == IGNORE_CURSOR)
+ rb.cursor_type = IGNORE_CURSOR;
+ else if (data->cursor_type == NO_CURSOR)
+ rb.cursor_type = NO_CURSOR;
+ else
+ rb.cursor_type = CURSOR_OFF;
+ }
+ else
+ rb.cursor_type = CURSOR_OFF;
+
+ Dynarr_add (data->db->runes, rb);
+ data->pixpos += width;
+
+ return retval;
+ }
+ else
+ {
+ if (!NILP (glyph_face (gb->glyph, data->window)))
+ gb->findex =
+ get_builtin_face_cache_index (w, glyph_face (gb->glyph,
+ data->window));
+ else
+ gb->findex = data->findex;
+
+ if (pos_type == BEGIN_GLYPHS)
+ {
+ if (!data->dl->left_glyphs)
+ data->dl->left_glyphs = Dynarr_new (glyph_block);
+ Dynarr_add (data->dl->left_glyphs, *gb);
+ return NULL;
+ }
+ else if (pos_type == END_GLYPHS)
+ {
+ if (!data->dl->right_glyphs)
+ data->dl->right_glyphs = Dynarr_new (glyph_block);
+ Dynarr_add (data->dl->right_glyphs, *gb);
+ return NULL;
+ }
+ else
+ ABORT (); /* there are no unknown types */
+ }
+
+ return NULL;
+}
+
+/* Add all glyphs at position POS_TYPE that are contained in the given
+ data. */
+
+static prop_block_dynarr *
+add_glyph_runes (pos_data *data, int pos_type)
+{
+ /* #### This still needs to handle the start_col parameter. Duh, Chuck,
+ why didn't you just modify add_glyph_rune in the first place? */
+ int elt;
+ glyph_block_dynarr *glyph_arr = (pos_type == BEGIN_GLYPHS
+ ? data->ef->begin_glyphs
+ : data->ef->end_glyphs);
+ prop_block_dynarr *prop;
+
+ for (elt = 0; elt < Dynarr_length (glyph_arr); elt++)
+ {
+ prop = add_glyph_rune (data, Dynarr_atp (glyph_arr, elt), pos_type, 0,
+ 0);
+
+ if (prop)
+ {
+ /* #### Add some propagation information. */
+ return prop;
+ }
+ }
+
+ Dynarr_reset (glyph_arr);
+
+ return NULL;
+}
+
+/* Given a position for a buffer in a window, ensure that the given
+ display line DL accurately represents the text on a line starting
+ at the given position.
+
+ NOTE NOTE NOTE NOTE: This function works with and returns Bytinds.
+ You must do appropriate conversion. */
+
+static Bytind
+create_text_block (struct window *w, struct display_line *dl,
+ Bytind bi_start_pos, prop_block_dynarr **prop,
+ int type)
+{
+ struct frame *f = XFRAME (w->frame);
+ struct buffer *b = XBUFFER (w->buffer);
+ struct device *d = XDEVICE (f->device);
+
+ pos_data data;
+
+ /* Don't display anything in the minibuffer if this window is not on
+ a selected frame. We consider all other windows to be active
+ minibuffers as it simplifies the coding. */
+ int active_minibuffer = (!MINI_WINDOW_P (w) ||
+ (f == device_selected_frame (d)) ||
+ is_surrogate_for_selected_frame (f));
+
+ int truncate_win = window_truncation_on (w);
+
+ /* If the buffer's value of selective_display is an integer then
+ only lines that start with less than selective_display columns of
+ space will be displayed. If selective_display is t then all text
+ after a ^M is invisible. */
+ int selective = (INTP (b->selective_display)
+ ? XINT (b->selective_display)
+ : (!NILP (b->selective_display) ? -1 : 0));
+
+ /* The variable ctl-arrow allows the user to specify what characters
+ can actually be displayed and which octal should be used for.
+ #### This variable should probably have some rethought done to
+ it.
+
+ #### It would also be really nice if you could specify that
+ the characters come out in hex instead of in octal. Mule
+ does that by adding a ctl-hexa variable similar to ctl-arrow,
+ but that's bogus -- we need a more general solution. I
+ think you need to extend the concept of display tables
+ into a more general conversion mechanism. Ideally you
+ could specify a Lisp function that converts characters,
+ but this violates the Second Golden Rule and besides would
+ make things way way way way slow.
+
+ So instead, we extend the display-table concept, which was
+ historically limited to 256-byte vectors, to one of the
+ following:
+
+ a) A 256-entry vector, for backward compatibility;
+ b) char-table, mapping characters to values;
+ c) range-table, mapping ranges of characters to values;
+ d) a list of the above.
+
+ The (d) option allows you to specify multiple display tables
+ instead of just one. Each display table can specify conversions
+ for some characters and leave others unchanged. The way the
+ character gets displayed is determined by the first display table
+ with a binding for that character. This way, you could call a
+ function `enable-hex-display' that adds a hex display-table to
+ the list of display tables for the current buffer.
+
+ #### ...not yet implemented... Also, we extend the concept of
+ "mapping" to include a printf-like spec. Thus you can make all
+ extended characters show up as hex with a display table like
+ this:
+
+ #s(range-table data ((256 524288) (format "%x")))
+
+ Since more than one display table is possible, you have
+ great flexibility in mapping ranges of characters. */
+ Emchar printable_min = (CHAR_OR_CHAR_INTP (b->ctl_arrow)
+ ? XCHAR_OR_CHAR_INT (b->ctl_arrow)
+ : ((EQ (b->ctl_arrow, Qt) || EQ (b->ctl_arrow, Qnil))
+ ? 255 : 160));
+
+ Lisp_Object face_dt, window_dt;
+
+ /* The text display block for this display line. */
+ struct display_block *db = get_display_block_from_line (dl, TEXT);
+
+ /* The first time through the main loop we need to force the glyph
+ data to be updated. */
+ int initial = 1;
+
+ /* Apparently the new extent_fragment_update returns an end position
+ equal to the position passed in if there are no more runs to be
+ displayed. */
+ int no_more_frags = 0;
+
+ Lisp_Object synch_minibuffers_value =
+ symbol_value_in_buffer (Qsynchronize_minibuffers, w->buffer);
+
+ dl->used_prop_data = 0;
+ dl->num_chars = 0;
+ dl->line_continuation = 0;
+
+ xzero (data);
+ data.ef = extent_fragment_new (w->buffer, f);
+
+ /* These values are used by all of the rune addition routines. We add
+ them to this structure for ease of passing. */
+ data.d = d;
+ XSETWINDOW (data.window, w);
+ data.string = Qnil;
+ data.db = db;
+ data.dl = dl;
+
+ data.bi_bufpos = bi_start_pos;
+ data.pixpos = dl->bounds.left_in;
+ data.last_charset = Qunbound;
+ data.last_findex = DEFAULT_INDEX;
+ data.result_str = Qnil;
+
+ /* Set the right boundary adjusting it to take into account any end
+ glyph. Save the width of the end glyph for later use. */
+ data.max_pixpos = dl->bounds.right_in;
+ if (truncate_win)
+ data.end_glyph_width = GLYPH_CACHEL_WIDTH (w, TRUN_GLYPH_INDEX);
+ else
+ data.end_glyph_width = GLYPH_CACHEL_WIDTH (w, CONT_GLYPH_INDEX);
+ data.max_pixpos -= data.end_glyph_width;
+
+ if (cursor_in_echo_area && MINI_WINDOW_P (w) && echo_area_active (f))
+ {
+ data.bi_cursor_bufpos = BI_BUF_ZV (b);
+ data.cursor_type = CURSOR_ON;
+ }
+ else if (MINI_WINDOW_P (w) && !active_minibuffer)
+ data.cursor_type = NO_CURSOR;
+ else if (w == XWINDOW (FRAME_SELECTED_WINDOW (f)) &&
+ EQ(DEVICE_CONSOLE(d), Vselected_console) &&
+ d == XDEVICE(CONSOLE_SELECTED_DEVICE(XCONSOLE(DEVICE_CONSOLE(d))))&&
+ f == XFRAME(DEVICE_SELECTED_FRAME(d)))
+ {
+ data.bi_cursor_bufpos = BI_BUF_PT (b);
+ data.cursor_type = CURSOR_ON;
+ }
+ else if (w == XWINDOW (FRAME_SELECTED_WINDOW (f)))
+ {
+ data.bi_cursor_bufpos = bi_marker_position (w->pointm[type]);
+ data.cursor_type = CURSOR_ON;
+ }
+ else
+ data.cursor_type = NO_CURSOR;
+ data.cursor_x = -1;
+
+ data.start_col = w->hscroll;
+ data.start_col_xoffset = w->left_xoffset;
+ data.bi_start_col_enabled = (w->hscroll ? bi_start_pos : 0);
+ data.hscroll_glyph_width_adjust = 0;
+
+ /* We regenerate the line from the very beginning. */
+ Dynarr_reset (db->runes);
+
+ /* Why is this less than or equal and not just less than? If the
+ starting position is already equal to the maximum we can't add
+ anything else, right? Wrong. We might still have a newline to
+ add. A newline can use the room allocated for an end glyph since
+ if we add it we know we aren't going to be adding any end
+ glyph. */
+
+ /* #### Chuck -- I think this condition should be while (1).
+ Otherwise if (e.g.) there is one begin-glyph and one end-glyph
+ and the begin-glyph ends exactly at the end of the window, the
+ end-glyph and text might not be displayed. while (1) ensures
+ that the loop terminates only when either (a) there is
+ propagation data or (b) the end-of-line or end-of-buffer is hit.
+
+ #### Also I think you need to ensure that the operation
+ "add begin glyphs; add end glyphs; add text" is atomic and
+ can't get interrupted in the middle. If you run off the end
+ of the line during that operation, then you keep accumulating
+ propagation data until you're done. Otherwise, if the (e.g.)
+ there's a begin glyph at a particular position and attempting
+ to display that glyph results in window-end being hit and
+ propagation data being generated, then the character at that
+ position won't be displayed.
+
+ #### See also the comment after the end of this loop, below.
+ */
+ while (data.pixpos <= data.max_pixpos
+ && (active_minibuffer || !NILP (synch_minibuffers_value)))
+ {
+ /* #### This check probably should not be necessary. */
+ if (data.bi_bufpos > BI_BUF_ZV (b))
+ {
+ /* #### urk! More of this lossage! */
+ data.bi_bufpos--;
+ goto done;
+ }
+
+ /* If selective display was an integer and we aren't working on
+ a continuation line then find the next line we are actually
+ supposed to display. */
+ if (selective > 0
+ && (data.bi_bufpos == BI_BUF_BEGV (b)
+ || BUF_FETCH_CHAR (b, prev_bytind (b, data.bi_bufpos)) == '\n'))
+ {
+ while (bi_spaces_at_point (b, data.bi_bufpos) >= selective)
+ {
+ data.bi_bufpos =
+ bi_find_next_newline_no_quit (b, data.bi_bufpos, 1);
+ if (data.bi_bufpos >= BI_BUF_ZV (b))
+ {
+ data.bi_bufpos = BI_BUF_ZV (b);
+ goto done;
+ }
+ }
+ }
+
+ /* Check for face changes. */
+ if (initial || (!no_more_frags && data.bi_bufpos == data.ef->end))
+ {
+ Lisp_Object last_glyph = Qnil;
+
+ /* Deal with glyphs that we have already displayed. The
+ theory is that if we end up with a PROP_GLYPH in the
+ propagation data then we are clipping the glyph and there
+ can be no propagation data before that point. The theory
+ works because we always recalculate the extent-fragments
+ for propagated data, we never actually propagate the
+ fragments that still need to be displayed. */
+ if (*prop && Dynarr_atp (*prop, 0)->type == PROP_GLYPH)
+ {
+ last_glyph = Dynarr_atp (*prop, 0)->data.p_glyph.glyph;
+ Dynarr_free (*prop);
+ *prop = 0;
+ }
+ /* Now compute the face and begin/end-glyph information. */
+ data.findex =
+ /* Remember that the extent-fragment routines deal in Bytind's. */
+ extent_fragment_update (w, data.ef, data.bi_bufpos, last_glyph);
+
+ get_display_tables (w, data.findex, &face_dt, &window_dt);
+
+ if (data.bi_bufpos == data.ef->end)
+ no_more_frags = 1;
+ }
+ initial = 0;
+
+ /* Determine what is next to be displayed. We first handle any
+ glyphs returned by glyphs_at_bufpos. If there are no glyphs to
+ display then we determine what to do based on the character at the
+ current buffer position. */
+
+ /* If the current position is covered by an invisible extent, do
+ nothing (except maybe add some ellipses).
+
+ #### The behavior of begin and end-glyphs at the edge of an
+ invisible extent should be investigated further. This is
+ fairly low priority though. */
+ if (data.ef->invisible)
+ {
+ /* #### Chuck, perhaps you could look at this code? I don't
+ really know what I'm doing. */
+ if (*prop)
+ {
+ Dynarr_free (*prop);
+ *prop = 0;
+ }
+
+ /* The extent fragment code only sets this when we should
+ really display the ellipses. It makes sure the ellipses
+ don't get displayed more than once in a row. */
+ if (data.ef->invisible_ellipses)
+ {
+ struct glyph_block gb;
+
+ data.ef->invisible_ellipses_already_displayed = 1;
+ data.ef->invisible_ellipses = 0;
+ gb.extent = Qnil;
+ gb.glyph = Vinvisible_text_glyph;
+ *prop = add_glyph_rune (&data, &gb, BEGIN_GLYPHS, 0,
+ GLYPH_CACHEL (w, INVIS_GLYPH_INDEX));
+ /* Perhaps they shouldn't propagate if the very next thing
+ is to display a newline (for compatibility with
+ selective-display-ellipses)? Maybe that's too
+ abstruse. */
+ if (*prop)
+ goto done;
+ }
+
+ /* If point is in an invisible region we place it on the
+ next visible character. */
+ if (data.cursor_type == CURSOR_ON
+ && data.bi_bufpos == data.bi_cursor_bufpos)
+ {
+ data.cursor_type = NEXT_CURSOR;
+ }
+
+ /* #### What if we we're dealing with a display table? */
+ if (data.start_col)
+ data.start_col--;
+
+ if (data.bi_bufpos == BI_BUF_ZV (b))
+ goto done;
+ else
+ INC_BYTIND (b, data.bi_bufpos);
+ }
+
+ /* If there is propagation data, then it represents the current
+ buffer position being displayed. Add them and advance the
+ position counter. This might also add the minibuffer
+ prompt. */
+ else if (*prop)
+ {
+ dl->used_prop_data = 1;
+ *prop = add_propagation_runes (prop, &data);
+
+ if (*prop)
+ goto done; /* gee, a really narrow window */
+ else if (data.bi_bufpos == BI_BUF_ZV (b))
+ goto done;
+ else if (data.bi_bufpos < BI_BUF_BEGV (b))
+ /* #### urk urk urk! Aborts are not very fun! Fix this please! */
+ data.bi_bufpos = BI_BUF_BEGV (b);
+ else
+ INC_BYTIND (b, data.bi_bufpos);
+ }
+
+ /* If there are end glyphs, add them to the line. These are
+ the end glyphs for the previous run of text. We add them
+ here rather than doing them at the end of handling the
+ previous run so that glyphs at the beginning and end of
+ a line are handled correctly. */
+ else if (Dynarr_length (data.ef->end_glyphs) > 0
+ || Dynarr_length (data.ef->begin_glyphs) > 0)
+ {
+ glyph_block_dynarr* tmpglyphs = 0;
+ /* #### I think this is safe, but could be wrong. */
+ data.ch = BI_BUF_FETCH_CHAR (b, data.bi_bufpos);
+
+ if (Dynarr_length (data.ef->end_glyphs) > 0)
+ {
+ *prop = add_glyph_runes (&data, END_GLYPHS);
+ tmpglyphs = data.ef->end_glyphs;
+ }
+
+ /* If there are begin glyphs, add them to the line. */
+ if (!*prop && Dynarr_length (data.ef->begin_glyphs) > 0)
+ {
+ *prop = add_glyph_runes (&data, BEGIN_GLYPHS);
+ tmpglyphs = data.ef->begin_glyphs;
+ }
+
+ if (*prop)
+ {
+ /* If we just clipped a glyph and we are at the end of a
+ line and there are more glyphs to display then do
+ appropriate processing to not get a continuation
+ glyph. */
+ if (*prop != ADD_FAILED
+ && Dynarr_atp (*prop, 0)->type == PROP_GLYPH
+ && data.ch == '\n')
+ {
+ /* If there are no more glyphs then do the normal
+ processing.
+
+ #### This doesn't actually work if the same glyph is
+ present more than once in the block. To solve
+ this we would have to carry the index around
+ which might be problematic since the fragment is
+ recalculated for each line. */
+ if (EQ (Dynarr_end (tmpglyphs)->glyph,
+ Dynarr_atp (*prop, 0)->data.p_glyph.glyph))
+ {
+ Dynarr_free (*prop);
+ *prop = 0;
+ }
+ else {
+ data.blank_width = DEVMETH (d, eol_cursor_width, ());
+ add_emchar_rune (&data); /* discard prop data. */
+ goto done;
+ }
+ }
+ else
+ goto done;
+ }
+ }
+
+ /* If at end-of-buffer, we've already processed begin and
+ end-glyphs at this point and there's no text to process,
+ so we're done. */
+ else if (data.bi_bufpos == BI_BUF_ZV (b))
+ goto done;
+
+ else
+ {
+ Lisp_Object entry = Qnil;
+ /* Get the character at the current buffer position. */
+ data.ch = BI_BUF_FETCH_CHAR (b, data.bi_bufpos);
+ if (!NILP (face_dt) || !NILP (window_dt))
+ entry = display_table_entry (data.ch, face_dt, window_dt);
+
+ /* If there is a display table entry for it, hand it off to
+ add_disp_table_entry_runes and let it worry about it. */
+ if (!NILP (entry) && !EQ (entry, make_char (data.ch)))
+ {
+ *prop = add_disp_table_entry_runes (&data, entry);
+
+ if (*prop)
+ goto done;
+ }
+
+ /* Check if we have hit a newline character. If so, add a marker
+ to the line and end this loop. */
+ else if (data.ch == '\n')
+ {
+ /* We aren't going to be adding an end glyph so give its
+ space back in order to make sure that the cursor can
+ fit. */
+ data.max_pixpos += data.end_glyph_width;
+
+ if (selective > 0
+ && (bi_spaces_at_point
+ (b, next_bytind (b, data.bi_bufpos))
+ >= selective))
+ {
+ if (!NILP (b->selective_display_ellipses))
+ {
+ struct glyph_block gb;
+
+ gb.extent = Qnil;
+ gb.glyph = Vinvisible_text_glyph;
+ add_glyph_rune (&data, &gb, BEGIN_GLYPHS, 0,
+ GLYPH_CACHEL (w, INVIS_GLYPH_INDEX));
+ }
+ else
+ {
+ /* Cheesy, cheesy, cheesy. We mark the end of the
+ line with a special "character rune" whose width
+ is the EOL cursor width and whose character is
+ the non-printing character '\n'. */
+ data.blank_width = DEVMETH (d, eol_cursor_width, ());
+ *prop = add_emchar_rune (&data);
+ }
+
+ /* We need to set data.bi_bufpos to the start of the
+ next visible region in order to make this line
+ appear to contain all of the invisible area.
+ Otherwise, the line cache won't work
+ correctly. */
+ INC_BYTIND (b, data.bi_bufpos);
+ while (bi_spaces_at_point (b, data.bi_bufpos) >= selective)
+ {
+ data.bi_bufpos =
+ bi_find_next_newline_no_quit (b, data.bi_bufpos, 1);
+ if (data.bi_bufpos >= BI_BUF_ZV (b))
+ {
+ data.bi_bufpos = BI_BUF_ZV (b);
+ break;
+ }
+ }
+ if (BI_BUF_FETCH_CHAR
+ (b, prev_bytind (b, data.bi_bufpos)) == '\n')
+ DEC_BYTIND (b, data.bi_bufpos);
+ }
+ else
+ {
+ data.blank_width = DEVMETH (d, eol_cursor_width, ());
+ *prop = add_emchar_rune (&data);
+ }
+
+ goto done;
+ }
+
+ /* If the current character is ^M, and selective display is
+ enabled, then add the invisible-text-glyph if
+ selective-display-ellipses is set. In any case, this
+ line is done. */
+ else if (data.ch == (('M' & 037)) && selective == -1)
+ {
+ Bytind bi_next_bufpos;
+
+ /* Find the buffer position at the end of the line. */
+ bi_next_bufpos =
+ bi_find_next_newline_no_quit (b, data.bi_bufpos, 1);
+ if (BI_BUF_FETCH_CHAR (b, prev_bytind (b, bi_next_bufpos))
+ == '\n')
+ DEC_BYTIND (b, bi_next_bufpos);
+
+ /* If the cursor is somewhere in the elided text make
+ sure that the cursor gets drawn appropriately. */
+ if (data.cursor_type == CURSOR_ON
+ && (data.bi_cursor_bufpos >= data.bi_bufpos &&
+ data.bi_cursor_bufpos < bi_next_bufpos))
+ {
+ data.cursor_type = NEXT_CURSOR;
+ }
+
+ /* We won't be adding a truncation or continuation glyph
+ so give up the room allocated for them. */
+ data.max_pixpos += data.end_glyph_width;
+
+ if (!NILP (b->selective_display_ellipses))
+ {
+ /* We don't propagate anything from the invisible
+ text glyph if it fails to fit. This is
+ intentional. */
+ struct glyph_block gb;
+
+ gb.extent = Qnil;
+ gb.glyph = Vinvisible_text_glyph;
+ add_glyph_rune (&data, &gb, BEGIN_GLYPHS, 1,
+ GLYPH_CACHEL (w, INVIS_GLYPH_INDEX));
+ }
+
+ /* Set the buffer position to the end of the line. We
+ need to do this before potentially adding a newline
+ so that the cursor flag will get set correctly (if
+ needed). */
+ data.bi_bufpos = bi_next_bufpos;
+
+ if (NILP (b->selective_display_ellipses)
+ || data.bi_cursor_bufpos == bi_next_bufpos)
+ {
+ /* We have to at least add a newline character so
+ that the cursor shows up properly. */
+ data.ch = '\n';
+ data.blank_width = DEVMETH (d, eol_cursor_width, ());
+ data.findex = DEFAULT_INDEX;
+ data.start_col = 0;
+ data.start_col_xoffset = 0;
+ data.bi_start_col_enabled = 0;
+
+ add_emchar_rune (&data);
+ }
+
+ /* This had better be a newline but doing it this way
+ we'll see obvious incorrect results if it isn't. No
+ need to ABORT here. */
+ data.ch = BI_BUF_FETCH_CHAR (b, data.bi_bufpos);
+
+ goto done;
+ }
+
+ /* If the current character is considered to be printable, then
+ just add it. */
+ else if (data.ch >= printable_min)
+ {
+ *prop = add_emchar_rune (&data);
+ if (*prop)
+ goto done;
+ }
+
+ /* If the current character is a tab, determine the next tab
+ starting position and add a blank rune which extends from the
+ current pixel position to that starting position. */
+ else if (data.ch == '\t')
+ {
+ int tab_start_pixpos = data.pixpos;
+ int next_tab_start;
+ int char_tab_width;
+ int prop_width = 0;
+
+ if (data.start_col > 1)
+ tab_start_pixpos -= (space_width (w) * (data.start_col - 1))
+ + data.start_col_xoffset;
+
+ next_tab_start =
+ next_tab_position (w, tab_start_pixpos,
+ dl->bounds.left_in +
+ data.hscroll_glyph_width_adjust);
+ if (next_tab_start > data.max_pixpos)
+ {
+ prop_width = next_tab_start - data.max_pixpos;
+ next_tab_start = data.max_pixpos;
+ }
+ data.blank_width = next_tab_start - data.pixpos;
+ char_tab_width =
+ (next_tab_start - tab_start_pixpos) / space_width (w);
+
+ *prop = add_blank_rune (&data, w, char_tab_width);
+
+ /* add_blank_rune is only supposed to be called with
+ sizes guaranteed to fit in the available space. */
+ assert (!(*prop));
+
+ if (prop_width)
+ {
+ struct prop_block pb;
+ *prop = Dynarr_new (prop_block);
+
+ pb.type = PROP_BLANK;
+ pb.data.p_blank.width = prop_width;
+ pb.data.p_blank.findex = data.findex;
+ Dynarr_add (*prop, pb);
+
+ goto done;
+ }
+ }
+
+ /* If character is a control character, pass it off to
+ add_control_char_runes.
+
+ The is_*() routines have undefined results on
+ arguments outside of the range [-1, 255]. (This
+ often bites people who carelessly use `char' instead
+ of `unsigned char'.)
+ */
+ else if (data.ch < 0x100 && iscntrl ((Bufbyte) data.ch))
+ {
+ *prop = add_control_char_runes (&data, b);
+
+ if (*prop)
+ goto done;
+ }
+
+ /* If the character is above the ASCII range and we have not
+ already handled it, then print it as an octal number. */
+ else if (data.ch >= 0200)
+ {
+ *prop = add_octal_runes (&data);
+
+ if (*prop)
+ goto done;
+ }
+
+ /* Assume the current character is considered to be printable,
+ then just add it. */
+ else
+ {
+ *prop = add_emchar_rune (&data);
+ if (*prop)
+ goto done;
+ }
+
+ INC_BYTIND (b, data.bi_bufpos);
+ }
+ }
+
+done:
+
+ /* Determine the starting point of the next line if we did not hit the
+ end of the buffer. */
+ if (data.bi_bufpos < BI_BUF_ZV (b)
+ && (active_minibuffer || !NILP (synch_minibuffers_value)))
+ {
+ /* #### This check is not correct. If the line terminated
+ due to a begin-glyph or end-glyph hitting window-end, then
+ data.ch will not point to the character at data.bi_bufpos. If
+ you make the two changes mentioned at the top of this loop,
+ you should be able to say '(if (*prop))'. That should also
+ make it possible to eliminate the data.bi_bufpos < BI_BUF_ZV (b)
+ check. */
+
+ /* The common case is that the line ended because we hit a newline.
+ In that case, the next character is just the next buffer
+ position. */
+ if (data.ch == '\n')
+ {
+ /* If data.start_col_enabled is still true, then the window is
+ scrolled far enough so that nothing on this line is visible.
+ We need to stick a truncation glyph at the beginning of the
+ line in that case unless the line is completely blank. */
+ if (data.bi_start_col_enabled)
+ {
+ if (data.cursor_type == CURSOR_ON)
+ {
+ if (data.bi_cursor_bufpos >= bi_start_pos
+ && data.bi_cursor_bufpos <= data.bi_bufpos)
+ data.bi_cursor_bufpos = data.bi_bufpos;
+ }
+ data.findex = DEFAULT_INDEX;
+ data.start_col = 0;
+ data.bi_start_col_enabled = 0;
+
+ if (data.bi_bufpos != bi_start_pos)
+ {
+ struct glyph_block gb;
+
+ gb.extent = Qnil;
+ gb.glyph = Vhscroll_glyph;
+ add_glyph_rune (&data, &gb, BEGIN_GLYPHS, 0,
+ GLYPH_CACHEL (w, HSCROLL_GLYPH_INDEX));
+ }
+ else
+ {
+ /* This duplicates code down below to add a newline to
+ the end of an otherwise empty line.*/
+ data.ch = '\n';
+ data.blank_width = DEVMETH (d, eol_cursor_width, ());
+
+ add_emchar_rune (&data);
+ }
+ }
+
+ INC_BYTIND (b, data.bi_bufpos);
+ }
+
+ /* Otherwise we have a buffer line which cannot fit on one display
+ line. */
+ else
+ {
+ struct glyph_block gb;
+ struct glyph_cachel *cachel;
+
+ /* If the line is to be truncated then we actually have to look
+ for the next newline. We also add the end-of-line glyph which
+ we know will fit because we adjusted the right border before
+ we starting laying out the line. */
+ data.max_pixpos += data.end_glyph_width;
+ data.findex = DEFAULT_INDEX;
+ gb.extent = Qnil;
+
+ if (truncate_win)
+ {
+ Bytind bi_pos;
+
+ /* Now find the start of the next line. */
+ bi_pos = bi_find_next_newline_no_quit (b, data.bi_bufpos, 1);
+
+ /* If the cursor is past the truncation line then we
+ make it appear on the truncation glyph. If we've hit
+ the end of the buffer then we also make the cursor
+ appear unless eob is immediately preceded by a
+ newline. In that case the cursor should actually
+ appear on the next line. */
+ if (data.cursor_type == CURSOR_ON
+ && data.bi_cursor_bufpos >= data.bi_bufpos
+ && (data.bi_cursor_bufpos < bi_pos ||
+ (bi_pos == BI_BUF_ZV (b)
+ && (bi_pos == BI_BUF_BEGV (b)
+ || (BI_BUF_FETCH_CHAR (b, prev_bytind (b, bi_pos))
+ != '\n')))))
+ data.bi_cursor_bufpos = bi_pos;
+ else
+ data.cursor_type = NO_CURSOR;
+
+ data.bi_bufpos = bi_pos;
+ gb.glyph = Vtruncation_glyph;
+ cachel = GLYPH_CACHEL (w, TRUN_GLYPH_INDEX);
+ }
+ else
+ {
+ /* The cursor can never be on the continuation glyph. */
+ data.cursor_type = NO_CURSOR;
+
+ /* data.bi_bufpos is already at the start of the next line. */
+
+ dl->line_continuation = 1;
+ gb.glyph = Vcontinuation_glyph;
+ cachel = GLYPH_CACHEL (w, CONT_GLYPH_INDEX);
+ }
+
+ add_glyph_rune (&data, &gb, BEGIN_GLYPHS, 0, cachel);
+
+ if (truncate_win && data.bi_bufpos == BI_BUF_ZV (b)
+ && BI_BUF_FETCH_CHAR (b, prev_bytind (b, BI_BUF_ZV (b))) != '\n')
+ /* #### Damn this losing shit. */
+ data.bi_bufpos++;
+ }
+ }
+ else if ((active_minibuffer || !NILP (synch_minibuffers_value))
+ && (!echo_area_active (f) || data.bi_bufpos == BI_BUF_ZV (b)))
+ {
+ /* We need to add a marker to the end of the line since there is no
+ newline character in order for the cursor to get drawn. We label
+ it as a newline so that it gets handled correctly by the
+ whitespace routines below. */
+
+ data.ch = '\n';
+ data.blank_width = DEVMETH (d, eol_cursor_width, ());
+ data.findex = DEFAULT_INDEX;
+ data.start_col = 0;
+ data.start_col_xoffset = 0;
+ data.bi_start_col_enabled = 0;
+
+ data.max_pixpos += data.blank_width;
+ add_emchar_rune (&data);
+ data.max_pixpos -= data.blank_width;
+
+ /* #### urk! Chuck, this shit is bad news. Going around
+ manipulating invalid positions is guaranteed to result in
+ trouble sooner or later. */
+ data.bi_bufpos = BI_BUF_ZV (b) + 1;
+ }
+
+ /* Calculate left whitespace boundary. */
+ {
+ int elt = 0;
+
+ /* Whitespace past a newline is considered right whitespace. */
+ while (elt < Dynarr_length (db->runes))
+ {
+ struct rune *rb = Dynarr_atp (db->runes, elt);
+
+ if ((rb->type == RUNE_CHAR && rb->object.chr.ch == ' ')
+ || rb->type == RUNE_BLANK)
+ {
+ dl->bounds.left_white += rb->width;
+ elt++;
+ }
+ else
+ elt = Dynarr_length (db->runes);
+ }
+ }
+
+ /* Calculate right whitespace boundary. */
+ {
+ int elt = Dynarr_length (db->runes) - 1;
+ int done = 0;
+
+ while (!done && elt >= 0)
+ {
+ struct rune *rb = Dynarr_atp (db->runes, elt);
+
+ if (!(rb->type == RUNE_CHAR && rb->object.chr.ch < 0x100
+ && isspace (rb->object.chr.ch))
+ && !rb->type == RUNE_BLANK)
+ {
+ dl->bounds.right_white = rb->xpos + rb->width;
+ done = 1;
+ }
+
+ elt--;
+
+ }
+
+ /* The line is blank so everything is considered to be right
+ whitespace. */
+ if (!done)
+ dl->bounds.right_white = dl->bounds.left_in;
+ }
+
+ /* Set the display blocks bounds. */
+ db->start_pos = dl->bounds.left_in;
+ if (Dynarr_length (db->runes))
+ {
+ struct rune *rb = Dynarr_atp (db->runes, Dynarr_length (db->runes) - 1);
+
+ db->end_pos = rb->xpos + rb->width;
+ }
+ else
+ db->end_pos = dl->bounds.right_white;
+
+ calculate_baseline (&data);
+
+ dl->ascent = data.new_ascent;
+ dl->descent = data.new_descent;
+
+ {
+ unsigned short ascent = (unsigned short) XINT (w->minimum_line_ascent);
+
+ if (dl->ascent < ascent)
+ dl->ascent = ascent;
+ }
+ {
+ unsigned short descent = (unsigned short) XINT (w->minimum_line_descent);
+
+ if (dl->descent < descent)
+ dl->descent = descent;
+ }
+
+ calculate_yoffset (dl, db);
+
+ dl->cursor_elt = data.cursor_x;
+ /* #### lossage lossage lossage! Fix this shit! */
+ if (data.bi_bufpos > BI_BUF_ZV (b))
+ dl->end_bufpos = BUF_ZV (b);
+ else
+ dl->end_bufpos = bytind_to_bufpos (b, data.bi_bufpos) - 1;
+ if (truncate_win)
+ data.dl->num_chars = column_at_point (b, dl->end_bufpos, 0);
+ else
+ /* This doesn't correctly take into account tabs and control
+ characters but if the window isn't being truncated then this
+ value isn't going to end up being used anyhow. */
+ data.dl->num_chars = dl->end_bufpos - dl->bufpos;
+
+ /* #### handle horizontally scrolled line with text none of which
+ was actually laid out. */
+
+ /* #### handle any remainder of overlay arrow */
+
+ if (*prop == ADD_FAILED)
+ *prop = NULL;
+
+ if (truncate_win && *prop)
+ {
+ Dynarr_free (*prop);
+ *prop = NULL;
+ }
+
+ extent_fragment_delete (data.ef);
+
+ /* #### If we started at EOB, then make sure we return a value past
+ it so that regenerate_window will exit properly. This is bogus.
+ The main loop should get fixed so that it isn't necessary to call
+ this function if we are already at EOB. */
+
+ if (data.bi_bufpos == BI_BUF_ZV (b) && bi_start_pos == BI_BUF_ZV (b))
+ return data.bi_bufpos + 1; /* Yuck! */
+ else
+ return data.bi_bufpos;
+}
+
+/* Display the overlay arrow at the beginning of the given line. */
+
+static int
+create_overlay_glyph_block (struct window *w, struct display_line *dl)
+{
+ struct frame *f = XFRAME (w->frame);
+ struct device *d = XDEVICE (f->device);
+ pos_data data;
+
+ /* If Voverlay_arrow_string isn't valid then just fail silently. */
+ if (!STRINGP (Voverlay_arrow_string) && !GLYPHP (Voverlay_arrow_string))
+ return 0;
+
+ xzero (data);
+ data.ef = NULL;
+ data.d = d;
+ XSETWINDOW (data.window, w);
+ data.db = get_display_block_from_line (dl, OVERWRITE);
+ data.dl = dl;
+ data.pixpos = dl->bounds.left_in;
+ data.max_pixpos = dl->bounds.right_in;
+ data.cursor_type = NO_CURSOR;
+ data.cursor_x = -1;
+ data.findex = DEFAULT_INDEX;
+ data.last_charset = Qunbound;
+ data.last_findex = DEFAULT_INDEX;
+ data.result_str = Qnil;
+ data.string = Qnil;
+
+ Dynarr_reset (data.db->runes);
+
+ if (STRINGP (Voverlay_arrow_string))
+ {
+ add_bufbyte_string_runes
+ (&data,
+ XSTRING_DATA (Voverlay_arrow_string),
+ XSTRING_LENGTH (Voverlay_arrow_string),
+ 1, 0);
+ }
+ else if (GLYPHP (Voverlay_arrow_string))
+ {
+ struct glyph_block gb;
+
+ gb.glyph = Voverlay_arrow_string;
+ gb.extent = Qnil;
+ add_glyph_rune (&data, &gb, BEGIN_GLYPHS, 0, 0);
+ }
+
+ calculate_baseline (&data);
+
+ dl->ascent = data.new_ascent;
+ dl->descent = data.new_descent;
+
+ data.db->start_pos = dl->bounds.left_in;
+ data.db->end_pos = data.pixpos;
+
+ calculate_yoffset (dl, data.db);
+
+ return data.pixpos - dl->bounds.left_in;
+}
+
+/* Add a type of glyph to a margin display block. */
+
+static int
+add_margin_runes (struct display_line *dl, struct display_block *db, int start,
+ int count, enum glyph_layout layout, int side, Lisp_Object window)
+{
+ glyph_block_dynarr *gbd = (side == LEFT_GLYPHS
+ ? dl->left_glyphs
+ : dl->right_glyphs);
+ int elt, end;
+ int reverse;
+ struct window *w = XWINDOW (window);
+ struct frame *f = XFRAME (w->frame);
+ struct device *d = XDEVICE (f->device);
+ pos_data data;
+
+ xzero (data);
+ data.d = d;
+ data.window = window;
+ data.db = db;
+ data.dl = dl;
+ data.pixpos = start;
+ data.cursor_type = NO_CURSOR;
+ data.cursor_x = -1;
+ data.last_charset = Qunbound;
+ data.last_findex = DEFAULT_INDEX;
+ data.result_str = Qnil;
+ data.string = Qnil;
+ data.new_ascent = dl->ascent;
+ data.new_descent = dl->descent;
+
+ if ((layout == GL_WHITESPACE && side == LEFT_GLYPHS)
+ || (layout == GL_INSIDE_MARGIN && side == RIGHT_GLYPHS))
+ {
+ reverse = 1;
+ elt = Dynarr_length (gbd) - 1;
+ end = 0;
+ }
+ else
+ {
+ reverse = 0;
+ elt = 0;
+ end = Dynarr_length (gbd);
+ }
+
+ while (count && ((!reverse && elt < end) || (reverse && elt >= end)))
+ {
+ struct glyph_block *gb = Dynarr_atp (gbd, elt);
+
+ if (NILP (gb->extent))
+ ABORT (); /* these should have been handled in add_glyph_rune */
+
+ if (gb->active &&
+ ((side == LEFT_GLYPHS &&
+ extent_begin_glyph_layout (XEXTENT (gb->extent)) == layout)
+ || (side == RIGHT_GLYPHS &&
+ extent_end_glyph_layout (XEXTENT (gb->extent)) == layout)))
+ {
+ data.findex = gb->findex;
+ data.max_pixpos = data.pixpos + gb->width;
+ add_glyph_rune (&data, gb, side, 0, NULL);
+ count--;
+ gb->active = 0;
+ }
+
+ (reverse ? elt-- : elt++);
+ }
+
+ calculate_baseline (&data);
+
+ dl->ascent = data.new_ascent;
+ dl->descent = data.new_descent;
+
+ calculate_yoffset (dl, data.db);
+
+ return data.pixpos;
+}
+
+/* Add a blank to a margin display block. */
+
+static void
+add_margin_blank (struct display_line *dl, struct display_block *db,
+ struct window *w, int xpos, int width, int side)
+{
+ struct rune rb;
+
+ rb.findex = (side == LEFT_GLYPHS
+ ? get_builtin_face_cache_index (w, Vleft_margin_face)
+ : get_builtin_face_cache_index (w, Vright_margin_face));
+ rb.xpos = xpos;
+ rb.width = width;
+ rb.bufpos = -1;
+ rb.endpos = 0;
+ rb.type = RUNE_BLANK;
+ rb.cursor_type = CURSOR_OFF;
+
+ Dynarr_add (db->runes, rb);
+}
+
+/* Display glyphs in the left outside margin, left inside margin and
+ left whitespace area. */
+
+static void
+create_left_glyph_block (struct window *w, struct display_line *dl,
+ int overlay_width)
+{
+ Lisp_Object window;
+
+ int use_overflow = (NILP (w->use_left_overflow) ? 0 : 1);
+ int elt, end_xpos;
+ int out_end, in_out_start, in_in_end, white_out_start, white_in_start;
+ int out_cnt, in_out_cnt, in_in_cnt, white_out_cnt, white_in_cnt;
+ int left_in_start = dl->bounds.left_in;
+ int left_in_end = dl->bounds.left_in + overlay_width;
+
+ struct display_block *odb, *idb;
+
+ XSETWINDOW (window, w);
+
+ /* We have to add the glyphs to the line in the order outside,
+ inside, whitespace. However the precedence dictates that we
+ determine how many will fit in the reverse order. */
+
+ /* Determine how many whitespace glyphs we can display and where
+ they should start. */
+ white_in_start = dl->bounds.left_white;
+ white_out_start = left_in_start;
+ white_out_cnt = white_in_cnt = 0;
+ elt = 0;
+
+ while (elt < Dynarr_length (dl->left_glyphs))
+ {
+ struct glyph_block *gb = Dynarr_atp (dl->left_glyphs, elt);
+
+ if (NILP (gb->extent))
+ ABORT (); /* these should have been handled in add_glyph_rune */
+
+ if (extent_begin_glyph_layout (XEXTENT (gb->extent)) == GL_WHITESPACE)
+ {
+ int width;
+
+ width = glyph_width (gb->glyph, window);
+
+ if (white_in_start - width >= left_in_end)
+ {
+ white_in_cnt++;
+ white_in_start -= width;
+ gb->width = width;
+ gb->active = 1;
+ }
+ else if (use_overflow
+ && (white_out_start - width > dl->bounds.left_out))
+ {
+ white_out_cnt++;
+ white_out_start -= width;
+ gb->width = width;
+ gb->active = 1;
+ }
+ else
+ gb->active = 0;
+ }
+
+ elt++;
+ }
+
+ /* Determine how many inside margin glyphs we can display and where
+ they should start. The inside margin glyphs get whatever space
+ is left after the whitespace glyphs have been displayed. These
+ are tricky to calculate since if we decide to use the overflow
+ area we basically have to start over. So for these we build up a
+ list of just the inside margin glyphs and manipulate it to
+ determine the needed info. */
+ {
+ glyph_block_dynarr *ib;
+ int avail_in, avail_out;
+ int done = 0;
+ int marker = 0;
+ int used_in, used_out;
+
+ elt = 0;
+ used_in = used_out = 0;
+ ib = Dynarr_new (glyph_block);
+ while (elt < Dynarr_length (dl->left_glyphs))
+ {
+ struct glyph_block *gb = Dynarr_atp (dl->left_glyphs, elt);
+
+ if (NILP (gb->extent))
+ ABORT (); /* these should have been handled in add_glyph_rune */
+
+ if (extent_begin_glyph_layout (XEXTENT (gb->extent)) ==
+ GL_INSIDE_MARGIN)
+ {
+ gb->width = glyph_width (gb->glyph, window);
+ used_in += gb->width;
+ Dynarr_add (ib, *gb);
+ }
+
+ elt++;
+ }
+
+ if (white_out_cnt)
+ avail_in = 0;
+ else
+ {
+ avail_in = white_in_start - left_in_end;
+ if (avail_in < 0)
+ avail_in = 0;
+ }
+
+ if (!use_overflow)
+ avail_out = 0;
+ else
+ avail_out = white_out_start - dl->bounds.left_out;
+
+ marker = 0;
+ while (!done && marker < Dynarr_length (ib))
+ {
+ int width = Dynarr_atp (ib, marker)->width;
+
+ /* If everything now fits in the available inside margin
+ space, we're done. */
+ if (used_in <= avail_in)
+ done = 1;
+ else
+ {
+ /* Otherwise see if we have room to move a glyph to the
+ outside. */
+ if (used_out + width <= avail_out)
+ {
+ used_out += width;
+ used_in -= width;
+ }
+ else
+ done = 1;
+ }
+
+ if (!done)
+ marker++;
+ }
+
+ /* At this point we now know that everything from marker on goes in
+ the inside margin and everything before it goes in the outside
+ margin. The stuff going into the outside margin is guaranteed
+ to fit, but we may have to trim some stuff from the inside. */
+
+ in_in_end = left_in_end;
+ in_out_start = white_out_start;
+ in_out_cnt = in_in_cnt = 0;
+
+ Dynarr_free (ib);
+ elt = 0;
+ while (elt < Dynarr_length (dl->left_glyphs))
+ {
+ struct glyph_block *gb = Dynarr_atp (dl->left_glyphs, elt);
+
+ if (NILP (gb->extent))
+ ABORT (); /* these should have been handled in add_glyph_rune */
+
+ if (extent_begin_glyph_layout (XEXTENT (gb->extent)) ==
+ GL_INSIDE_MARGIN)
+ {
+ int width = glyph_width (gb->glyph, window);
+
+ if (used_out)
+ {
+ in_out_cnt++;
+ in_out_start -= width;
+ gb->width = width;
+ gb->active = 1;
+ used_out -= width;
+ }
+ else if (in_in_end + width < white_in_start)
+ {
+ in_in_cnt++;
+ in_in_end += width;
+ gb->width = width;
+ gb->active = 1;
+ }
+ else
+ gb->active = 0;
+ }
+
+ elt++;
+ }
+ }
+
+ /* Determine how many outside margin glyphs we can display. They
+ always start at the left outside margin and can only use the
+ outside margin space. */
+ out_end = dl->bounds.left_out;
+ out_cnt = 0;
+ elt = 0;
+
+ while (elt < Dynarr_length (dl->left_glyphs))
+ {
+ struct glyph_block *gb = Dynarr_atp (dl->left_glyphs, elt);
+
+ if (NILP (gb->extent))
+ ABORT (); /* these should have been handled in add_glyph_rune */
+
+ if (extent_begin_glyph_layout (XEXTENT (gb->extent)) ==
+ GL_OUTSIDE_MARGIN)
+ {
+ int width = glyph_width (gb->glyph, window);
+
+ if (out_end + width <= in_out_start)
+ {
+ out_cnt++;
+ out_end += width;
+ gb->width = width;
+ gb->active = 1;
+ }
+ else
+ gb->active = 0;
+ }
+
+ elt++;
+ }
+
+ /* Now that we know where everything goes, we add the glyphs as
+ runes to the appropriate display blocks. */
+ if (out_cnt || in_out_cnt || white_out_cnt)
+ {
+ odb = get_display_block_from_line (dl, LEFT_OUTSIDE_MARGIN);
+ odb->start_pos = dl->bounds.left_out;
+ /* #### We should stop adding a blank to account for the space
+ between the end of the glyphs and the margin and instead set
+ this accordingly. */
+ odb->end_pos = dl->bounds.left_in;
+ Dynarr_reset (odb->runes);
+ }
+ else
+ odb = 0;
+
+ if (in_in_cnt || white_in_cnt)
+ {
+ idb = get_display_block_from_line (dl, LEFT_INSIDE_MARGIN);
+ idb->start_pos = dl->bounds.left_in;
+ /* #### See above comment for odb->end_pos */
+ idb->end_pos = dl->bounds.left_white;
+ Dynarr_reset (idb->runes);
+ }
+ else
+ idb = 0;
+
+ /* First add the outside margin glyphs. */
+ if (out_cnt)
+ end_xpos = add_margin_runes (dl, odb, dl->bounds.left_out, out_cnt,
+ GL_OUTSIDE_MARGIN, LEFT_GLYPHS, window);
+ else
+ end_xpos = dl->bounds.left_out;
+
+ /* There may be blank space between the outside margin glyphs and
+ the inside margin glyphs. If so, add a blank. */
+ if (in_out_cnt && (in_out_start - end_xpos))
+ {
+ add_margin_blank (dl, odb, w, end_xpos, in_out_start - end_xpos,
+ LEFT_GLYPHS);
+ }
+
+ /* Next add the inside margin glyphs which are actually in the
+ outside margin. */
+ if (in_out_cnt)
+ {
+ end_xpos = add_margin_runes (dl, odb, in_out_start, in_out_cnt,
+ GL_INSIDE_MARGIN, LEFT_GLYPHS, window);
+ }
+
+ /* If we didn't add any inside margin glyphs to the outside margin,
+ but are adding whitespace glyphs, then we need to add a blank
+ here. */
+ if (!in_out_cnt && white_out_cnt && (white_out_start - end_xpos))
+ {
+ add_margin_blank (dl, odb, w, end_xpos, white_out_start - end_xpos,
+ LEFT_GLYPHS);
+ }
+
+ /* Next add the whitespace margin glyphs which are actually in the
+ outside margin. */
+ if (white_out_cnt)
+ {
+ end_xpos = add_margin_runes (dl, odb, white_out_start, white_out_cnt,
+ GL_WHITESPACE, LEFT_GLYPHS, window);
+ }
+
+ /* We take care of clearing between the end of the glyphs and the
+ start of the inside margin for lines which have glyphs. */
+ if (odb && (left_in_start - end_xpos))
+ {
+ add_margin_blank (dl, odb, w, end_xpos, left_in_start - end_xpos,
+ LEFT_GLYPHS);
+ }
+
+ /* Next add the inside margin glyphs which are actually in the
+ inside margin. */
+ if (in_in_cnt)
+ {
+ end_xpos = add_margin_runes (dl, idb, left_in_end, in_in_cnt,
+ GL_INSIDE_MARGIN, LEFT_GLYPHS, window);
+ }
+ else
+ end_xpos = left_in_end;
+
+ /* Make sure that the area between the end of the inside margin
+ glyphs and the whitespace glyphs is cleared. */
+ if (idb && (white_in_start - end_xpos > 0))
+ {
+ add_margin_blank (dl, idb, w, end_xpos, white_in_start - end_xpos,
+ LEFT_GLYPHS);
+ }
+
+ /* Next add the whitespace margin glyphs which are actually in the
+ inside margin. */
+ if (white_in_cnt)
+ {
+ add_margin_runes (dl, idb, white_in_start, white_in_cnt, GL_WHITESPACE,
+ LEFT_GLYPHS, window);
+ }
+
+ /* Whitespace glyphs always end right next to the text block so
+ there is nothing we have to make sure is cleared after them. */
+}
+
+/* Display glyphs in the right outside margin, right inside margin and
+ right whitespace area. */
+
+static void
+create_right_glyph_block (struct window *w, struct display_line *dl)
+{
+ Lisp_Object window;
+
+ int use_overflow = (NILP (w->use_right_overflow) ? 0 : 1);
+ int elt, end_xpos;
+ int out_start, in_out_end, in_in_start, white_out_end, white_in_end;
+ int out_cnt, in_out_cnt, in_in_cnt, white_out_cnt, white_in_cnt;
+
+ struct display_block *odb, *idb;
+
+ XSETWINDOW (window, w);
+
+ /* We have to add the glyphs to the line in the order outside,
+ inside, whitespace. However the precedence dictates that we
+ determine how many will fit in the reverse order. */
+
+ /* Determine how many whitespace glyphs we can display and where
+ they should start. */
+ white_in_end = dl->bounds.right_white;
+ white_out_end = dl->bounds.right_in;
+ white_out_cnt = white_in_cnt = 0;
+ elt = 0;
+
+ while (elt < Dynarr_length (dl->right_glyphs))
+ {
+ struct glyph_block *gb = Dynarr_atp (dl->right_glyphs, elt);
+
+ if (NILP (gb->extent))
+ ABORT (); /* these should have been handled in add_glyph_rune */
+
+ if (extent_end_glyph_layout (XEXTENT (gb->extent)) == GL_WHITESPACE)
+ {
+ int width = glyph_width (gb->glyph, window);
+
+ if (white_in_end + width <= dl->bounds.right_in)
+ {
+ white_in_cnt++;
+ white_in_end += width;
+ gb->width = width;
+ gb->active = 1;
+ }
+ else if (use_overflow
+ && (white_out_end + width <= dl->bounds.right_out))
+ {
+ white_out_cnt++;
+ white_out_end += width;
+ gb->width = width;
+ gb->active = 1;
+ }
+ else
+ gb->active = 0;
+ }
+
+ elt++;
+ }
+
+ /* Determine how many inside margin glyphs we can display and where
+ they should start. The inside margin glyphs get whatever space
+ is left after the whitespace glyphs have been displayed. These
+ are tricky to calculate since if we decide to use the overflow
+ area we basically have to start over. So for these we build up a
+ list of just the inside margin glyphs and manipulate it to
+ determine the needed info. */
+ {
+ glyph_block_dynarr *ib;
+ int avail_in, avail_out;
+ int done = 0;
+ int marker = 0;
+ int used_in, used_out;
+
+ elt = 0;
+ used_in = used_out = 0;
+ ib = Dynarr_new (glyph_block);
+ while (elt < Dynarr_length (dl->right_glyphs))
+ {
+ struct glyph_block *gb = Dynarr_atp (dl->right_glyphs, elt);
+
+ if (NILP (gb->extent))
+ ABORT (); /* these should have been handled in add_glyph_rune */
+
+ if (extent_end_glyph_layout (XEXTENT (gb->extent)) == GL_INSIDE_MARGIN)
+ {
+ gb->width = glyph_width (gb->glyph, window);
+ used_in += gb->width;
+ Dynarr_add (ib, *gb);
+ }
+
+ elt++;
+ }
+
+ if (white_out_cnt)
+ avail_in = 0;
+ else
+ avail_in = dl->bounds.right_in - white_in_end;
+
+ if (!use_overflow)
+ avail_out = 0;
+ else
+ avail_out = dl->bounds.right_out - white_out_end;
+
+ marker = 0;
+ while (!done && marker < Dynarr_length (ib))
+ {
+ int width = Dynarr_atp (ib, marker)->width;
+
+ /* If everything now fits in the available inside margin
+ space, we're done. */
+ if (used_in <= avail_in)
+ done = 1;
+ else
+ {
+ /* Otherwise see if we have room to move a glyph to the
+ outside. */
+ if (used_out + width <= avail_out)
+ {
+ used_out += width;
+ used_in -= width;
+ }
+ else
+ done = 1;
+ }
+
+ if (!done)
+ marker++;
+ }
+
+ /* At this point we now know that everything from marker on goes in
+ the inside margin and everything before it goes in the outside
+ margin. The stuff going into the outside margin is guaranteed
+ to fit, but we may have to trim some stuff from the inside. */
+
+ in_in_start = dl->bounds.right_in;
+ in_out_end = dl->bounds.right_in;
+ in_out_cnt = in_in_cnt = 0;
+
+ Dynarr_free (ib);
+ elt = 0;
+ while (elt < Dynarr_length (dl->right_glyphs))
+ {
+ struct glyph_block *gb = Dynarr_atp (dl->right_glyphs, elt);
+
+ if (NILP (gb->extent))
+ ABORT (); /* these should have been handled in add_glyph_rune */
+
+ if (extent_end_glyph_layout (XEXTENT (gb->extent)) == GL_INSIDE_MARGIN)
+ {
+ int width = glyph_width (gb->glyph, window);
+
+ if (used_out)
+ {
+ in_out_cnt++;
+ in_out_end += width;
+ gb->width = width;
+ gb->active = 1;
+ used_out -= width;
+ }
+ else if (in_in_start - width >= white_in_end)
+ {
+ in_in_cnt++;
+ in_in_start -= width;
+ gb->width = width;
+ gb->active = 1;
+ }
+ else
+ gb->active = 0;
+ }
+
+ elt++;
+ }
+ }
+
+ /* Determine how many outside margin glyphs we can display. They
+ always start at the right outside margin and can only use the
+ outside margin space. */
+ out_start = dl->bounds.right_out;
+ out_cnt = 0;
+ elt = 0;
+
+ while (elt < Dynarr_length (dl->right_glyphs))
+ {
+ struct glyph_block *gb = Dynarr_atp (dl->right_glyphs, elt);
+
+ if (NILP (gb->extent))
+ ABORT (); /* these should have been handled in add_glyph_rune */
+
+ if (extent_end_glyph_layout (XEXTENT (gb->extent)) == GL_OUTSIDE_MARGIN)
+ {
+ int width = glyph_width (gb->glyph, window);
+
+ if (out_start - width >= in_out_end)
+ {
+ out_cnt++;
+ out_start -= width;
+ gb->width = width;
+ gb->active = 1;
+ }
+ else
+ gb->active = 0;
+ }
+
+ elt++;
+ }
+
+ /* Now that we now where everything goes, we add the glyphs as runes
+ to the appropriate display blocks. */
+ if (out_cnt || in_out_cnt || white_out_cnt)
+ {
+ odb = get_display_block_from_line (dl, RIGHT_OUTSIDE_MARGIN);
+ /* #### See comments before odb->start_pos init in
+ create_left_glyph_block */
+ odb->start_pos = dl->bounds.right_in;
+ odb->end_pos = dl->bounds.right_out;
+ Dynarr_reset (odb->runes);
+ }
+ else
+ odb = 0;
+
+ if (in_in_cnt || white_in_cnt)
+ {
+ idb = get_display_block_from_line (dl, RIGHT_INSIDE_MARGIN);
+ idb->start_pos = dl->bounds.right_white;
+ /* #### See comments before odb->start_pos init in
+ create_left_glyph_block */
+ idb->end_pos = dl->bounds.right_in;
+ Dynarr_reset (idb->runes);
+ }
+ else
+ idb = 0;
+
+ /* First add the whitespace margin glyphs which are actually in the
+ inside margin. */
+ if (white_in_cnt)
+ {
+ end_xpos = add_margin_runes (dl, idb, dl->bounds.right_white,
+ white_in_cnt, GL_WHITESPACE, RIGHT_GLYPHS,
+ window);
+ }
+ else
+ end_xpos = dl->bounds.right_white;
+
+ /* Make sure that the area between the end of the whitespace glyphs
+ and the inside margin glyphs is cleared. */
+ if (in_in_cnt && (in_in_start - end_xpos))
+ {
+ add_margin_blank (dl, idb, w, end_xpos, in_in_start - end_xpos,
+ RIGHT_GLYPHS);
+ }
+
+ /* Next add the inside margin glyphs which are actually in the
+ inside margin. */
+ if (in_in_cnt)
+ {
+ end_xpos = add_margin_runes (dl, idb, in_in_start, in_in_cnt,
+ GL_INSIDE_MARGIN, RIGHT_GLYPHS, window);
+ }
+
+ /* If we didn't add any inside margin glyphs then make sure the rest
+ of the inside margin area gets cleared. */
+ if (idb && (dl->bounds.right_in - end_xpos))
+ {
+ add_margin_blank (dl, idb, w, end_xpos, dl->bounds.right_in - end_xpos,
+ RIGHT_GLYPHS);
+ }
+
+ /* Next add any whitespace glyphs in the outside margin. */
+ if (white_out_cnt)
+ {
+ end_xpos = add_margin_runes (dl, odb, dl->bounds.right_in, white_out_cnt,
+ GL_WHITESPACE, RIGHT_GLYPHS, window);
+ }
+ else
+ end_xpos = dl->bounds.right_in;
+
+ /* Next add any inside margin glyphs in the outside margin. */
+ if (in_out_cnt)
+ {
+ end_xpos = add_margin_runes (dl, odb, end_xpos, in_out_cnt,
+ GL_INSIDE_MARGIN, RIGHT_GLYPHS, window);
+ }
+
+ /* There may be space between any whitespace or inside margin glyphs
+ in the outside margin and the actual outside margin glyphs. */
+ if (odb && (out_start - end_xpos))
+ {
+ add_margin_blank (dl, odb, w, end_xpos, out_start - end_xpos,
+ RIGHT_GLYPHS);
+ }
+
+ /* Finally, add the outside margin glyphs. */
+ if (out_cnt)
+ {
+ add_margin_runes (dl, odb, out_start, out_cnt, GL_OUTSIDE_MARGIN,
+ RIGHT_GLYPHS, window);
+ }
+}
+
+\f
+/***************************************************************************/
+/* */
+/* modeline routines */
+/* */
+/***************************************************************************/
+
+/* This function is also used in frame.c by `generate_title_string' */
+void
+generate_formatted_string_db (Lisp_Object format_str, Lisp_Object result_str,
+ struct window *w, struct display_line *dl,
+ struct display_block *db, face_index findex,
+ int min_pixpos, int max_pixpos, int type)
+{
+ struct frame *f = XFRAME (w->frame);
+ struct device *d = XDEVICE (f->device);
+
+ pos_data data;
+ int c_pixpos;
+ Charcount offset = 0;
+
+ xzero (data);
+ data.d = d;
+ data.db = db;
+ data.dl = dl;
+ data.findex = findex;
+ data.pixpos = min_pixpos;
+ data.max_pixpos = max_pixpos;
+ data.cursor_type = NO_CURSOR;
+ data.last_charset = Qunbound;
+ data.last_findex = DEFAULT_INDEX;
+ data.result_str = result_str;
+ data.is_modeline = 1;
+ data.string = Qnil;
+ XSETWINDOW (data.window, w);
+
+ Dynarr_reset (formatted_string_extent_dynarr);
+ Dynarr_reset (formatted_string_extent_start_dynarr);
+ Dynarr_reset (formatted_string_extent_end_dynarr);
+
+ /* result_str is nil when we're building a frame or icon title. Otherwise,
+ we're building a modeline, so the offset starts at the modeline
+ horizontal scrolling amount */
+ if (! NILP (result_str))
+ offset = w->modeline_hscroll;
+ generate_fstring_runes (w, &data, 0, 0, -1, format_str, 0,
+ max_pixpos - min_pixpos, findex, type, &offset,
+ Qnil);
+
+ if (Dynarr_length (db->runes))
+ {
+ struct rune *rb =
+ Dynarr_atp (db->runes, Dynarr_length (db->runes) - 1);
+ c_pixpos = rb->xpos + rb->width;
+ }
+ else
+ c_pixpos = min_pixpos;
+
+ /* If we don't reach the right side of the window, add a blank rune
+ to make up the difference. This usually only occurs if the
+ modeline face is using a proportional width font or a fixed width
+ font of a different size from the default face font. */
+
+ if (c_pixpos < max_pixpos)
+ {
+ data.pixpos = c_pixpos;
+ data.blank_width = max_pixpos - data.pixpos;
+
+ add_blank_rune (&data, NULL, 0);
+ }
+
+ /* Now create the result string and frob the extents into it. */
+ if (!NILP (result_str))
+ {
+ int elt;
+ Bytecount len;
+ Bufbyte *strdata;
+ struct buffer *buf = XBUFFER (WINDOW_BUFFER (w));
+
+ in_modeline_generation = 1;
+
+ detach_all_extents (result_str);
+ resize_string (XSTRING (result_str), -1,
+ data.bytepos - XSTRING_LENGTH (result_str));
+
+ strdata = XSTRING_DATA (result_str);
+
+ for (elt = 0, len = 0; elt < Dynarr_length (db->runes); elt++)
+ {
+ if (Dynarr_atp (db->runes, elt)->type == RUNE_CHAR)
+ {
+ len += (set_charptr_emchar
+ (strdata + len, Dynarr_atp (db->runes,
+ elt)->object.chr.ch));
+ }
+ }
+
+ for (elt = 0; elt < Dynarr_length (formatted_string_extent_dynarr);
+ elt++)
+ {
+ Lisp_Object extent = Qnil;
+ Lisp_Object child;
+
+ XSETEXTENT (extent, Dynarr_at (formatted_string_extent_dynarr, elt));
+ child = Fgethash (extent, buf->modeline_extent_table, Qnil);
+ if (NILP (child))
+ {
+ child = Fmake_extent (Qnil, Qnil, result_str);
+ Fputhash (extent, child, buf->modeline_extent_table);
+ }
+ Fset_extent_parent (child, extent);
+ set_extent_endpoints
+ (XEXTENT (child),
+ Dynarr_at (formatted_string_extent_start_dynarr, elt),
+ Dynarr_at (formatted_string_extent_end_dynarr, elt),
+ result_str);
+ }
+
+ in_modeline_generation = 0;
+ }
+}
+
+/* Ensure that the given display line DL accurately represents the
+ modeline for the given window. */
+static void
+generate_modeline (struct window *w, struct display_line *dl, int type)
+{
+ struct buffer *b = XBUFFER (w->buffer);
+ struct frame *f = XFRAME (w->frame);
+ struct device *d = XDEVICE (f->device);
+
+ /* Unlike display line and rune pointers, this one can't change underneath
+ our feet. */
+ struct display_block *db = get_display_block_from_line (dl, TEXT);
+ int max_pixpos, min_pixpos, ypos_adj;
+ Lisp_Object font_inst;
+
+ /* This will actually determine incorrect inside boundaries for the
+ modeline since it ignores the margins. However being aware of this fact
+ we never use those values anywhere so it doesn't matter. */
+ dl->bounds = calculate_display_line_boundaries (w, 1);
+
+ /* We are generating a modeline. */
+ dl->modeline = 1;
+ dl->cursor_elt = -1;
+
+ /* Reset the runes on the modeline. */
+ Dynarr_reset (db->runes);
+
+ if (!WINDOW_HAS_MODELINE_P (w))
+ {
+ struct rune rb;
+
+ /* If there is a horizontal scrollbar, don't add anything. */
+ if (window_scrollbar_height (w))
+ return;
+
+ dl->ascent = DEVMETH (d, divider_height, ());
+ dl->descent = 0;
+ /* The modeline is at the bottom of the gutters. */
+ dl->ypos = WINDOW_BOTTOM (w);
+
+ rb.findex = MODELINE_INDEX;
+ rb.xpos = dl->bounds.left_out;
+ rb.width = dl->bounds.right_out - dl->bounds.left_out;
+ rb.bufpos = 0;
+ rb.endpos = 0;
+ rb.type = RUNE_HLINE;
+ rb.object.hline.thickness = 1;
+ rb.object.hline.yoffset = 0;
+ rb.cursor_type = NO_CURSOR;
+
+ if (!EQ (Qzero, w->modeline_shadow_thickness)
+ && FRAME_WIN_P (f))
+ {
+ int shadow_thickness = MODELINE_SHADOW_THICKNESS (w);
+
+ dl->ypos -= shadow_thickness;
+ rb.xpos += shadow_thickness;
+ rb.width -= 2 * shadow_thickness;
+ }
+
+ Dynarr_add (db->runes, rb);
+ return;
+ }
+
+ /* !!#### not right; needs to compute the max height of
+ all the charsets */
+ font_inst = WINDOW_FACE_CACHEL_FONT (w, MODELINE_INDEX, Vcharset_ascii);
+
+ dl->ascent = XFONT_INSTANCE (font_inst)->ascent;
+ dl->descent = XFONT_INSTANCE (font_inst)->descent;
+
+ min_pixpos = dl->bounds.left_out;
+ max_pixpos = dl->bounds.right_out;
+
+ if (!EQ (Qzero, w->modeline_shadow_thickness) && FRAME_WIN_P (f))
+ {
+ int shadow_thickness = MODELINE_SHADOW_THICKNESS (w);
+
+ ypos_adj = shadow_thickness;
+ min_pixpos += shadow_thickness;
+ max_pixpos -= shadow_thickness;
+ }
+ else
+ ypos_adj = 0;
+
+ generate_formatted_string_db (b->modeline_format,
+ b->generated_modeline_string, w, dl, db,
+ MODELINE_INDEX, min_pixpos, max_pixpos, type);
+
+ /* The modeline is at the bottom of the gutters. We have to wait to
+ set this until we've generated the modeline in order to account
+ for any embedded faces. */
+ dl->ypos = WINDOW_BOTTOM (w) - dl->descent - ypos_adj;
+}
+
+static Charcount
+add_string_to_fstring_db_runes (pos_data *data, const Bufbyte *str,
+ Charcount pos, Charcount min_pos, Charcount max_pos)
+{
+ /* This function has been Mule-ized. */
+ Charcount end;
+ const Bufbyte *cur_pos = str;
+ struct display_block *db = data->db;
+
+ data->blank_width = space_width (XWINDOW (data->window));
+ while (Dynarr_length (db->runes) < pos)
+ add_blank_rune (data, NULL, 0);
+
+ end = (Dynarr_length (db->runes) +
+ bytecount_to_charcount (str, strlen ((const char *) str)));
+ if (max_pos != -1)
+ end = min (max_pos, end);
+
+ while (pos < end && *cur_pos)
+ {
+ const Bufbyte *old_cur_pos = cur_pos;
+ int succeeded;
+
+ data->ch = charptr_emchar (cur_pos);
+ succeeded = (add_emchar_rune (data) != ADD_FAILED);
+ INC_CHARPTR (cur_pos);
+ if (succeeded)
+ {
+ pos++;
+ data->modeline_charpos++;
+ data->bytepos += cur_pos - old_cur_pos;
+ }
+ }
+
+ while (Dynarr_length (db->runes) < min_pos &&
+ (data->pixpos + data->blank_width <= data->max_pixpos))
+ add_blank_rune (data, NULL, 0);
+
+ return Dynarr_length (db->runes);
+}
+
+/* #### Urk! Should also handle begin-glyphs and end-glyphs in
+ modeline extents. */
+static Charcount
+add_glyph_to_fstring_db_runes (pos_data *data, Lisp_Object glyph,
+ Charcount pos, Charcount min_pos,
+ Charcount max_pos, Lisp_Object extent)
+{
+ /* This function has been Mule-ized. */
+ Charcount end;
+ struct display_block *db = data->db;
+ struct glyph_block gb;
+
+ data->blank_width = space_width (XWINDOW (data->window));
+ while (Dynarr_length (db->runes) < pos)
+ add_blank_rune (data, NULL, 0);
+
+ end = Dynarr_length (db->runes) + 1;
+ if (max_pos != -1)
+ end = min (max_pos, end);
+
+ gb.glyph = glyph;
+ gb.extent = extent;
+ add_glyph_rune (data, &gb, BEGIN_GLYPHS, 0, 0);
+ pos++;
+
+ while (Dynarr_length (db->runes) < pos &&
+ (data->pixpos + data->blank_width <= data->max_pixpos))
+ add_blank_rune (data, NULL, 0);
+
+ return Dynarr_length (db->runes);
+}
+
+/* If max_pos is == -1, it is considered to be infinite. The same is
+ true of max_pixsize. */
+#define SET_CURRENT_MODE_CHARS_PIXSIZE \
+ if (Dynarr_length (data->db->runes)) \
+ cur_pixsize = data->pixpos - Dynarr_atp (data->db->runes, 0)->xpos; \
+ else \
+ cur_pixsize = 0;
+
+/* Note that this function does "positions" in terms of characters and
+ not in terms of columns. This is necessary to make the formatting
+ work correctly when proportional width fonts are used in the
+ modeline. */
+static Charcount
+generate_fstring_runes (struct window *w, pos_data *data, Charcount pos,
+ Charcount min_pos, Charcount max_pos,
+ Lisp_Object elt, int depth, int max_pixsize,
+ face_index findex, int type, Charcount *offset,
+ Lisp_Object cur_ext)
+{
+ /* This function has been Mule-ized. */
+ /* #### The other losing things in this function are:
+
+ -- C zero-terminated-string lossage.
+ -- Non-printable characters should be converted into something
+ appropriate (e.g. ^F) instead of blindly being printed anyway.
+ */
+
+tail_recurse:
+ if (depth > 10)
+ goto invalid;
+
+ depth++;
+
+ if (STRINGP (elt))
+ {
+ /* A string. Add to the display line and check for %-constructs
+ within it. */
+
+ Bufbyte *this = XSTRING_DATA (elt);
+
+ while ((pos < max_pos || max_pos == -1) && *this)
+ {
+ Bufbyte *last = this;
+
+ while (*this && *this != '%')
+ this++;
+
+ if (this != last)
+ {
+ /* No %-construct */
+ Charcount size =
+ bytecount_to_charcount (last, this - last);
+
+ if (size <= *offset)
+ *offset -= size;
+ else
+ {
+ Charcount tmp_max = (max_pos == -1 ? pos + size - *offset :
+ min (pos + size - *offset, max_pos));
+ const Bufbyte *tmp_last = charptr_n_addr (last, *offset);
+
+ pos = add_string_to_fstring_db_runes (data, tmp_last,
+ pos, pos, tmp_max);
+ *offset = 0;
+ }
+ }
+ else /* *this == '%' */
+ {
+ Charcount spec_width = 0;
+
+ this++; /* skip over '%' */
+
+ /* We can't allow -ve args due to the "%-" construct.
+ * Argument specifies minwidth but not maxwidth
+ * (maxwidth can be specified by
+ * (<negative-number> . <stuff>) modeline elements)
+ */
+ while (isdigit (*this))
+ {
+ spec_width = spec_width * 10 + (*this - '0');
+ this++;
+ }
+ spec_width += pos;
+
+ if (*this == 'M')
+ {
+ pos = generate_fstring_runes (w, data, pos, spec_width,
+ max_pos, Vglobal_mode_string,
+ depth, max_pixsize, findex,
+ type, offset, cur_ext);
+ }
+ else if (*this == '-')
+ {
+ Charcount num_to_add;
+
+ if (max_pixsize < 0)
+ num_to_add = 0;
+ else if (max_pos != -1)
+ num_to_add = max_pos - pos;
+ else
+ {
+ int cur_pixsize;
+ int dash_pixsize;
+ Bufbyte ch = '-';
+ SET_CURRENT_MODE_CHARS_PIXSIZE;
+
+ dash_pixsize =
+ redisplay_text_width_string (w, findex, &ch, Qnil, 0,
+ 1);
+
+ num_to_add = (max_pixsize - cur_pixsize) / dash_pixsize;
+ num_to_add++;
+ }
+
+ while (num_to_add--)
+ pos = add_string_to_fstring_db_runes
+ (data, (const Bufbyte *) "-", pos, pos, max_pos);
+ }
+ else if (*this != 0)
+ {
+ Emchar ch = charptr_emchar (this);
+ Bufbyte *str;
+ Charcount size;
+
+ decode_mode_spec (w, ch, type);
+
+ str = Dynarr_atp (mode_spec_bufbyte_string, 0);
+ size = bytecount_to_charcount
+ /* Skip the null character added by `decode_mode_spec' */
+ (str, Dynarr_length (mode_spec_bufbyte_string)) - 1;
+
+ if (size <= *offset)
+ *offset -= size;
+ else
+ {
+ const Bufbyte *tmp_str = charptr_n_addr (str, *offset);
+
+ /* #### NOTE: I don't understand why a tmp_max is not
+ computed and used here as in the plain string case
+ above. -- dv */
+ pos = add_string_to_fstring_db_runes (data, tmp_str,
+ pos, pos,
+ max_pos);
+ *offset = 0;
+ }
+ }
+
+ /* NOT this++. There could be any sort of character at
+ the current position. */
+ INC_CHARPTR (this);
+ }
+
+ if (max_pixsize > 0)
+ {
+ int cur_pixsize;
+ SET_CURRENT_MODE_CHARS_PIXSIZE;
+
+ if (cur_pixsize >= max_pixsize)
+ break;
+ }
+ }
+ }
+ else if (SYMBOLP (elt))
+ {
+ /* A symbol: process the value of the symbol recursively
+ as if it appeared here directly. */
+ Lisp_Object tem = symbol_value_in_buffer (elt, w->buffer);
+
+ if (!UNBOUNDP (tem))
+ {
+ /* If value is a string, output that string literally:
+ don't check for % within it. */
+ if (STRINGP (tem))
+ {
+ Bufbyte *str = XSTRING_DATA (tem);
+ Charcount size = XSTRING_CHAR_LENGTH (tem);
+
+ if (size <= *offset)
+ *offset -= size;
+ else
+ {
+ const Bufbyte *tmp_str = charptr_n_addr (str, *offset);
+
+ /* #### NOTE: I don't understand why a tmp_max is not
+ computed and used here as in the plain string case
+ above. -- dv */
+ pos = add_string_to_fstring_db_runes (data, tmp_str, pos,
+ min_pos, max_pos);
+ *offset = 0;
+ }
+ }
+ /* Give up right away for nil or t. */
+ else if (!EQ (tem, elt))
+ {
+ elt = tem;
+ goto tail_recurse;
+ }
+ }
+ }
+ else if (GENERIC_SPECIFIERP (elt))
+ {
+ Lisp_Object window, tem;
+ XSETWINDOW (window, w);
+ tem = specifier_instance_no_quit (elt, Qunbound, window,
+ ERROR_ME_NOT, 0, Qzero);
+ if (!UNBOUNDP (tem))
+ {
+ elt = tem;
+ goto tail_recurse;
+ }
+ }
+ else if (CONSP (elt))
+ {
+ /* A cons cell: four distinct cases.
+ * - If first element is a string or a cons, process all the elements
+ * and effectively concatenate them.
+ * - If first element is a negative number, truncate displaying cdr to
+ * at most that many characters. If positive, pad (with spaces)
+ * to at least that many characters.
+ * - If first element is another symbol, process the cadr or caddr
+ * recursively according to whether the symbol's value is non-nil or
+ * nil.
+ * - If first element is an extent, process the cdr recursively
+ * and handle the extent's face.
+ */
+
+ Lisp_Object car, tem;
+
+ car = XCAR (elt);
+ if (SYMBOLP (car))
+ {
+ elt = XCDR (elt);
+ if (!CONSP (elt))
+ goto invalid;
+
+ tem = symbol_value_in_buffer (car, w->buffer);
+ /* elt is now the cdr, and we know it is a cons cell.
+ Use its car if CAR has a non-nil value. */
+ if (!UNBOUNDP (tem))
+ {
+ if (!NILP (tem))
+ {
+ elt = XCAR (elt);
+ goto tail_recurse;
+ }
+ }
+ /* Symbol's value is nil (or symbol is unbound)
+ * Get the cddr of the original list
+ * and if possible find the caddr and use that.
+ */
+ elt = XCDR (elt);
+ if (NILP (elt))
+ ;
+ else if (!CONSP (elt))
+ goto invalid;
+ else
+ {
+ elt = XCAR (elt);
+ goto tail_recurse;
+ }
+ }
+ else if (INTP (car))
+ {
+ Charcount lim = XINT (car);
+
+ elt = XCDR (elt);
+
+ if (lim < 0)
+ {
+ /* Negative int means reduce maximum width.
+ * DO NOT change MIN_PIXPOS here!
+ * (20 -10 . foo) should truncate foo to 10 col
+ * and then pad to 20.
+ */
+ if (max_pos == -1)
+ max_pos = pos - lim;
+ else
+ max_pos = min (max_pos, pos - lim);
+ }
+ else if (lim > 0)
+ {
+ /* Padding specified. Don't let it be more than
+ * current maximum.
+ */
+ lim += pos;
+ if (max_pos != -1 && lim > max_pos)
+ lim = max_pos;
+ /* If that's more padding than already wanted, queue it.
+ * But don't reduce padding already specified even if
+ * that is beyond the current truncation point.
+ */
+ if (lim > min_pos)
+ min_pos = lim;
+ }
+ goto tail_recurse;
+ }
+ else if (STRINGP (car) || CONSP (car))
+ {
+ int limit = 50;
+
+ /* LIMIT is to protect against circular lists. */
+ while (CONSP (elt) && --limit > 0
+ && (pos < max_pos || max_pos == -1))
+ {
+ pos = generate_fstring_runes (w, data, pos, pos, max_pos,
+ XCAR (elt), depth, max_pixsize,
+ findex, type, offset, cur_ext);
+ elt = XCDR (elt);
+ }
+ }
+ else if (EXTENTP (car))
+ {
+ struct extent *ext = XEXTENT (car);
+
+ if (EXTENT_LIVE_P (ext))
+ {
+ face_index old_findex = data->findex;
+ Lisp_Object face;
+ Lisp_Object font_inst;
+ face_index new_findex;
+ Bytecount start = data->bytepos;
+
+ face = extent_face (ext);
+ if (FACEP (face))
+ {
+ /* #### needs to merge faces, sigh */
+ /* #### needs to handle list of faces */
+ new_findex = get_builtin_face_cache_index (w, face);
+ /* !!#### not right; needs to compute the max height of
+ all the charsets */
+ font_inst = WINDOW_FACE_CACHEL_FONT (w, new_findex,
+ Vcharset_ascii);
+
+ data->dl->ascent = max (data->dl->ascent,
+ XFONT_INSTANCE (font_inst)->ascent);
+ data->dl->descent = max (data->dl->descent,
+ XFONT_INSTANCE (font_inst)->
+ descent);
+ }
+ else
+ new_findex = old_findex;
+
+ data->findex = new_findex;
+ pos = generate_fstring_runes (w, data, pos, pos, max_pos,
+ XCDR (elt), depth - 1,
+ max_pixsize, new_findex, type,
+ offset, car);
+ data->findex = old_findex;
+ Dynarr_add (formatted_string_extent_dynarr, ext);
+ Dynarr_add (formatted_string_extent_start_dynarr, start);
+ Dynarr_add (formatted_string_extent_end_dynarr, data->bytepos);
+ }
+ }
+ }
+ else if (GLYPHP (elt))
+ {
+ /* Glyphs are considered as one character with respect to the modeline
+ horizontal scrolling facility. -- dv */
+ if (*offset > 0)
+ *offset -= 1;
+ else
+ pos = add_glyph_to_fstring_db_runes (data, elt, pos, pos, max_pos,
+ cur_ext);
+ }
+ else
+ {
+ invalid:
+ {
+ char *str = GETTEXT ("*invalid*");
+ Charcount size = (Charcount) strlen (str); /* is this ok ?? -- dv */
+
+ if (size <= *offset)
+ *offset -= size;
+ else
+ {
+ const Bufbyte *tmp_str =
+ charptr_n_addr ((const Bufbyte *) str, *offset);
+
+ /* #### NOTE: I don't understand why a tmp_max is not computed and
+ used here as in the plain string case above. -- dv */
+ pos = add_string_to_fstring_db_runes (data, tmp_str, pos,
+ min_pos, max_pos);
+ *offset = 0;
+ }
+ }
+ }
+
+ if (min_pos > pos)
+ {
+ add_string_to_fstring_db_runes (data, (const Bufbyte *) "", pos,
+ min_pos, -1);
+ }
+
+ return pos;
+}
+
+/* Update just the modeline. Assumes the desired display structs. If
+ they do not have a modeline block, it does nothing. */
+static void
+regenerate_modeline (struct window *w)
+{
+ display_line_dynarr *dla = window_display_lines (w, DESIRED_DISP);
+
+ if (!Dynarr_length (dla) || !Dynarr_atp (dla, 0)->modeline)
+ return;
+ else
+ {
+ generate_modeline (w, Dynarr_atp (dla, 0), DESIRED_DISP);
+ redisplay_update_line (w, 0, 0, 0);
+ }
+}
+
+/* Make sure that modeline display line is present in the given
+ display structs if the window has a modeline and update that
+ line. Returns true if a modeline was needed. */
+static int
+ensure_modeline_generated (struct window *w, int type)
+{
+ int need_modeline;
+
+ /* minibuffer windows don't have modelines */
+ if (MINI_WINDOW_P (w))
+ need_modeline = 0;
+ /* windows which haven't had it turned off do */
+ else if (WINDOW_HAS_MODELINE_P (w))
+ need_modeline = 1;
+ /* windows which have it turned off don't have a divider if there is
+ a horizontal scrollbar */
+ else if (window_scrollbar_height (w))
+ need_modeline = 0;
+ /* and in this case there is none */
+ else
+ need_modeline = 1;
+
+ if (need_modeline)
+ {
+ display_line_dynarr *dla;
+
+ dla = window_display_lines (w, type);
+
+ /* We don't care if there is a display line which is not
+ currently a modeline because it is definitely going to become
+ one if we have gotten to this point. */
+ if (Dynarr_length (dla) == 0)
+ {
+ if (Dynarr_largest (dla) > 0)
+ {
+ struct display_line *mlp = Dynarr_atp (dla, 0);
+ Dynarr_add (dla, *mlp);
+ }
+ else
+ {
+ struct display_line modeline;
+ xzero (modeline);
+ Dynarr_add (dla, modeline);
+ }
+ }
+
+ /* If we're adding a new place marker go ahead and generate the
+ modeline so that it is available for use by
+ window_modeline_height. */
+ generate_modeline (w, Dynarr_atp (dla, 0), type);
+ }
+
+ return need_modeline;
+}
+
+/* #### Kludge or not a kludge. I tend towards the former. */
+int
+real_current_modeline_height (struct window *w)
+{
+ Fset_marker (w->start[CMOTION_DISP], w->start[CURRENT_DISP], w->buffer);
+ Fset_marker (w->pointm[CMOTION_DISP], w->pointm[CURRENT_DISP], w->buffer);
+
+ if (ensure_modeline_generated (w, CMOTION_DISP))
+ {
+ display_line_dynarr *dla = window_display_lines (w, CMOTION_DISP);
+
+ if (Dynarr_length (dla))
+ {
+ if (Dynarr_atp (dla, 0)->modeline)
+ return (Dynarr_atp (dla, 0)->ascent +
+ Dynarr_atp (dla, 0)->descent);
+ }
+ }
+ return 0;
+}
+
+\f
+/***************************************************************************/
+/* */
+/* displayable string routines */
+/* */
+/***************************************************************************/
+
+/* Given a position for a string in a window, ensure that the given
+ display line DL accurately represents the text on a line starting
+ at the given position.
+
+ Yes, this is duplicating the code of create_text_block, but it
+ looked just too hard to change create_text_block to handle strings
+ *and* buffers. We already make a distinction between the two
+ elsewhere in the code so I think unifying them would require a
+ complete MULE rewrite. Besides, the other distinction is that these
+ functions cover text that the user *cannot edit* so we can remove
+ everything to do with cursors, minibuffers etc. Eventually the
+ modeline routines should be modified to use this code as it copes
+ with many more types of display situation. */
+
+static Bufpos
+create_string_text_block (struct window *w, Lisp_Object disp_string,
+ struct display_line *dl,
+ Bufpos start_pos,
+ prop_block_dynarr **prop,
+ face_index default_face)
+{
+ struct frame *f = XFRAME (w->frame);
+ /* Note that a lot of the buffer controlled stuff has been left in
+ because you might well want to make use of it (selective display
+ etc), its just the buffer text that we do not use. However, it
+ seems to be possible for buffer to be nil sometimes so protect
+ against this case. */
+ struct buffer *b = BUFFERP (w->buffer) ? XBUFFER (w->buffer) : 0;
+ struct device *d = XDEVICE (f->device);
+ Lisp_String* s = XSTRING (disp_string);
+
+ /* we're working with these a lot so precalculate them */
+ Bytecount slen = XSTRING_LENGTH (disp_string);
+ Bytecount bi_string_zv = slen;
+ Bytind bi_start_pos = charcount_to_bytecount (string_data (s), start_pos);
+
+ pos_data data;
+
+ int truncate_win = b ? window_truncation_on (w) : 0;
+
+ /* We're going to ditch selective display for static text, it's an
+ FSF thing and invisible extents are the way to go here.
+ Implementing it also relies on a number of buffer-specific
+ functions that we don't have the luxury of being able to use
+ here. */
+
+ /* The variable ctl-arrow allows the user to specify what characters
+ can actually be displayed and which octal should be used for.
+ #### This variable should probably have some rethought done to
+ it.
+
+ #### It would also be really nice if you could specify that
+ the characters come out in hex instead of in octal. Mule
+ does that by adding a ctl-hexa variable similar to ctl-arrow,
+ but that's bogus -- we need a more general solution. I
+ think you need to extend the concept of display tables
+ into a more general conversion mechanism. Ideally you
+ could specify a Lisp function that converts characters,
+ but this violates the Second Golden Rule and besides would
+ make things way way way way slow.
+
+ So instead, we extend the display-table concept, which was
+ historically limited to 256-byte vectors, to one of the
+ following:
+
+ a) A 256-entry vector, for backward compatibility;
+ b) char-table, mapping characters to values;
+ c) range-table, mapping ranges of characters to values;
+ d) a list of the above.
+
+ The (d) option allows you to specify multiple display tables
+ instead of just one. Each display table can specify conversions
+ for some characters and leave others unchanged. The way the
+ character gets displayed is determined by the first display table
+ with a binding for that character. This way, you could call a
+ function `enable-hex-display' that adds a hex display-table to
+ the list of display tables for the current buffer.
+
+ #### ...not yet implemented... Also, we extend the concept of
+ "mapping" to include a printf-like spec. Thus you can make all
+ extended characters show up as hex with a display table like
+ this:
+
+ #s(range-table data ((256 524288) (format "%x")))
+
+ Since more than one display table is possible, you have
+ great flexibility in mapping ranges of characters. */
+ Emchar printable_min = b ? (CHAR_OR_CHAR_INTP (b->ctl_arrow)
+ ? XCHAR_OR_CHAR_INT (b->ctl_arrow)
+ : ((EQ (b->ctl_arrow, Qt) || EQ (b->ctl_arrow, Qnil))
+ ? 255 : 160)) : 255;
+
+ Lisp_Object face_dt, window_dt;
+
+ /* The text display block for this display line. */
+ struct display_block *db = get_display_block_from_line (dl, TEXT);
+
+ /* The first time through the main loop we need to force the glyph
+ data to be updated. */
+ int initial = 1;
+
+ /* Apparently the new extent_fragment_update returns an end position
+ equal to the position passed in if there are no more runs to be
+ displayed. */
+ int no_more_frags = 0;
+
+ dl->used_prop_data = 0;
+ dl->num_chars = 0;
+ dl->line_continuation = 0;
+
+ /* set up faces to use for clearing areas, used by
+ output_display_line */
+ dl->default_findex = default_face;
+ if (default_face)
+ {
+ dl->left_margin_findex = default_face;
+ dl->right_margin_findex = default_face;
+ }
+ else
+ {
+ dl->left_margin_findex =
+ get_builtin_face_cache_index (w, Vleft_margin_face);
+ dl->right_margin_findex =
+ get_builtin_face_cache_index (w, Vright_margin_face);
+ }
+
+ xzero (data);
+ data.ef = extent_fragment_new (disp_string, f);
+
+ /* These values are used by all of the rune addition routines. We add
+ them to this structure for ease of passing. */
+ data.d = d;
+ XSETWINDOW (data.window, w);
+ data.db = db;
+ data.dl = dl;
+
+ data.bi_bufpos = bi_start_pos;
+ data.pixpos = dl->bounds.left_in;
+ data.last_charset = Qunbound;
+ data.last_findex = default_face;
+ data.result_str = Qnil;
+ data.string = disp_string;
+
+ /* Set the right boundary adjusting it to take into account any end
+ glyph. Save the width of the end glyph for later use. */
+ data.max_pixpos = dl->bounds.right_in;
+ data.max_pixpos -= data.end_glyph_width;
+
+ data.cursor_type = NO_CURSOR;
+ data.cursor_x = -1;
+
+ data.start_col = 0;
+ /* I don't think we want this, string areas should not scroll with
+ the window
+ data.start_col = w->hscroll;
+ data.bi_start_col_enabled = (w->hscroll ? bi_start_pos : 0);
+ */
+ data.bi_start_col_enabled = 0;
+ data.hscroll_glyph_width_adjust = 0;
+
+ /* We regenerate the line from the very beginning. */
+ Dynarr_reset (db->runes);
+
+ /* Why is this less than or equal and not just less than? If the
+ starting position is already equal to the maximum we can't add
+ anything else, right? Wrong. We might still have a newline to
+ add. A newline can use the room allocated for an end glyph since
+ if we add it we know we aren't going to be adding any end
+ glyph. */
+
+ /* #### Chuck -- I think this condition should be while (1).
+ Otherwise if (e.g.) there is one begin-glyph and one end-glyph
+ and the begin-glyph ends exactly at the end of the window, the
+ end-glyph and text might not be displayed. while (1) ensures
+ that the loop terminates only when either (a) there is
+ propagation data or (b) the end-of-line or end-of-buffer is hit.
+
+ #### Also I think you need to ensure that the operation
+ "add begin glyphs; add end glyphs; add text" is atomic and
+ can't get interrupted in the middle. If you run off the end
+ of the line during that operation, then you keep accumulating
+ propagation data until you're done. Otherwise, if the (e.g.)
+ there's a begin glyph at a particular position and attempting
+ to display that glyph results in window-end being hit and
+ propagation data being generated, then the character at that
+ position won't be displayed.
+
+ #### See also the comment after the end of this loop, below.
+ */
+ while (data.pixpos <= data.max_pixpos)
+ {
+ /* #### This check probably should not be necessary. */
+ if (data.bi_bufpos > bi_string_zv)
+ {
+ /* #### urk! More of this lossage! */
+ data.bi_bufpos--;
+ goto done;
+ }
+
+ /* Check for face changes. */
+ if (initial || (!no_more_frags && data.bi_bufpos == data.ef->end))
+ {
+ Lisp_Object last_glyph = Qnil;
+ /* Deal with clipped glyphs that we have already displayed. */
+ if (*prop && Dynarr_atp (*prop, 0)->type == PROP_GLYPH)
+ {
+ last_glyph = Dynarr_atp (*prop, 0)->data.p_glyph.glyph;
+ Dynarr_free (*prop);
+ *prop = 0;
+ }
+ /* Now compute the face and begin/end-glyph information. */
+ data.findex =
+ /* Remember that the extent-fragment routines deal in Bytind's. */
+ extent_fragment_update (w, data.ef, data.bi_bufpos, last_glyph);
+ /* This is somewhat cheesy but the alternative is to
+ propagate default_face into extent_fragment_update. */
+ if (data.findex == DEFAULT_INDEX)
+ data.findex = default_face;
+
+ get_display_tables (w, data.findex, &face_dt, &window_dt);
+
+ if (data.bi_bufpos == data.ef->end)
+ no_more_frags = 1;
+ }
+ initial = 0;
+
+ /* Determine what is next to be displayed. We first handle any
+ glyphs returned by glyphs_at_bufpos. If there are no glyphs to
+ display then we determine what to do based on the character at the
+ current buffer position. */
+
+ /* If the current position is covered by an invisible extent, do
+ nothing (except maybe add some ellipses).
+
+ #### The behavior of begin and end-glyphs at the edge of an
+ invisible extent should be investigated further. This is
+ fairly low priority though. */
+ if (data.ef->invisible)
+ {
+ /* #### Chuck, perhaps you could look at this code? I don't
+ really know what I'm doing. */
+ if (*prop)
+ {
+ Dynarr_free (*prop);
+ *prop = 0;
+ }
+
+ /* The extent fragment code only sets this when we should
+ really display the ellipses. It makes sure the ellipses
+ don't get displayed more than once in a row. */
+ if (data.ef->invisible_ellipses)
+ {
+ struct glyph_block gb;
+
+ data.ef->invisible_ellipses_already_displayed = 1;
+ data.ef->invisible_ellipses = 0;
+ gb.extent = Qnil;
+ gb.glyph = Vinvisible_text_glyph;
+ *prop = add_glyph_rune (&data, &gb, BEGIN_GLYPHS, 0,
+ GLYPH_CACHEL (w, INVIS_GLYPH_INDEX));
+ /* Perhaps they shouldn't propagate if the very next thing
+ is to display a newline (for compatibility with
+ selective-display-ellipses)? Maybe that's too
+ abstruse. */
+ if (*prop)
+ goto done;
+ }
+
+ /* #### What if we're dealing with a display table? */
+ if (data.start_col)
+ data.start_col--;
+
+ if (data.bi_bufpos == bi_string_zv)
+ goto done;
+ else
+ INC_CHARBYTIND (string_data (s), data.bi_bufpos);
+ }
+
+ /* If there is propagation data, then it represents the current
+ buffer position being displayed. Add them and advance the
+ position counter. This might also add the minibuffer
+ prompt. */
+ else if (*prop)
+ {
+ dl->used_prop_data = 1;
+ *prop = add_propagation_runes (prop, &data);
+
+ if (*prop)
+ goto done; /* gee, a really narrow window */
+ else if (data.bi_bufpos == bi_string_zv)
+ goto done;
+ else if (data.bi_bufpos < 0)
+ /* #### urk urk urk! ABORTs are not very fun! Fix this please! */
+ data.bi_bufpos = 0;
+ else
+ INC_CHARBYTIND (string_data (s), data.bi_bufpos);
+ }
+
+ /* If there are end glyphs, add them to the line. These are
+ the end glyphs for the previous run of text. We add them
+ here rather than doing them at the end of handling the
+ previous run so that glyphs at the beginning and end of
+ a line are handled correctly. */
+ else if (Dynarr_length (data.ef->end_glyphs) > 0)
+ {
+ data.ch = string_char (s, data.bi_bufpos);
+ *prop = add_glyph_runes (&data, END_GLYPHS);
+
+ if (*prop) {
+ goto done;
+ }
+ }
+
+ /* If there are begin glyphs, add them to the line. */
+ else if (Dynarr_length (data.ef->begin_glyphs) > 0)
+ {
+ data.ch = string_char (s, data.bi_bufpos);
+ *prop = add_glyph_runes (&data, BEGIN_GLYPHS);
+
+ if (*prop) {
+ goto done;
+ }
+ }
+
+ /* If at end-of-buffer, we've already processed begin and
+ end-glyphs at this point and there's no text to process,
+ so we're done. */
+ else if (data.bi_bufpos == bi_string_zv)
+ goto done;
+
+ else
+ {
+ Lisp_Object entry = Qnil;
+ /* Get the character at the current buffer position. */
+ data.ch = string_char (s, data.bi_bufpos);
+ if (!NILP (face_dt) || !NILP (window_dt))
+ entry = display_table_entry (data.ch, face_dt, window_dt);
+
+ /* If there is a display table entry for it, hand it off to
+ add_disp_table_entry_runes and let it worry about it. */
+ if (!NILP (entry) && !EQ (entry, make_char (data.ch)))
+ {
+ *prop = add_disp_table_entry_runes (&data, entry);
+
+ if (*prop)
+ goto done;
+ }
+
+ /* Check if we have hit a newline character. If so, add a marker
+ to the line and end this loop. */
+ else if (data.ch == '\n')
+ {
+ /* We aren't going to be adding an end glyph so give its
+ space back in order to make sure that the cursor can
+ fit. */
+ data.max_pixpos += data.end_glyph_width;
+ goto done;
+ }
+
+ /* If the current character is considered to be printable, then
+ just add it. */
+ else if (data.ch >= printable_min)
+ {
+ *prop = add_emchar_rune (&data);
+ if (*prop)
+ goto done;
+ }
+
+ /* If the current character is a tab, determine the next tab
+ starting position and add a blank rune which extends from the
+ current pixel position to that starting position. */
+ else if (data.ch == '\t')
+ {
+ int tab_start_pixpos = data.pixpos;
+ int next_tab_start;
+ int char_tab_width;
+ int prop_width = 0;
+
+ if (data.start_col > 1)
+ tab_start_pixpos -= (space_width (w) * (data.start_col - 1));
+
+ next_tab_start =
+ next_tab_position (w, tab_start_pixpos,
+ dl->bounds.left_in +
+ data.hscroll_glyph_width_adjust);
+ if (next_tab_start > data.max_pixpos)
+ {
+ prop_width = next_tab_start - data.max_pixpos;
+ next_tab_start = data.max_pixpos;
+ }
+ data.blank_width = next_tab_start - data.pixpos;
+ char_tab_width =
+ (next_tab_start - tab_start_pixpos) / space_width (w);
+
+ *prop = add_blank_rune (&data, w, char_tab_width);
+
+ /* add_blank_rune is only supposed to be called with
+ sizes guaranteed to fit in the available space. */
+ assert (!(*prop));
+
+ if (prop_width)
+ {
+ struct prop_block pb;
+ *prop = Dynarr_new (prop_block);
+
+ pb.type = PROP_BLANK;
+ pb.data.p_blank.width = prop_width;
+ pb.data.p_blank.findex = data.findex;
+ Dynarr_add (*prop, pb);
+
+ goto done;
+ }
+ }
+
+ /* If character is a control character, pass it off to
+ add_control_char_runes.
+
+ The is_*() routines have undefined results on
+ arguments outside of the range [-1, 255]. (This
+ often bites people who carelessly use `char' instead
+ of `unsigned char'.)
+ */
+ else if (data.ch < 0x100 && iscntrl ((Bufbyte) data.ch))
+ {
+ *prop = add_control_char_runes (&data, b);
+
+ if (*prop)
+ goto done;
+ }
+
+ /* If the character is above the ASCII range and we have not
+ already handled it, then print it as an octal number. */
+ else if (data.ch >= 0200)
+ {
+ *prop = add_octal_runes (&data);
+
+ if (*prop)
+ goto done;
+ }
+
+ /* Assume the current character is considered to be printable,
+ then just add it. */
+ else
+ {
+ *prop = add_emchar_rune (&data);
+ if (*prop)
+ goto done;
+ }
+
+ INC_CHARBYTIND (string_data (s), data.bi_bufpos);
+ }
+ }
+
+ done:
+
+ /* Determine the starting point of the next line if we did not hit the
+ end of the buffer. */
+ if (data.bi_bufpos < bi_string_zv)
+ {
+ /* #### This check is not correct. If the line terminated
+ due to a begin-glyph or end-glyph hitting window-end, then
+ data.ch will not point to the character at data.bi_bufpos. If
+ you make the two changes mentioned at the top of this loop,
+ you should be able to say '(if (*prop))'. That should also
+ make it possible to eliminate the data.bi_bufpos < BI_BUF_ZV (b)
+ check. */
+
+ /* The common case is that the line ended because we hit a newline.
+ In that case, the next character is just the next buffer
+ position. */
+ if (data.ch == '\n')
+ {
+ INC_CHARBYTIND (string_data (s), data.bi_bufpos);
+ }
+
+ /* Otherwise we have a buffer line which cannot fit on one display
+ line. */
+ else
+ {
+ struct glyph_block gb;
+ struct glyph_cachel *cachel;
+
+ /* If the line is to be truncated then we actually have to look
+ for the next newline. We also add the end-of-line glyph which
+ we know will fit because we adjusted the right border before
+ we starting laying out the line. */
+ data.max_pixpos += data.end_glyph_width;
+ data.findex = default_face;
+ gb.extent = Qnil;
+
+ if (truncate_win)
+ {
+ Bytind bi_pos;
+
+ /* Now find the start of the next line. */
+ bi_pos = bi_find_next_emchar_in_string (s, '\n', data.bi_bufpos, 1);
+
+ data.cursor_type = NO_CURSOR;
+ data.bi_bufpos = bi_pos;
+ gb.glyph = Vtruncation_glyph;
+ cachel = GLYPH_CACHEL (w, TRUN_GLYPH_INDEX);
+ }
+ else
+ {
+ /* The cursor can never be on the continuation glyph. */
+ data.cursor_type = NO_CURSOR;
+
+ /* data.bi_bufpos is already at the start of the next line. */
+
+ dl->line_continuation = 1;
+ gb.glyph = Vcontinuation_glyph;
+ cachel = GLYPH_CACHEL (w, CONT_GLYPH_INDEX);
+ }
+
+ if (data.end_glyph_width)
+ add_glyph_rune (&data, &gb, BEGIN_GLYPHS, 0, cachel);
+
+ if (truncate_win && data.bi_bufpos == bi_string_zv)
+ {
+ const Bufbyte* endb = charptr_n_addr (string_data (s), bi_string_zv);
+ DEC_CHARPTR (endb);
+ if (charptr_emchar (endb) != '\n')
+ {
+ /* #### Damn this losing shit. */
+ data.bi_bufpos++;
+ }
+ }
+ }
+ }
+ else if (data.bi_bufpos == bi_string_zv)
+ {
+ /* create_text_block () adds a bogus \n marker here which screws
+ up subwindow display. Since we never have a cursor in the
+ gutter we can safely ignore it. */
+ }
+ /* Calculate left whitespace boundary. */
+ {
+ int elt = 0;
+
+ /* Whitespace past a newline is considered right whitespace. */
+ while (elt < Dynarr_length (db->runes))
+ {
+ struct rune *rb = Dynarr_atp (db->runes, elt);
+
+ if ((rb->type == RUNE_CHAR && rb->object.chr.ch == ' ')
+ || rb->type == RUNE_BLANK)
+ {
+ dl->bounds.left_white += rb->width;
+ elt++;
+ }
+ else
+ elt = Dynarr_length (db->runes);
+ }
+ }
+
+ /* Calculate right whitespace boundary. */
+ {
+ int elt = Dynarr_length (db->runes) - 1;
+ int done = 0;
+
+ while (!done && elt >= 0)
+ {
+ struct rune *rb = Dynarr_atp (db->runes, elt);
+
+ if (!(rb->type == RUNE_CHAR && rb->object.chr.ch < 0x100
+ && isspace (rb->object.chr.ch))
+ && !rb->type == RUNE_BLANK)
+ {
+ dl->bounds.right_white = rb->xpos + rb->width;
+ done = 1;
+ }
+
+ elt--;
+
+ }
+
+ /* The line is blank so everything is considered to be right
+ whitespace. */
+ if (!done)
+ dl->bounds.right_white = dl->bounds.left_in;
+ }
+
+ /* Set the display blocks bounds. */
+ db->start_pos = dl->bounds.left_in;
+ if (Dynarr_length (db->runes))
+ {
+ struct rune *rb = Dynarr_atp (db->runes, Dynarr_length (db->runes) - 1);
+
+ db->end_pos = rb->xpos + rb->width;
+ }
+ else
+ db->end_pos = dl->bounds.right_white;
+
+ calculate_baseline (&data);
+
+ dl->ascent = data.new_ascent;
+ dl->descent = data.new_descent;
+
+ {
+ unsigned short ascent = (unsigned short) XINT (w->minimum_line_ascent);
+
+ if (dl->ascent < ascent)
+ dl->ascent = ascent;
+ }
+ {
+ unsigned short descent = (unsigned short) XINT (w->minimum_line_descent);
+
+ if (dl->descent < descent)
+ dl->descent = descent;
+ }
+
+ calculate_yoffset (dl, db);
+
+ dl->cursor_elt = data.cursor_x;
+ /* #### lossage lossage lossage! Fix this shit! */
+ if (data.bi_bufpos > bi_string_zv)
+ dl->end_bufpos = buffer_or_string_bytind_to_bufpos (disp_string, bi_string_zv);
+ else
+ dl->end_bufpos = buffer_or_string_bytind_to_bufpos (disp_string, data.bi_bufpos) - 1;
+ if (truncate_win)
+ data.dl->num_chars =
+ string_column_at_point (s, dl->end_bufpos, b ? XINT (b->tab_width) : 8);
+ else
+ /* This doesn't correctly take into account tabs and control
+ characters but if the window isn't being truncated then this
+ value isn't going to end up being used anyhow. */
+ data.dl->num_chars = dl->end_bufpos - dl->bufpos;
+
+ /* #### handle horizontally scrolled line with text none of which
+ was actually laid out. */
+
+ /* #### handle any remainder of overlay arrow */
+
+ if (*prop == ADD_FAILED)
+ *prop = NULL;
+
+ if (truncate_win && *prop)
+ {
+ Dynarr_free (*prop);
+ *prop = NULL;
+ }
+
+ extent_fragment_delete (data.ef);
+
+ /* #### If we started at EOB, then make sure we return a value past
+ it so that regenerate_window will exit properly. This is bogus.
+ The main loop should get fixed so that it isn't necessary to call
+ this function if we are already at EOB. */
+
+ if (data.bi_bufpos == bi_string_zv && bi_start_pos == bi_string_zv)
+ return bytecount_to_charcount (string_data (s), data.bi_bufpos) + 1; /* Yuck! */
+ else
+ return bytecount_to_charcount (string_data (s), data.bi_bufpos);
+}
+
+/* Given a display line and a starting position, ensure that the
+ contents of the display line accurately represent the visual
+ representation of the buffer contents starting from the given
+ position when displayed in the given window. The display line ends
+ when the contents of the line reach the right boundary of the given
+ window.
+
+ This is very similar to generate_display_line but with the same
+ limitations as create_string_text_block. I have taken the liberty
+ of fixing the bytind stuff though.*/
+
+static Bufpos
+generate_string_display_line (struct window *w, Lisp_Object disp_string,
+ struct display_line *dl,
+ Bufpos start_pos,
+ prop_block_dynarr **prop,
+ face_index default_face)
+{
+ Bufpos ret_bufpos;
+
+ /* you must set bounds before calling this. */
+
+ /* Reset what this line is using. */
+ if (dl->display_blocks)
+ Dynarr_reset (dl->display_blocks);
+ if (dl->left_glyphs)
+ {
+ Dynarr_free (dl->left_glyphs);
+ dl->left_glyphs = 0;
+ }
+ if (dl->right_glyphs)
+ {
+ Dynarr_free (dl->right_glyphs);
+ dl->right_glyphs = 0;
+ }
+
+ /* We aren't generating a modeline at the moment. */
+ dl->modeline = 0;
+
+ /* Create a display block for the text region of the line. */
+ ret_bufpos = create_string_text_block (w, disp_string, dl, start_pos,
+ prop, default_face);
+ dl->bufpos = start_pos;
+ if (dl->end_bufpos < dl->bufpos)
+ dl->end_bufpos = dl->bufpos;
+
+ /* If there are left glyphs associated with any character in the
+ text block, then create a display block to handle them. */
+ if (dl->left_glyphs != NULL && Dynarr_length (dl->left_glyphs))
+ create_left_glyph_block (w, dl, 0);
+
+ /* If there are right glyphs associated with any character in the
+ text block, then create a display block to handle them. */
+ if (dl->right_glyphs != NULL && Dynarr_length (dl->right_glyphs))
+ create_right_glyph_block (w, dl);
+
+ return ret_bufpos;
+}
+
+/* This is ripped off from regenerate_window. All we want to do is
+ loop through elements in the string creating display lines until we
+ have covered the provided area. Simple really. */
+void
+generate_displayable_area (struct window *w, Lisp_Object disp_string,
+ int xpos, int ypos, int width, int height,
+ display_line_dynarr* dla,
+ Bufpos start_pos,
+ face_index default_face)
+{
+ int yend = ypos + height;
+ Charcount s_zv;
+
+ prop_block_dynarr *prop = 0;
+ layout_bounds bounds;
+ assert (dla);
+
+ Dynarr_reset (dla);
+ /* if there's nothing to do then do nothing. code after this assumes
+ there is something to do. */
+ if (NILP (disp_string))
+ return;
+
+ s_zv = XSTRING_CHAR_LENGTH (disp_string);
+
+ bounds.left_out = xpos;
+ bounds.right_out = xpos + width;
+ /* The inner boundaries mark where the glyph margins are located. */
+ bounds.left_in = bounds.left_out + window_left_margin_width (w);
+ bounds.right_in = bounds.right_out - window_right_margin_width (w);
+ /* We cannot fully calculate the whitespace boundaries as they
+ depend on the contents of the line being displayed. */
+ bounds.left_white = bounds.left_in;
+ bounds.right_white = bounds.right_in;
+
+ while (ypos < yend)
+ {
+ struct display_line dl;
+ struct display_line *dlp;
+ Bufpos next_pos;
+ int local;
+
+ if (Dynarr_length (dla) < Dynarr_largest (dla))
+ {
+ dlp = Dynarr_atp (dla, Dynarr_length (dla));
+ local = 0;
+ }
+ else
+ {
+
+ xzero (dl);
+ dlp = &dl;
+ local = 1;
+ }
+
+ dlp->bounds = bounds;
+ dlp->offset = 0;
+ next_pos = generate_string_display_line (w, disp_string, dlp, start_pos,
+ &prop, default_face);
+ /* we need to make sure that we continue along the line if there
+ is more left to display otherwise we just end up redisplaying
+ the same chunk over and over again. */
+ if (next_pos == start_pos && next_pos < s_zv)
+ start_pos++;
+ else
+ start_pos = next_pos;
+
+ dlp->ypos = ypos + dlp->ascent;
+ ypos = dlp->ypos + dlp->descent;
+
+ if (ypos > yend)
+ {
+ int visible_height = dlp->ascent + dlp->descent;
+
+ dlp->clip = (ypos - yend);
+ visible_height -= dlp->clip;
+
+ if (visible_height < VERTICAL_CLIP (w, 1))
+ {
+ if (local)
+ free_display_line (dlp);
+ break;
+ }
+ }
+ else
+ dlp->clip = 0;
+
+ Dynarr_add (dla, *dlp);
+
+ /* #### This type of check needs to be done down in the
+ generate_display_line call. */
+ if (start_pos >= s_zv)
+ break;
+ }
+
+ if (prop)
+ Dynarr_free (prop);
+}
+
+\f
+/***************************************************************************/
+/* */
+/* window-regeneration routines */
+/* */
+/***************************************************************************/
+
+/* For a given window and starting position in the buffer it contains,
+ ensure that the TYPE display lines accurately represent the
+ presentation of the window. We pass the buffer instead of getting
+ it from the window since redisplay_window may have temporarily
+ changed it to the echo area buffer. */
+
+static void
+regenerate_window (struct window *w, Bufpos start_pos, Bufpos point, int type)
+{
+ struct frame *f = XFRAME (w->frame);
+ struct buffer *b = XBUFFER (w->buffer);
+ int ypos = WINDOW_TEXT_TOP (w);
+ int yend; /* set farther down */
+ int yclip = WINDOW_TEXT_TOP_CLIP (w);
+ int force;
+
+ prop_block_dynarr *prop;
+ layout_bounds bounds;
+ display_line_dynarr *dla;
+ int need_modeline;
+
+ /* The lines had better exist by this point. */
+ if (!(dla = window_display_lines (w, type)))
+ ABORT ();
+ Dynarr_reset (dla);
+ w->max_line_len = 0;
+
+ /* Normally these get updated in redisplay_window but it is possible
+ for this function to get called from some other points where that
+ update may not have occurred. This acts as a safety check. */
+ if (!Dynarr_length (w->face_cachels))
+ reset_face_cachels (w);
+ if (!Dynarr_length (w->glyph_cachels))
+ reset_glyph_cachels (w);
+
+ Fset_marker (w->start[type], make_int (start_pos), w->buffer);
+ Fset_marker (w->pointm[type], make_int (point), w->buffer);
+ w->last_point_x[type] = -1;
+ w->last_point_y[type] = -1;
+
+ /* Make sure a modeline is in the structs if needed. */
+ need_modeline = ensure_modeline_generated (w, type);
+
+ /* Wait until here to set this so that the structs have a modeline
+ generated in the case where one didn't exist. */
+ yend = WINDOW_TEXT_BOTTOM (w);
+
+ bounds = calculate_display_line_boundaries (w, 0);
+
+ /* 97/3/14 jhod: stuff added here to support pre-prompts (used for input systems) */
+ if (MINI_WINDOW_P (w)
+ && (!NILP (Vminibuf_prompt) || !NILP (Vminibuf_preprompt))
+ && !echo_area_active (f)
+ && start_pos == BUF_BEGV (b))
+ {
+ struct prop_block pb;
+ Lisp_Object string;
+ prop = Dynarr_new (prop_block);
+
+ string = concat2(Vminibuf_preprompt, Vminibuf_prompt);
+ pb.type = PROP_MINIBUF_PROMPT;
+ pb.data.p_string.str = XSTRING_DATA(string);
+ pb.data.p_string.len = XSTRING_LENGTH(string);
+ Dynarr_add (prop, pb);
+ }
+ else
+ prop = 0;
+
+ /* When we are computing things for scrolling purposes, make
+ sure at least one line is always generated */
+ force = (type == CMOTION_DISP);
+
+ /* Make sure this is set always */
+ /* Note the conversion at end */
+ w->window_end_pos[type] = start_pos;
+ while (ypos < yend || force)
+ {
+ struct display_line dl;
+ struct display_line *dlp;
+ int local;
+
+ if (Dynarr_length (dla) < Dynarr_largest (dla))
+ {
+ dlp = Dynarr_atp (dla, Dynarr_length (dla));
+ local = 0;
+ }
+ else
+ {
+
+ xzero (dl);
+ dlp = &dl;
+ local = 1;
+ }
+
+ dlp->bounds = bounds;
+ dlp->offset = 0;
+ start_pos = generate_display_line (w, dlp, 1, start_pos, &prop, type);
+
+ if (yclip > dlp->ascent)
+ {
+ /* this should never happen, but if it does just display the
+ whole line */
+ yclip = 0;
+ }
+
+ dlp->ypos = (ypos + dlp->ascent) - yclip;
+ ypos = dlp->ypos + dlp->descent;
+
+ /* See if we've been asked to start midway through a line, for
+ partial display line scrolling. */
+ if (yclip)
+ {
+ dlp->top_clip = yclip;
+ yclip = 0;
+ }
+ else
+ dlp->top_clip = 0;
+
+ if (ypos > yend)
+ {
+ int visible_height = dlp->ascent + dlp->descent;
+
+ dlp->clip = (ypos - yend);
+ /* Although this seems strange we could have a single very
+ tall line visible for which we need to account for both
+ the top clip and the bottom clip. */
+ visible_height -= (dlp->clip + dlp->top_clip);
+
+ if (visible_height < VERTICAL_CLIP (w, 1) && !force)
+ {
+ if (local)
+ free_display_line (dlp);
+ break;
+ }
+ }
+ else
+ dlp->clip = 0;
+
+ if (dlp->cursor_elt != -1)
+ {
+ /* #### This check is steaming crap. Have to get things
+ fixed so when create_text_block hits EOB, we're done,
+ period. */
+ if (w->last_point_x[type] == -1)
+ {
+ w->last_point_x[type] = dlp->cursor_elt;
+ w->last_point_y[type] = Dynarr_length (dla);
+ }
+ else
+ {
+ /* #### This means that we've added a cursor at EOB
+ twice. Yuck oh yuck. */
+ struct display_block *db =
+ get_display_block_from_line (dlp, TEXT);
+
+ Dynarr_atp (db->runes, dlp->cursor_elt)->cursor_type = NO_CURSOR;
+ dlp->cursor_elt = -1;
+ }
+ }
+
+ if (dlp->num_chars > w->max_line_len)
+ w->max_line_len = dlp->num_chars;
+
+ Dynarr_add (dla, *dlp);
+
+ /* #### This isn't right, but it is close enough for now. */
+ w->window_end_pos[type] = start_pos;
+
+ /* #### This type of check needs to be done down in the
+ generate_display_line call. */
+ if (start_pos > BUF_ZV (b))
+ break;
+
+ force = 0;
+ }
+
+ if (prop)
+ Dynarr_free (prop);
+
+ /* #### More not quite right, but close enough. */
+ /* Ben sez: apparently window_end_pos[] is measured
+ as the number of characters between the window end and the
+ end of the buffer? This seems rather weirdo. What's
+ the justification for this?
+
+ JV sez: Because BUF_Z (b) would be a good initial value, however
+ that can change. This representation allows initalizing with 0.
+ */
+ w->window_end_pos[type] = BUF_Z (b) - w->window_end_pos[type];
+
+ if (need_modeline)
+ {
+ /* We know that this is the right thing to use because we put it
+ there when we first started working in this function. */
+ generate_modeline (w, Dynarr_atp (dla, 0), type);
+ }
+}
+
+#define REGEN_INC_FIND_START_END \
+ do { \
+ /* Determine start and end of lines. */ \
+ if (!Dynarr_length (cdla)) \
+ return 0; \
+ else \
+ { \
+ if (Dynarr_atp (cdla, 0)->modeline && Dynarr_atp (ddla, 0)->modeline) \
+ { \
+ dla_start = 1; \
+ } \
+ else if (!Dynarr_atp (cdla, 0)->modeline \
+ && !Dynarr_atp (ddla, 0)->modeline) \
+ { \
+ dla_start = 0; \
+ } \
+ else \
+ ABORT (); /* structs differ */ \
+ \
+ dla_end = Dynarr_length (cdla) - 1; \
+ } \
+ \
+ start_pos = (Dynarr_atp (cdla, dla_start)->bufpos \
+ + Dynarr_atp (cdla, dla_start)->offset); \
+ /* If this isn't true, then startp has changed and we need to do a \
+ full regen. */ \
+ if (startp != start_pos) \
+ return 0; \
+ \
+ /* Point is outside the visible region so give up. */ \
+ if (pointm < start_pos) \
+ return 0; \
+ \
+ } while (0)
+
+/* This attempts to incrementally update the display structures. It
+ returns a boolean indicating success or failure. This function is
+ very similar to regenerate_window_incrementally and is in fact only
+ called from that function. However, because of the nature of the
+ changes it deals with it sometimes makes different assumptions
+ which can lead to success which are much more difficult to make
+ when dealing with buffer changes. */
+
+static int
+regenerate_window_extents_only_changed (struct window *w, Bufpos startp,
+ Bufpos pointm,
+ Charcount beg_unchanged,
+ Charcount end_unchanged)
+{
+ struct buffer *b = XBUFFER (w->buffer);
+ display_line_dynarr *cdla = window_display_lines (w, CURRENT_DISP);
+ display_line_dynarr *ddla = window_display_lines (w, DESIRED_DISP);
+
+ int dla_start = 0;
+ int dla_end, line;
+ int first_line, last_line;
+ Bufpos start_pos;
+ /* Don't define this in the loop where it is used because we
+ definitely want its value to survive between passes. */
+ prop_block_dynarr *prop = NULL;
+
+ /* If we don't have any buffer change recorded but the modiff flag has
+ been incremented, then fail. I'm not sure of the exact circumstances
+ under which this can happen, but I believe that it is probably a
+ reasonable happening. */
+ if (!point_visible (w, pointm, CURRENT_DISP)
+ || XINT (w->last_modified[CURRENT_DISP]) < BUF_MODIFF (b))
+ return 0;
+
+ /* If the cursor is moved we attempt to update it. If we succeed we
+ go ahead and proceed with the optimization attempt. */
+ if (!EQ (Fmarker_buffer (w->last_point[CURRENT_DISP]), w->buffer)
+ || pointm != marker_position (w->last_point[CURRENT_DISP]))
+ {
+ struct frame *f = XFRAME (w->frame);
+ struct device *d = XDEVICE (f->device);
+ struct frame *sel_f = device_selected_frame (d);
+ int success = 0;
+
+ if (w->last_point_x[CURRENT_DISP] != -1
+ && w->last_point_y[CURRENT_DISP] != -1)
+ {
+
+ if (redisplay_move_cursor (w, pointm, WINDOW_TTY_P (w)))
+ {
+ /* Always regenerate the modeline in case it is
+ displaying the current line or column. */
+ regenerate_modeline (w);
+ success = 1;
+ }
+ }
+ else if (w != XWINDOW (FRAME_SELECTED_WINDOW (sel_f)))
+ {
+ if (f->modeline_changed)
+ regenerate_modeline (w);
+ success = 1;
+ }
+
+ if (!success)
+ return 0;
+ }
+
+ if (beg_unchanged == -1 && end_unchanged == -1)
+ return 1;
+
+ /* assert: There are no buffer modifications or they are all below the
+ visible region. We assume that regenerate_window_incrementally has
+ not called us unless this is true. */
+
+ REGEN_INC_FIND_START_END;
+
+ /* If the changed are starts before the visible area, give up. */
+ if (beg_unchanged < startp)
+ return 0;
+
+ /* Find what display line the extent changes first affect. */
+ line = dla_start;
+ while (line <= dla_end)
+ {
+ struct display_line *dl = Dynarr_atp (cdla, line);
+ Bufpos lstart = dl->bufpos + dl->offset;
+ Bufpos lend = dl->end_bufpos + dl->offset;
+
+ if (beg_unchanged >= lstart && beg_unchanged <= lend)
+ break;
+
+ line++;
+ }
+
+ /* If the changes are below the visible area then if point hasn't
+ moved return success otherwise fail in order to be safe. */
+ if (line > dla_end)
+ {
+ if (EQ (Fmarker_buffer (w->last_point[CURRENT_DISP]), w->buffer)
+ && pointm == marker_position (w->last_point[CURRENT_DISP]))
+ return 1;
+ else
+ return 0;
+ }
+
+ /* At this point we know what line the changes first affect. We now
+ begin redrawing lines as long as we are still in the affected
+ region and the line's size and positioning don't change.
+ Otherwise we fail. If we fail we will have altered the desired
+ structs which could lead to an assertion failure. However, if we
+ fail the next thing that is going to happen is a full regen so we
+ will actually end up being safe. */
+ w->last_modified[DESIRED_DISP] = make_int (BUF_MODIFF (b));
+ w->last_facechange[DESIRED_DISP] = make_int (BUF_FACECHANGE (b));
+ Fset_marker (w->last_start[DESIRED_DISP], make_int (startp), w->buffer);
+ Fset_marker (w->last_point[DESIRED_DISP], make_int (pointm), w->buffer);
+
+ first_line = last_line = line;
+ while (line <= dla_end)
+ {
+ Bufpos old_start, old_end, new_start;
+ struct display_line *cdl = Dynarr_atp (cdla, line);
+ struct display_line *ddl = Dynarr_atp (ddla, line);
+ struct display_block *db;
+ int initial_size;
+
+ assert (cdl->bufpos == ddl->bufpos);
+ assert (cdl->end_bufpos == ddl->end_bufpos);
+ assert (cdl->offset == ddl->offset);
+
+ db = get_display_block_from_line (ddl, TEXT);
+ initial_size = Dynarr_length (db->runes);
+ old_start = ddl->bufpos + ddl->offset;
+ old_end = ddl->end_bufpos + ddl->offset;
+
+ /* If this is the first line being updated and it used
+ propagation data, fail. Otherwise we'll be okay because
+ we'll have the necessary propagation data. */
+ if (line == first_line && ddl->used_prop_data)
+ return 0;
+
+ new_start = generate_display_line (w, ddl, 0, ddl->bufpos + ddl->offset,
+ &prop, DESIRED_DISP);
+ ddl->offset = 0;
+
+ /* #### If there is propagated stuff the fail. We could
+ probably actually deal with this if the line had propagated
+ information when originally created by a full
+ regeneration. */
+ if (prop)
+ {
+ Dynarr_free (prop);
+ return 0;
+ }
+
+ /* If any line position parameters have changed or a
+ cursor has disappeared or disappeared, fail. */
+ db = get_display_block_from_line (ddl, TEXT);
+ if (cdl->ypos != ddl->ypos
+ || cdl->ascent != ddl->ascent
+ || cdl->descent != ddl->descent
+ || cdl->top_clip != ddl->top_clip
+ || (cdl->cursor_elt != -1 && ddl->cursor_elt == -1)
+ || (cdl->cursor_elt == -1 && ddl->cursor_elt != -1)
+ || old_start != ddl->bufpos
+ || old_end != ddl->end_bufpos
+ || initial_size != Dynarr_length (db->runes))
+ {
+ return 0;
+ }
+
+ if (ddl->cursor_elt != -1)
+ {
+ w->last_point_x[DESIRED_DISP] = ddl->cursor_elt;
+ w->last_point_y[DESIRED_DISP] = line;
+ }
+
+ last_line = line;
+
+ /* If the extent changes end on the line we just updated then
+ we're done. Otherwise go on to the next line. */
+ if (end_unchanged <= ddl->end_bufpos)
+ break;
+ else
+ line++;
+ }
+
+ redisplay_update_line (w, first_line, last_line, 1);
+ return 1;
+}
+
+/* Attempt to update the display data structures based on knowledge of
+ the changed region in the buffer. Returns a boolean indicating
+ success or failure. If this function returns a failure then a
+ regenerate_window _must_ be performed next in order to maintain
+ invariants located here. */
+
+static int
+regenerate_window_incrementally (struct window *w, Bufpos startp,
+ Bufpos pointm)
+{
+ struct buffer *b = XBUFFER (w->buffer);
+ display_line_dynarr *cdla = window_display_lines (w, CURRENT_DISP);
+ display_line_dynarr *ddla = window_display_lines (w, DESIRED_DISP);
+ Charcount beg_unchanged, end_unchanged;
+ Charcount extent_beg_unchanged, extent_end_unchanged;
+
+ int dla_start = 0;
+ int dla_end, line;
+ Bufpos start_pos;
+
+ /* If this function is called, the current and desired structures
+ had better be identical. If they are not, then that is a bug. */
+ assert (Dynarr_length (cdla) == Dynarr_length (ddla));
+
+ /* We don't handle minibuffer windows yet. The minibuffer prompt
+ screws us up. */
+ if (MINI_WINDOW_P (w))
+ return 0;
+
+ extent_beg_unchanged = BUF_EXTENT_BEGIN_UNCHANGED (b);
+ extent_end_unchanged = (BUF_EXTENT_END_UNCHANGED (b) == -1
+ ? -1
+ : BUF_Z (b) - BUF_EXTENT_END_UNCHANGED (b));
+
+ /* If nothing has changed in the buffer, then make sure point is ok
+ and succeed. */
+ if (BUF_BEGIN_UNCHANGED (b) == -1 && BUF_END_UNCHANGED (b) == -1)
+ return regenerate_window_extents_only_changed (w, startp, pointm,
+ extent_beg_unchanged,
+ extent_end_unchanged);
+
+ /* We can't deal with deleted newlines. */
+ if (BUF_NEWLINE_WAS_DELETED (b))
+ return 0;
+
+ beg_unchanged = BUF_BEGIN_UNCHANGED (b);
+ end_unchanged = (BUF_END_UNCHANGED (b) == -1
+ ? -1
+ : BUF_Z (b) - BUF_END_UNCHANGED (b));
+
+ REGEN_INC_FIND_START_END;
+
+ /* If the changed area starts before the visible area, give up. */
+ if (beg_unchanged < startp)
+ return 0;
+
+ /* Find what display line the buffer changes first affect. */
+ line = dla_start;
+ while (line <= dla_end)
+ {
+ struct display_line *dl = Dynarr_atp (cdla, line);
+ Bufpos lstart = dl->bufpos + dl->offset;
+ Bufpos lend = dl->end_bufpos + dl->offset;
+
+ if (beg_unchanged >= lstart && beg_unchanged <= lend)
+ break;
+
+ line++;
+ }
+
+ /* If the changes are below the visible area then if point hasn't
+ moved return success otherwise fail in order to be safe. */
+ if (line > dla_end)
+ return regenerate_window_extents_only_changed (w, startp, pointm,
+ extent_beg_unchanged,
+ extent_end_unchanged);
+ else
+ /* At this point we know what line the changes first affect. We
+ now redraw that line. If the changes are contained within it
+ we are going to succeed and can update just that one line.
+ Otherwise we fail. If we fail we will have altered the desired
+ structs which could lead to an assertion failure. However, if
+ we fail the next thing that is going to happen is a full regen
+ so we will actually end up being safe. */
+ {
+ Bufpos new_start;
+ prop_block_dynarr *prop = NULL;
+ struct display_line *cdl = Dynarr_atp (cdla, line);
+ struct display_line *ddl = Dynarr_atp (ddla, line);
+
+ assert (cdl->bufpos == ddl->bufpos);
+ assert (cdl->end_bufpos == ddl->end_bufpos);
+ assert (cdl->offset == ddl->offset);
+
+ /* If the line continues to next display line, fail. */
+ if (ddl->line_continuation)
+ return 0;
+
+ /* If the line was generated using propagation data, fail. */
+ if (ddl->used_prop_data)
+ return 0;
+
+ new_start = generate_display_line (w, ddl, 0, ddl->bufpos + ddl->offset,
+ &prop, DESIRED_DISP);
+ ddl->offset = 0;
+
+ /* If there is propagated stuff then it is pretty much a
+ guarantee that more than just the one line is affected. */
+ if (prop)
+ {
+ Dynarr_free (prop);
+ return 0;
+ }
+
+ /* If the line continues to next display line, fail. */
+ if (ddl->line_continuation)
+ return 0;
+
+ /* If any line position parameters have changed or a
+ cursor has disappeared or disappeared, fail. */
+ if (cdl->ypos != ddl->ypos
+ || cdl->ascent != ddl->ascent
+ || cdl->descent != ddl->descent
+ || cdl->top_clip != ddl->top_clip
+ || (cdl->cursor_elt != -1 && ddl->cursor_elt == -1)
+ || (cdl->cursor_elt == -1 && ddl->cursor_elt != -1))
+ {
+ return 0;
+ }
+
+ /* If the changed area also ends on this line, then we may be in
+ business. Update everything and return success. */
+ if (end_unchanged >= ddl->bufpos && end_unchanged <= ddl->end_bufpos)
+ {
+ w->last_modified[DESIRED_DISP] = make_int (BUF_MODIFF (b));
+ w->last_facechange[DESIRED_DISP] = make_int (BUF_FACECHANGE (b));
+ Fset_marker (w->last_start[DESIRED_DISP], make_int (startp),
+ w->buffer);
+ Fset_marker (w->last_point[DESIRED_DISP], make_int (pointm),
+ w->buffer);
+
+ if (ddl->cursor_elt != -1)
+ {
+ w->last_point_x[DESIRED_DISP] = ddl->cursor_elt;
+ w->last_point_y[DESIRED_DISP] = line;
+ }
+
+ redisplay_update_line (w, line, line, 1);
+ regenerate_modeline (w);
+
+ /* #### For now we just flush the cache until this has been
+ tested. After that is done, this should correct the
+ cache directly. */
+ Dynarr_reset (w->line_start_cache);
+
+ /* Adjust the extent changed boundaries to remove any
+ overlap with the buffer changes since we've just
+ successfully updated that area. */
+ if (extent_beg_unchanged != -1
+ && extent_beg_unchanged >= beg_unchanged
+ && extent_beg_unchanged < end_unchanged)
+ extent_beg_unchanged = end_unchanged;
+
+ if (extent_end_unchanged != -1
+ && extent_end_unchanged >= beg_unchanged
+ && extent_end_unchanged < end_unchanged)
+ extent_end_unchanged = beg_unchanged - 1;
+
+ if (extent_end_unchanged <= extent_beg_unchanged)
+ extent_beg_unchanged = extent_end_unchanged = -1;
+
+ /* This could lead to odd results if it fails, but since the
+ buffer changes update succeeded this probably will to.
+ We already know that the extent changes start at or after
+ the line because we checked before entering the loop. */
+ if (extent_beg_unchanged != -1
+ && extent_end_unchanged != -1
+ && ((extent_beg_unchanged < ddl->bufpos)
+ || (extent_end_unchanged > ddl->end_bufpos)))
+ return regenerate_window_extents_only_changed (w, startp, pointm,
+ extent_beg_unchanged,
+ extent_end_unchanged);
+ else
+ return 1;
+ }
+ }
+
+ /* Oh, well. */
+ return 0;
+}
+
+/* Given a window and a point, update the given display lines such
+ that point is displayed in the middle of the window.
+ Return the window's new start position. */
+
+static Bufpos
+regenerate_window_point_center (struct window *w, Bufpos point, int type)
+{
+ Bufpos startp;
+
+ /* We need to make sure that the modeline is generated so that the
+ window height can be calculated correctly. */
+ ensure_modeline_generated (w, type);
+
+ startp = start_with_line_at_pixpos (w, point, window_half_pixpos (w));
+ regenerate_window (w, startp, point, type);
+ Fset_marker (w->start[type], make_int (startp), w->buffer);
+
+ return startp;
+}
+
+/* Given a window and a set of display lines, return a boolean
+ indicating whether the given point is contained within. */
+
+static int
+point_visible (struct window *w, Bufpos point, int type)
+{
+ struct buffer *b = XBUFFER (w->buffer);
+ display_line_dynarr *dla = window_display_lines (w, type);
+ int first_line;
+
+ if (Dynarr_length (dla) && Dynarr_atp (dla, 0)->modeline)
+ first_line = 1;
+ else
+ first_line = 0;
+
+ if (Dynarr_length (dla) > first_line)
+ {
+ Bufpos start, end;
+ struct display_line *dl = Dynarr_atp (dla, first_line);
+
+ start = dl->bufpos;
+ end = BUF_Z (b) - w->window_end_pos[type] - 1;
+
+ if (point >= start && point <= end)
+ {
+ if (!MINI_WINDOW_P (w) && scroll_on_clipped_lines)
+ {
+ dl = Dynarr_atp (dla, Dynarr_length (dla) - 1);
+
+ if (point >= (dl->bufpos + dl->offset)
+ && point <= (dl->end_bufpos + dl->offset))
+ return !dl->clip;
+ else
+ return 1;
+ }
+ else
+ return 1;
+ }
+ else
+ return 0;
+ }
+ else
+ return 0;
+}
+
+/* Return pixel position the middle of the window, not including the
+ modeline and any potential horizontal scrollbar. */
+
+int
+window_half_pixpos (struct window *w)
+{
+ return WINDOW_TEXT_TOP (w) + (WINDOW_TEXT_HEIGHT (w) >> 1);
+}
+
+/* Return the display line which is currently in the middle of the
+ window W for display lines TYPE. */
+
+int
+line_at_center (struct window *w, int type, Bufpos start, Bufpos point)
+{
+ display_line_dynarr *dla;
+ int half;
+ int elt;
+ int first_elt = (MINI_WINDOW_P (w) ? 0 : 1);
+
+ if (type == CMOTION_DISP)
+ regenerate_window (w, start, point, type);
+
+ dla = window_display_lines (w, type);
+ half = window_half_pixpos (w);
+
+ for (elt = first_elt; elt < Dynarr_length (dla); elt++)
+ {
+ struct display_line *dl = Dynarr_atp (dla, elt);
+ int line_bot = dl->ypos + dl->descent;
+
+ if (line_bot > half)
+ return elt;
+ }
+
+ /* We may not have a line at the middle if the end of the buffer is
+ being displayed. */
+ return -1;
+}
+
+/* Return a value for point that would place it at the beginning of
+ the line which is in the middle of the window. */
+
+Bufpos
+point_at_center (struct window *w, int type, Bufpos start, Bufpos point)
+{
+ /* line_at_center will regenerate the display structures, if necessary. */
+ int line = line_at_center (w, type, start, point);
+
+ if (line == -1)
+ return BUF_ZV (XBUFFER (w->buffer));
+ else
+ {
+ display_line_dynarr *dla = window_display_lines (w, type);
+ struct display_line *dl = Dynarr_atp (dla, line);
+
+ return dl->bufpos;
+ }
+}
+
+/* For a given window, ensure that the current visual representation
+ is accurate. */
+
+static void
+redisplay_window (Lisp_Object window, int skip_selected)
+{
+ struct window *w = XWINDOW (window);
+ struct frame *f = XFRAME (w->frame);
+ struct device *d = XDEVICE (f->device);
+ Lisp_Object old_buffer = w->buffer;
+ Lisp_Object the_buffer = w->buffer;
+ struct buffer *b;
+ int echo_active = 0;
+ int startp = 1;
+ int pointm;
+ int old_startp = 1;
+ int old_pointm = 1;
+ int selected_in_its_frame;
+ int selected_globally;
+ int skip_output = 0;
+ int truncation_changed;
+ int inactive_minibuffer =
+ (MINI_WINDOW_P (w) &&
+ (f != device_selected_frame (d)) &&
+ !is_surrogate_for_selected_frame (f));
+
+ /* #### In the new world this function actually does a bunch of
+ optimizations such as buffer-based scrolling, but none of that is
+ implemented yet. */
+
+ /* If this is a combination window, do its children; that's all.
+ The selected window is always a leaf so we don't check for
+ skip_selected here. */
+ if (!NILP (w->vchild))
+ {
+ redisplay_windows (w->vchild, skip_selected);
+ return;
+ }
+ if (!NILP (w->hchild))
+ {
+ redisplay_windows (w->hchild, skip_selected);
+ return;
+ }
+
+ /* Is this window the selected window on its frame? */
+ selected_in_its_frame = (w == XWINDOW (FRAME_SELECTED_WINDOW (f)));
+ selected_globally =
+ selected_in_its_frame &&
+ EQ(DEVICE_CONSOLE(d), Vselected_console) &&
+ XDEVICE(CONSOLE_SELECTED_DEVICE(XCONSOLE(DEVICE_CONSOLE(d)))) == d &&
+ XFRAME(DEVICE_SELECTED_FRAME(d)) == f;
+ if (skip_selected && selected_in_its_frame)
+ return;
+
+ /* It is possible that the window is not fully initialized yet. */
+ if (NILP (w->buffer))
+ return;
+
+ if (MINI_WINDOW_P (w) && echo_area_active (f))
+ {
+ w->buffer = the_buffer = Vecho_area_buffer;
+ echo_active = 1;
+ }
+
+ b = XBUFFER (w->buffer);
+
+ if (echo_active)
+ {
+ old_pointm = selected_globally
+ ? BUF_PT (b)
+ : marker_position (w->pointm[CURRENT_DISP]);
+ pointm = 1;
+ }
+ else
+ {
+ if (selected_globally)
+ {
+ pointm = BUF_PT (b);
+ }
+ else
+ {
+ pointm = marker_position (w->pointm[CURRENT_DISP]);
+
+ if (pointm < BUF_BEGV (b))
+ pointm = BUF_BEGV (b);
+ else if (pointm > BUF_ZV (b))
+ pointm = BUF_ZV (b);
+ }
+ }
+ Fset_marker (w->pointm[DESIRED_DISP], make_int (pointm), the_buffer);
+
+ /* If the buffer has changed we have to invalidate all of our face
+ cache elements. */
+ if ((!echo_active && b != window_display_buffer (w))
+ || !Dynarr_length (w->face_cachels)
+ || f->faces_changed)
+ reset_face_cachels (w);
+ else
+ mark_face_cachels_as_not_updated (w);
+
+ /* Ditto the glyph cache elements, although we do *not* invalidate
+ the cache purely because glyphs have changed - this is now
+ handled by the dirty flag.*/
+ if ((!echo_active && b != window_display_buffer (w))
+ || !Dynarr_length (w->glyph_cachels) || f->faces_changed)
+ reset_glyph_cachels (w);
+ else
+ mark_glyph_cachels_as_not_updated (w);
+
+ /* If the marker's buffer is not the window's buffer, then we need
+ to find a new starting position. */
+ if (!MINI_WINDOW_P (w)
+ && !EQ (Fmarker_buffer (w->start[CURRENT_DISP]), w->buffer))
+ {
+ startp = regenerate_window_point_center (w, pointm, DESIRED_DISP);
+
+ goto regeneration_done;
+ }
+
+ if (echo_active)
+ {
+ old_startp = marker_position (w->start[CURRENT_DISP]);
+ startp = 1;
+ }
+ else
+ {
+ startp = marker_position (w->start[CURRENT_DISP]);
+ if (startp < BUF_BEGV (b))
+ startp = BUF_BEGV (b);
+ else if (startp > BUF_ZV (b))
+ startp = BUF_ZV (b);
+ }
+ Fset_marker (w->start[DESIRED_DISP], make_int (startp), the_buffer);
+
+ truncation_changed = (find_window_mirror (w)->truncate_win !=
+ window_truncation_on (w));
+
+ /* If w->force_start is set, then some function set w->start and we
+ should display from there and change point, if necessary, to
+ ensure that it is visible. */
+ if (w->force_start || inactive_minibuffer)
+ {
+ w->force_start = 0;
+ w->last_modified[DESIRED_DISP] = Qzero;
+ w->last_facechange[DESIRED_DISP] = Qzero;
+
+ regenerate_window (w, startp, pointm, DESIRED_DISP);
+
+ if (!point_visible (w, pointm, DESIRED_DISP) && !inactive_minibuffer)
+ {
+ pointm = point_at_center (w, DESIRED_DISP, 0, 0);
+
+ if (selected_globally)
+ BUF_SET_PT (b, pointm);
+
+ Fset_marker (w->pointm[DESIRED_DISP], make_int (pointm),
+ the_buffer);
+
+ /* #### BUFU amounts of overkill just to get the cursor
+ location marked properly. FIX ME FIX ME FIX ME */
+ regenerate_window (w, startp, pointm, DESIRED_DISP);
+ }
+
+ goto regeneration_done;
+ }
+
+ /* If nothing has changed since the last redisplay, then we just
+ need to make sure that point is still visible. */
+ if (XINT (w->last_modified[CURRENT_DISP]) >= BUF_MODIFF (b)
+ && XINT (w->last_facechange[CURRENT_DISP]) >= BUF_FACECHANGE (b)
+ && pointm >= startp
+ /* This check is to make sure we restore the minibuffer after a
+ temporary change to the echo area. */
+ && !(MINI_WINDOW_P (w) && f->buffers_changed)
+ && !f->frame_changed
+ && !truncation_changed
+ /* check whether start is really at the beginning of a line GE */
+ && (!w->start_at_line_beg || beginning_of_line_p (b, startp))
+ )
+ {
+ /* Check if the cursor has actually moved. */
+ if (EQ (Fmarker_buffer (w->last_point[CURRENT_DISP]), w->buffer)
+ && pointm == marker_position (w->last_point[CURRENT_DISP])
+ && selected_globally
+ && !w->windows_changed
+ && !f->clip_changed
+ && !f->extents_changed
+ && !f->faces_changed
+ && !f->glyphs_changed
+ && !f->subwindows_changed
+ /* && !f->subwindows_state_changed*/
+ && !f->point_changed
+ && !f->windows_structure_changed)
+ {
+ /* If not, we're done. */
+ if (f->modeline_changed)
+ regenerate_modeline (w);
+
+ skip_output = 1;
+ goto regeneration_done;
+ }
+ else
+ {
+ /* If the new point is visible in the redisplay structures,
+ then let the output update routines handle it, otherwise
+ do things the hard way. */
+ if (!w->windows_changed
+ && !f->clip_changed
+ && !f->extents_changed
+ && !f->faces_changed
+ && !f->glyphs_changed
+ && !f->subwindows_changed
+ /* && !f->subwindows_state_changed*/
+ && !f->windows_structure_changed)
+ {
+ if (point_visible (w, pointm, CURRENT_DISP)
+ && w->last_point_x[CURRENT_DISP] != -1
+ && w->last_point_y[CURRENT_DISP] != -1)
+ {
+ if (redisplay_move_cursor (w, pointm, FRAME_TTY_P (f)))
+ {
+ /* Always regenerate in case it is displaying
+ the current line or column. */
+ regenerate_modeline (w);
+
+ skip_output = 1;
+ goto regeneration_done;
+ }
+ }
+ else if (!selected_in_its_frame && !f->point_changed)
+ {
+ if (f->modeline_changed)
+ regenerate_modeline (w);
+
+ skip_output = 1;
+ goto regeneration_done;
+ }
+ }
+
+ /* If we weren't able to take the shortcut method, then use
+ the brute force method. */
+ regenerate_window (w, startp, pointm, DESIRED_DISP);
+
+ if (point_visible (w, pointm, DESIRED_DISP))
+ goto regeneration_done;
+ }
+ }
+
+ /* Check if the starting point is no longer at the beginning of a
+ line, in which case find a new starting point. We also recenter
+ if our start position is equal to point-max. Otherwise we'll end
+ up with a blank window. */
+ else if (((w->start_at_line_beg || MINI_WINDOW_P (w))
+ && !(startp == BUF_BEGV (b)
+ || BUF_FETCH_CHAR (b, startp - 1) == '\n'))
+ || (pointm == startp &&
+ EQ (Fmarker_buffer (w->last_start[CURRENT_DISP]), w->buffer) &&
+ startp < marker_position (w->last_start[CURRENT_DISP]))
+ || (startp == BUF_ZV (b)))
+ {
+ startp = regenerate_window_point_center (w, pointm, DESIRED_DISP);
+
+ goto regeneration_done;
+ }
+ /* See if we can update the data structures locally based on
+ knowledge of what changed in the buffer. */
+ else if (!w->windows_changed
+ && !f->clip_changed
+ && !f->faces_changed
+ && !f->glyphs_changed
+ && !f->subwindows_changed
+ /* && !f->subwindows_state_changed*/
+ && !f->windows_structure_changed
+ && !f->frame_changed
+ && !truncation_changed
+ && pointm >= startp
+ && regenerate_window_incrementally (w, startp, pointm))
+ {
+ if (f->modeline_changed
+ || XINT (w->last_modified[CURRENT_DISP]) < BUF_MODIFF (b)
+ || XINT (w->last_facechange[CURRENT_DISP]) < BUF_FACECHANGE (b))
+ regenerate_modeline (w);
+
+ skip_output = 1;
+ goto regeneration_done;
+ }
+ /* #### This is where a check for structure based scrolling would go. */
+ /* If all else fails, try just regenerating and see what happens. */
+ else
+ {
+ regenerate_window (w, startp, pointm, DESIRED_DISP);
+
+ if (point_visible (w, pointm, DESIRED_DISP))
+ goto regeneration_done;
+ }
+
+ /* We still haven't gotten the window regenerated with point
+ visible. Next we try scrolling a little and see if point comes
+ back onto the screen. */
+ if (scroll_step > 0)
+ {
+ int scrolled = scroll_conservatively;
+ for (; scrolled >= 0; scrolled -= scroll_step)
+ {
+ startp = vmotion (w, startp,
+ (pointm < startp) ? -scroll_step : scroll_step, 0);
+ regenerate_window (w, startp, pointm, DESIRED_DISP);
+
+ if (point_visible (w, pointm, DESIRED_DISP))
+ goto regeneration_done;
+ }
+ }
+
+ /* We still haven't managed to get the screen drawn with point on
+ the screen, so just center it and be done with it. */
+ startp = regenerate_window_point_center (w, pointm, DESIRED_DISP);
+
+
+regeneration_done:
+
+ /* If the window's frame is changed then reset the current display
+ lines in order to force a full repaint. */
+ if (f->frame_changed)
+ {
+ display_line_dynarr *cla = window_display_lines (w, CURRENT_DISP);
+
+ Dynarr_reset (cla);
+ }
+
+ /* Must do this before calling redisplay_output_window because it
+ sets some markers on the window. */
+ if (echo_active)
+ {
+ w->buffer = old_buffer;
+ Fset_marker (w->pointm[DESIRED_DISP], make_int (old_pointm), old_buffer);
+ Fset_marker (w->start[DESIRED_DISP], make_int (old_startp), old_buffer);
+ }
+
+ /* These also have to be set before calling redisplay_output_window
+ since it sets the CURRENT_DISP values based on them. */
+ w->last_modified[DESIRED_DISP] = make_int (BUF_MODIFF (b));
+ w->last_facechange[DESIRED_DISP] = make_int (BUF_FACECHANGE (b));
+ Fset_marker (w->last_start[DESIRED_DISP], make_int (startp), w->buffer);
+ Fset_marker (w->last_point[DESIRED_DISP], make_int (pointm), w->buffer);
+
+ if (!skip_output)
+ {
+ Bufpos start = marker_position (w->start[DESIRED_DISP]);
+ Bufpos end = (w->window_end_pos[DESIRED_DISP] == -1
+ ? BUF_ZV (b)
+ : BUF_Z (b) - w->window_end_pos[DESIRED_DISP] - 1);
+ /* Don't pollute the cache if not sure if we are correct */
+ if (w->start_at_line_beg)
+ update_line_start_cache (w, start, end, pointm, 1);
+ redisplay_output_window (w);
+ /*
+ * If we just displayed the echo area, the line start cache is
+ * no longer valid, because the minibuffer window is associated
+ * with the window now.
+ */
+ if (echo_active)
+ w->line_cache_last_updated = make_int (-1);
+ }
+
+ /* #### This should be dependent on face changes and will need to be
+ somewhere else once tty updates occur on a per-frame basis. */
+ mark_face_cachels_as_clean (w);
+
+ /* The glyph cachels only get dirty if someone changed something.
+ Since redisplay has now effectively ended we can reset the dirty
+ flag since everything must be up-to-date. */
+ if (glyphs_changed)
+ mark_glyph_cachels_as_clean (w);
+
+ w->windows_changed = 0;
+}
+
+/* Call buffer_reset_changes for all buffers present in any window
+ currently visible in all frames on all devices. #### There has to
+ be a better way to do this. */
+
+static int
+reset_buffer_changes_mapfun (struct window *w, void *ignored_closure)
+{
+ buffer_reset_changes (XBUFFER (w->buffer));
+ return 0;
+}
+
+static void
+reset_buffer_changes (void)
+{
+ Lisp_Object frmcons, devcons, concons;
+
+ FRAME_LOOP_NO_BREAK (frmcons, devcons, concons)
+ {
+ struct frame *f = XFRAME (XCAR (frmcons));
+
+ if (FRAME_REPAINT_P (f))
+ map_windows (f, reset_buffer_changes_mapfun, 0);
+ }
+}
+
+/* Ensure that all windows underneath the given window in the window
+ hierarchy are correctly displayed. */
+
+static void
+redisplay_windows (Lisp_Object window, int skip_selected)
+{
+ for (; !NILP (window) ; window = XWINDOW (window)->next)
+ {
+ redisplay_window (window, skip_selected);
+ }
+}
+
+static int
+call_redisplay_end_triggers (struct window *w, void *closure)
+{
+ Bufpos lrpos = w->last_redisplay_pos;
+ w->last_redisplay_pos = 0;
+ if (!NILP (w->buffer)
+ && !NILP (w->redisplay_end_trigger)
+ && lrpos > 0)
+ {
+ Bufpos pos;
+
+ if (MARKERP (w->redisplay_end_trigger)
+ && XMARKER (w->redisplay_end_trigger)->buffer != 0)
+ pos = marker_position (w->redisplay_end_trigger);
+ else if (INTP (w->redisplay_end_trigger))
+ pos = XINT (w->redisplay_end_trigger);
+ else
+ {
+ w->redisplay_end_trigger = Qnil;
+ return 0;
+ }
+
+ if (lrpos >= pos)
+ {
+ Lisp_Object window;
+ XSETWINDOW (window, w);
+ va_run_hook_with_args_in_buffer (XBUFFER (w->buffer),
+ Qredisplay_end_trigger_functions,
+ 2, window,
+ w->redisplay_end_trigger);
+ w->redisplay_end_trigger = Qnil;
+ }
+ }
+
+ return 0;
+}
+
+/* Ensure that all windows on the given frame are correctly displayed. */
+
+int
+redisplay_frame (struct frame *f, int preemption_check)
+{
+ struct device *d = XDEVICE (f->device);
+
+ if (preemption_check
+ && !DEVICE_IMPL_FLAG (d, XDEVIMPF_DONT_PREEMPT_REDISPLAY))
+ {
+ /* The preemption check itself takes a lot of time,
+ so normally don't do it here. We do it if called
+ from Lisp, though (`redisplay-frame'). */
+ int preempted;
+
+ REDISPLAY_PREEMPTION_CHECK;
+ if (preempted)
+ return 1;
+ }
+
+ if (!internal_equal (f->old_buffer_alist, f->buffer_alist, 0))
+ {
+ Lisp_Object frame;
+
+ f->old_buffer_alist = Freplace_list (f->old_buffer_alist,
+ f->buffer_alist);
+ XSETFRAME (frame, f);
+ va_run_hook_with_args (Qbuffer_list_changed_hook, 1, frame);
+ }
+
+ /* Before we put a hold on frame size changes, attempt to process
+ any which are already pending. */
+ if (f->size_change_pending)
+ change_frame_size (f, f->new_height, f->new_width, 0);
+
+ /* If frame size might need to be changed, due to changed size
+ of toolbars, scrollbars etc, change it now */
+ if (f->size_slipped)
+ {
+ adjust_frame_size (f);
+ assert (!f->size_slipped);
+ }
+
+ /* The menubar, toolbar, and icon updates must be done before
+ hold_frame_size_changes is called and we are officially
+ 'in_display'. They may eval lisp code which may call Fsignal.
+ If in_display is set Fsignal will ABORT. */
+
+#ifdef HAVE_MENUBARS
+ /* Update the menubar. It is done first since it could change
+ the menubar's visibility. This way we avoid having flashing
+ caused by an Expose event generated by the visibility change
+ being handled. */
+ update_frame_menubars (f);
+#endif /* HAVE_MENUBARS */
+#ifdef HAVE_TOOLBARS
+ /* Update the toolbars geometry. We don't update the toolbars
+ themselves at this point since the space they are trying to
+ occupy may currently by occupied by gutter elements. Instead we
+ update the geometry, then update the gutter geometry, then update
+ the gutters - which will cause mapped windows to be repositioned
+ - and finally update the toolbars. */
+ update_frame_toolbars_geometry (f);
+#endif /* HAVE_TOOLBARS */
+ /* Gutter update proper has to be done inside display when no frame
+ size changes can occur, thus we separately update the gutter
+ geometry here if it needs it. */
+ update_frame_gutter_geometry (f);
+
+ /* If we clear the frame we have to force its contents to be redrawn. */
+ if (f->clear)
+ f->frame_changed = 1;
+
+ /* Invalidate the subwindow caches. We use subwindows_changed here
+ to cause subwindows to get instantiated. This is because
+ subwindows_state_changed is less strict - dealing with things
+ like the clicked state of button. We have to do this before
+ redisplaying the gutters as subwindows get unmapped in the
+ process.*/
+ if (f->frame_changed)
+ reset_frame_subwindow_instance_cache (f);
+
+ if (f->frame_changed || f->subwindows_changed)
+ {
+ /* we have to do this so the gutter gets regenerated. */
+ reset_gutter_display_lines (f);
+ }
+
+ hold_frame_size_changes ();
+
+ /* ----------------- BEGIN CRITICAL REDISPLAY SECTION ---------------- */
+ /* Within this section, we are defenseless and assume that the
+ following cannot happen:
+
+ 1) garbage collection
+ 2) Lisp code evaluation
+ 3) frame size changes
+
+ We ensure (3) by calling hold_frame_size_changes(), which
+ will cause any pending frame size changes to get put on hold
+ till after the end of the critical section. (1) follows
+ automatically if (2) is met. #### Unfortunately, there are
+ some places where Lisp code can be called within this section.
+ We need to remove them.
+
+ If Fsignal() is called during this critical section, we
+ will ABORT().
+
+ If garbage collection is called during this critical section,
+ we simply return. #### We should ABORT instead.
+
+ #### If a frame-size change does occur we should probably
+ actually be preempting redisplay. */
+
+ MAYBE_DEVMETH (d, frame_output_begin, (f));
+
+ /* We can now update the gutters, safe in the knowledge that our
+ efforts won't get undone. */
+
+ /* This can call lisp, but redisplay is protected by binding
+ inhibit_quit. More importantly the code involving display lines
+ *assumes* that GC will not happen and so does not GCPRO
+ anything. Since we use this code the whole time with the gutters
+ we cannot allow GC to happen when manipulating the gutters. */
+ update_frame_gutters (f);
+
+ /* Erase the frame before outputting its contents. */
+ if (f->clear)
+ {
+ MAYBE_DEVMETH (d, clear_frame, (f));
+ }
+
+ /* Do the selected window first. */
+ redisplay_window (FRAME_SELECTED_WINDOW (f), 0);
+
+ /* Then do the rest. */
+ redisplay_windows (f->root_window, 1);
+
+ MAYBE_DEVMETH (d, frame_output_end, (f));
+
+ update_frame_title (f);
+
+#ifdef HAVE_TOOLBARS
+ /* Finally update the toolbars. It seems its possible to get in a
+ cycle between updating the gutter and the toolbars. Basically we
+ want to end up with both being up-to-date and this doesn't seem
+ possible in a single pass. */
+ update_frame_toolbars (f);
+#endif /* HAVE_TOOLBARS */
+
+ CLASS_RESET_CHANGED_FLAGS (f);
+ f->window_face_cache_reset = 0;
+ f->echo_area_garbaged = 0;
+ f->clear = 0;
+
+ if (!f->size_change_pending)
+ f->size_changed = 0;
+
+ /* ----------------- END CRITICAL REDISPLAY SECTION ---------------- */
+
+ /* Allow frame size changes to occur again.
+
+ #### what happens if changes to other frames happen? */
+ unhold_one_frame_size_changes (f);
+
+ map_windows (f, call_redisplay_end_triggers, 0);
+ return 0;
+}
+
+/* Ensure that all frames on the given device are correctly displayed.
+ If AUTOMATIC is non-zero, and the device implementation indicates
+ no automatic redisplay, as printers do, then the device is not
+ redisplayed. AUTOMATIC is set to zero when called from lisp
+ functions (redraw-device) and (redisplay-device), and to non-zero
+ when called from "lazy" redisplay();
+*/
+
+static int
+redisplay_device (struct device *d, int automatic)
+{
+ Lisp_Object frame, frmcons;
+ int size_change_failed = 0;
+ struct frame *f;
+
+ if (automatic && DEVICE_IMPL_FLAG (d, XDEVIMPF_NO_AUTO_REDISPLAY))
+ return 0;
+
+ if (DEVICE_STREAM_P (d)) /* nothing to do */
+ return 0;
+
+ /* It is possible that redisplay has been called before the
+ device is fully initialized, or that the console implementation
+ allows frameless devices. If so then continue with the next
+ device. */
+ if (NILP (DEVICE_SELECTED_FRAME (d)))
+ return 0;
+
+ if (!DEVICE_IMPL_FLAG (d, XDEVIMPF_DONT_PREEMPT_REDISPLAY))
+ {
+ int preempted;
+ REDISPLAY_PREEMPTION_CHECK;
+ if (preempted)
+ return 1;
+ }
+
+ /* Always do the selected frame first. */
+ frame = DEVICE_SELECTED_FRAME (d);
+
+ f = XFRAME (frame);
+
+ if (f->icon_changed || f->windows_changed)
+ update_frame_icon (f);
+
+ if (FRAME_REPAINT_P (f))
+ {
+ if (CLASS_REDISPLAY_FLAGS_CHANGEDP(f))
+ {
+ int preempted = redisplay_frame (f, 0);
+ if (preempted)
+ return 1;
+ }
+
+ /* If the frame redisplay did not get preempted, then this flag
+ should have gotten set to 0. It might be possible for that
+ not to happen if a size change event were to occur at an odd
+ time. To make sure we don't miss anything we simply don't
+ reset the top level flags until the condition ends up being
+ in the right state. */
+ if (f->size_changed)
+ size_change_failed = 1;
+ }
+
+ DEVICE_FRAME_LOOP (frmcons, d)
+ {
+ f = XFRAME (XCAR (frmcons));
+
+ if (f == XFRAME (DEVICE_SELECTED_FRAME (d)))
+ continue;
+
+ if (f->icon_changed || f->windows_changed)
+ update_frame_icon (f);
+
+ if (FRAME_REPAINT_P (f))
+ {
+ if (CLASS_REDISPLAY_FLAGS_CHANGEDP (f))
+ {
+ int preempted = redisplay_frame (f, 0);
+ if (preempted)
+ return 1;
+ }
+
+ if (f->size_change_pending)
+ size_change_failed = 1;
+ }
+ }
+
+ /* If we get here then we redisplayed all of our frames without
+ getting preempted so mark ourselves as clean. */
+ CLASS_RESET_CHANGED_FLAGS (d);
+
+ if (!size_change_failed)
+ d->size_changed = 0;
+
+ return 0;
+}
+
+static Lisp_Object
+restore_profiling_redisplay_flag (Lisp_Object val)
+{
+ profiling_redisplay_flag = XINT (val);
+ return Qnil;
+}
+
+/* Ensure that all windows on all frames on all devices are displaying
+ the current contents of their respective buffers. */
+
+static void
+redisplay_without_hooks (void)
+{
+ Lisp_Object devcons, concons;
+ int size_change_failed = 0;
+ int count = specpdl_depth ();
+
+ if (profiling_active)
+ {
+ record_unwind_protect (restore_profiling_redisplay_flag,
+ make_int (profiling_redisplay_flag));
+ profiling_redisplay_flag = 1;
+ }
+
+ if (asynch_device_change_pending)
+ handle_asynch_device_change ();
+
+ if (!GLOBAL_REDISPLAY_FLAGS_CHANGEDP &&
+ !disable_preemption && preemption_count < max_preempts)
+ goto done;
+
+ DEVICE_LOOP_NO_BREAK (devcons, concons)
+ {
+ struct device *d = XDEVICE (XCAR (devcons));
+ int preempted;
+
+ if (CLASS_REDISPLAY_FLAGS_CHANGEDP (d))
+ {
+ preempted = redisplay_device (d, 1);
+
+ if (preempted)
+ {
+ preemption_count++;
+ RESET_CHANGED_SET_FLAGS;
+ goto done;
+ }
+
+ /* See comment in redisplay_device. */
+ if (d->size_changed)
+ size_change_failed = 1;
+ }
+ }
+ preemption_count = 0;
+
+ /* Mark redisplay as accurate */
+ GLOBAL_RESET_CHANGED_FLAGS;
+ RESET_CHANGED_SET_FLAGS;
+
+ if (faces_changed)
+ {
+ mark_all_faces_as_clean ();
+ faces_changed = 0;
+ }
+
+ if (!size_change_failed)
+ size_changed = 0;
+
+ reset_buffer_changes ();
+
+ done:
+ unbind_to (count, Qnil);
+}
+
+void
+redisplay (void)
+{
+ if (last_display_warning_tick != display_warning_tick &&
+ !inhibit_warning_display)
+ {
+ /* If an error occurs during this function, oh well.
+ If we report another warning, we could get stuck in an
+ infinite loop reporting warnings. */
+ call0_trapping_errors (0, Qdisplay_warning_buffer);
+ last_display_warning_tick = display_warning_tick;
+ }
+ /* The run_hook_trapping_errors functions are smart enough not
+ to do any evalling if the hook function is empty, so there
+ should not be any significant time loss. All places in the
+ C code that call redisplay() are prepared to handle GCing,
+ so we should be OK. */
+#ifndef INHIBIT_REDISPLAY_HOOKS
+ run_hook_trapping_errors ("Error in pre-redisplay-hook",
+ Qpre_redisplay_hook);
+#endif /* INHIBIT_REDISPLAY_HOOKS */
+
+ redisplay_without_hooks ();
+
+#ifndef INHIBIT_REDISPLAY_HOOKS
+ run_hook_trapping_errors ("Error in post-redisplay-hook",
+ Qpost_redisplay_hook);
+#endif /* INHIBIT_REDISPLAY_HOOKS */
+}
+
+
+static char window_line_number_buf[32];
+
+/* Efficiently determine the window line number, and return a pointer
+ to its printed representation. Do this regardless of whether
+ line-number-mode is on. The first line in the buffer is counted as
+ 1. If narrowing is in effect, the lines are counted from the
+ beginning of the visible portion of the buffer. */
+static char *
+window_line_number (struct window *w, int type)
+{
+ struct device *d = XDEVICE (XFRAME (w->frame)->device);
+ struct buffer *b = XBUFFER (w->buffer);
+ /* Be careful in the order of these tests. The first clause will
+ fail if DEVICE_SELECTED_FRAME == Qnil (since w->frame cannot be).
+ This can occur when the frame title is computed really early */
+ Bufpos pos =
+ ((EQ(DEVICE_SELECTED_FRAME(d), w->frame) &&
+ (w == XWINDOW (FRAME_SELECTED_WINDOW (device_selected_frame(d)))) &&
+ EQ(DEVICE_CONSOLE(d), Vselected_console) &&
+ XDEVICE(CONSOLE_SELECTED_DEVICE(XCONSOLE(DEVICE_CONSOLE(d)))) == d )
+ ? BUF_PT (b)
+ : marker_position (w->pointm[type]));
+ EMACS_INT line;
+
+ line = buffer_line_number (b, pos, 1);
+
+ long_to_string (window_line_number_buf, line + 1);
+
+ return window_line_number_buf;
+}
+
+
+/* Given a character representing an object in a modeline
+ specification, return a string (stored into the global array
+ `mode_spec_bufbyte_string') with the information that object
+ represents.
+
+ This function is largely unchanged from previous versions of the
+ redisplay engine.
+
+ Warning! This code is also used for frame titles and can be called
+ very early in the device/frame update process! JV
+*/
+
+static void
+decode_mode_spec (struct window *w, Emchar spec, int type)
+{
+ Lisp_Object obj = Qnil;
+ const char *str = NULL;
+ struct buffer *b = XBUFFER (w->buffer);
+
+ Dynarr_reset (mode_spec_bufbyte_string);
+
+ switch (spec)
+ {
+ /* print buffer name */
+ case 'b':
+ obj = b->name;
+ break;
+
+ /* print visited file name */
+ case 'f':
+ obj = b->filename;
+ break;
+
+ /* print the current column */
+ case 'c':
+ {
+ Bufpos pt = (w == XWINDOW (Fselected_window (Qnil)))
+ ? BUF_PT (b)
+ : marker_position (w->pointm[type]);
+ int col = column_at_point (b, pt, 1) + !!column_number_start_at_one;
+ char buf[32];
+
+ long_to_string (buf, col);
+
+ Dynarr_add_many (mode_spec_bufbyte_string,
+ (const Bufbyte *) buf, strlen (buf));
+
+ goto decode_mode_spec_done;
+ }
+ /* print the file coding system */
+ case 'C':
+#ifdef FILE_CODING
+ {
+ Lisp_Object codesys = b->buffer_file_coding_system;
+ /* Be very careful here not to get an error. */
+ if (NILP (codesys) || SYMBOLP (codesys) || CODING_SYSTEMP (codesys))
+ {
+ codesys = Ffind_coding_system (codesys);
+ if (CODING_SYSTEMP (codesys))
+ obj = XCODING_SYSTEM_MNEMONIC (codesys);
+ }
+ }
+#endif /* FILE_CODING */
+ break;
+
+ /* print the current line number */
+ case 'l':
+ str = window_line_number (w, type);
+ break;
+
+ /* print value of mode-name (obsolete) */
+ case 'm':
+ obj = b->mode_name;
+ break;
+
+ /* print hyphen and frame number, if != 1 */
+ case 'N':
+#ifdef HAVE_TTY
+ {
+ struct frame *f = XFRAME (w->frame);
+ if (FRAME_TTY_P (f) && f->order_count > 1 && f->order_count <= 99999999)
+ {
+ /* Naughty, naughty */
+ char * writable_str = alloca_array (char, 10);
+ sprintf (writable_str, "-%d", f->order_count);
+ str = writable_str;
+ }
+ }
+#endif /* HAVE_TTY */
+ break;
+
+ /* print Narrow if appropriate */
+ case 'n':
+ if (BUF_BEGV (b) > BUF_BEG (b)
+ || BUF_ZV (b) < BUF_Z (b))
+ str = " Narrow";
+ break;
+
+ /* print %, * or hyphen, if buffer is read-only, modified or neither */
+ case '*':
+ str = (!NILP (b->read_only)
+ ? "%"
+ : ((BUF_MODIFF (b) > BUF_SAVE_MODIFF (b))
+ ? "*"
+ : "-"));
+ break;
+
+ /* print * or hyphen -- XEmacs change to allow a buffer to be
+ read-only but still indicate whether it is modified. */
+ case '+':
+ str = ((BUF_MODIFF (b) > BUF_SAVE_MODIFF (b))
+ ? "*"
+ : (!NILP (b->read_only)
+ ? "%"
+ : "-"));
+ break;
+
+ /* #### defined in 19.29 decode_mode_spec, but not in
+ modeline-format doc string. */
+ /* This differs from %* in that it ignores read-only-ness. */
+ case '&':
+ str = ((BUF_MODIFF (b) > BUF_SAVE_MODIFF (b))
+ ? "*"
+ : "-");
+ break;
+
+ /* print process status */
+ case 's':
+ obj = Fget_buffer_process (w->buffer);
+ if (NILP (obj))
+ str = GETTEXT ("no process");
+ else
+ obj = Fsymbol_name (Fprocess_status (obj));
+ break;
+
+ /* Print name of selected frame. */
+ case 'S':
+ obj = XFRAME (w->frame)->name;
+ break;
+
+ /* indicate TEXT or BINARY */
+ case 't':
+ /* #### NT does not use this any more. Now what? */
+ str = "T";
+ break;
+
+ /* print percent of buffer above top of window, or Top, Bot or All */
+ case 'p':
+ {
+ Bufpos pos = marker_position (w->start[type]);
+
+ /* This had better be while the desired lines are being done. */
+ if (w->window_end_pos[type] <= BUF_Z (b) - BUF_ZV (b))
+ {
+ if (pos <= BUF_BEGV (b))
+ str = "All";
+ else
+ str = "Bottom";
+ }
+ else if (pos <= BUF_BEGV (b))
+ str = "Top";
+ else
+ {
+ /* This hard limit is ok since the string it will hold has a
+ fixed maximum length of 3. But just to be safe... */
+ char buf[10];
+ Charcount chars = pos - BUF_BEGV (b);
+ Charcount total = BUF_ZV (b) - BUF_BEGV (b);
+
+ /* Avoid overflow on big buffers */
+ int percent = total > LONG_MAX/200 ?
+ (chars + total/200) / (total / 100) :
+ (chars * 100 + total/2) / total;
+
+ /* We can't normally display a 3-digit number, so get us a
+ 2-digit number that is close. */
+ if (percent == 100)
+ percent = 99;
+
+ sprintf (buf, "%d%%", percent);
+ Dynarr_add_many (mode_spec_bufbyte_string, (Bufbyte *) buf,
+ strlen (buf));
+
+ goto decode_mode_spec_done;
+ }
+ break;
+ }
+
+ /* print percent of buffer above bottom of window, perhaps plus
+ Top, or print Bottom or All */
+ case 'P':
+ {
+ Bufpos toppos = marker_position (w->start[type]);
+ Bufpos botpos = BUF_Z (b) - w->window_end_pos[type];
+
+ /* botpos is only accurate as of the last redisplay, so we can
+ only treat it as a hint. In particular, after erase-buffer,
+ botpos may be negative. */
+ if (botpos < toppos)
+ botpos = toppos;
+
+ if (botpos >= BUF_ZV (b))
+ {
+ if (toppos <= BUF_BEGV (b))
+ str = "All";
+ else
+ str = "Bottom";
+ }
+ else
+ {
+ /* This hard limit is ok since the string it will hold has a
+ fixed maximum length of around 6. But just to be safe... */
+ char buf[10];
+ Charcount chars = botpos - BUF_BEGV (b);
+ Charcount total = BUF_ZV (b) - BUF_BEGV (b);
+
+ /* Avoid overflow on big buffers */
+ int percent = total > LONG_MAX/200 ?
+ (chars + total/200) / (total / 100) :
+ (chars * 100 + total/2) / max (total, 1);
+
+ /* We can't normally display a 3-digit number, so get us a
+ 2-digit number that is close. */
+ if (percent == 100)
+ percent = 99;
+
+ if (toppos <= BUF_BEGV (b))
+ sprintf (buf, "Top%d%%", percent);
+ else
+ sprintf (buf, "%d%%", percent);
+
+ Dynarr_add_many (mode_spec_bufbyte_string, (Bufbyte *) buf,
+ strlen (buf));
+
+ goto decode_mode_spec_done;
+ }
+ break;
+ }
+
+ /* print % */
+ case '%':
+ str = "%";
+ break;
+
+ /* print one [ for each recursive editing level. */
+ case '[':
+ {
+ int i;
+
+ if (command_loop_level > 5)
+ {
+ str = "[[[... ";
+ break;
+ }
+
+ for (i = 0; i < command_loop_level; i++)
+ Dynarr_add (mode_spec_bufbyte_string, '[');
+
+ goto decode_mode_spec_done;
+ }
+
+ /* print one ] for each recursive editing level. */
+ case ']':
+ {
+ int i;
+
+ if (command_loop_level > 5)
+ {
+ str = "...]]]";
+ break;
+ }
+
+ for (i = 0; i < command_loop_level; i++)
+ Dynarr_add (mode_spec_bufbyte_string, ']');
+
+ goto decode_mode_spec_done;
+ }
+
+ /* print infinitely many dashes -- handle at top level now */
+ case '-':
+ break;
+
+ }
+
+ if (STRINGP (obj))
+ Dynarr_add_many (mode_spec_bufbyte_string,
+ XSTRING_DATA (obj),
+ XSTRING_LENGTH (obj));
+ else if (str)
+ Dynarr_add_many (mode_spec_bufbyte_string, (Bufbyte *) str, strlen (str));
+
+decode_mode_spec_done:
+ Dynarr_add (mode_spec_bufbyte_string, '\0');
+}
+
+/* Given a display line, free all of its data structures. */
+
+static void
+free_display_line (struct display_line *dl)
+{
+ int block;
+
+ if (dl->display_blocks)
+ {
+ for (block = 0; block < Dynarr_largest (dl->display_blocks); block++)
+ {
+ struct display_block *db = Dynarr_atp (dl->display_blocks, block);
+
+ Dynarr_free (db->runes);
+ }
+
+ Dynarr_free (dl->display_blocks);
+ dl->display_blocks = NULL;
+ }
+
+ if (dl->left_glyphs)
+ {
+ Dynarr_free (dl->left_glyphs);
+ dl->left_glyphs = NULL;
+ }
+
+ if (dl->right_glyphs)
+ {
+ Dynarr_free (dl->right_glyphs);
+ dl->right_glyphs = NULL;
+ }
+}
+
+
+/* Given an array of display lines, free them and all data structures
+ contained within them. */
+
+void
+free_display_lines (display_line_dynarr *dla)
+{
+ int line;
+
+ for (line = 0; line < Dynarr_largest (dla); line++)
+ {
+ free_display_line (Dynarr_atp (dla, line));
+ }
+
+ Dynarr_free (dla);
+}
+
+/* Call internal free routine for each set of display lines. */
+
+void
+free_display_structs (struct window_mirror *mir)
+{
+ if (mir->current_display_lines)
+ {
+ free_display_lines (mir->current_display_lines);
+ mir->current_display_lines = 0;
+ }
+
+ if (mir->desired_display_lines)
+ {
+ free_display_lines (mir->desired_display_lines);
+ mir->desired_display_lines = 0;
+ }
+}
+\f
+
+static void
+mark_glyph_block_dynarr (glyph_block_dynarr *gba)
+{
+ if (gba)
+ {
+ glyph_block *gb = Dynarr_atp (gba, 0);
+ glyph_block *gb_last = Dynarr_atp (gba, Dynarr_length (gba));
+
+ for (; gb < gb_last; gb++)
+ {
+ if (!NILP (gb->glyph))
+ mark_object (gb->glyph);
+ if (!NILP (gb->extent))
+ mark_object (gb->extent);
+ }
+ }
+}
+
+/* See the comment in image_instantiate_cache_result as to why marking
+ the glyph will also mark the image_instance. */
+void
+mark_redisplay_structs (display_line_dynarr *dla)
+{
+ display_line *dl = Dynarr_atp (dla, 0);
+ display_line *dl_last = Dynarr_atp (dla, Dynarr_length (dla));
+
+ for (; dl < dl_last; dl++)
+ {
+ display_block_dynarr *dba = dl->display_blocks;
+ display_block *db = Dynarr_atp (dba, 0);
+ display_block *db_last = Dynarr_atp (dba, Dynarr_length (dba));
+
+ for (; db < db_last; db++)
+ {
+ rune_dynarr *ra = db->runes;
+ rune *r = Dynarr_atp (ra, 0);
+ rune *r_last = Dynarr_atp (ra, Dynarr_length (ra));
+
+ for (; r < r_last; r++)
+ {
+ if (r->type == RUNE_DGLYPH)
+ {
+ if (!NILP (r->object.dglyph.glyph))
+ mark_object (r->object.dglyph.glyph);
+ if (!NILP (r->object.dglyph.extent))
+ mark_object (r->object.dglyph.extent);
+ }
+ }
+ }
+
+ mark_glyph_block_dynarr (dl->left_glyphs);
+ mark_glyph_block_dynarr (dl->right_glyphs);
+ }
+}
+
+static void
+mark_window_mirror (struct window_mirror *mir)
+{
+ mark_redisplay_structs (mir->current_display_lines);
+ mark_redisplay_structs (mir->desired_display_lines);
+
+ if (mir->next)
+ mark_window_mirror (mir->next);
+
+ if (mir->hchild)
+ mark_window_mirror (mir->hchild);
+ else if (mir->vchild)
+ mark_window_mirror (mir->vchild);
+}
+
+void
+mark_redisplay (void)
+{
+ Lisp_Object frmcons, devcons, concons;
+
+ FRAME_LOOP_NO_BREAK (frmcons, devcons, concons)
+ {
+ struct frame *f = XFRAME (XCAR (frmcons));
+ mark_window_mirror (f->root_mirror);
+ mark_gutters (f);
+ }
+}
+\f
+/*****************************************************************************
+ Line Start Cache Description and Rationale
+
+ The traditional scrolling code in Emacs breaks in a variable height world.
+ It depends on the key assumption that the number of lines that can be
+ displayed at any given time is fixed. This led to a complete separation
+ of the scrolling code from the redisplay code. In order to fully support
+ variable height lines, the scrolling code must actually be tightly
+ integrated with redisplay. Only redisplay can determine how many lines
+ will be displayed on a screen for any given starting point.
+
+ What is ideally wanted is a complete list of the starting buffer position
+ for every possible display line of a buffer along with the height of that
+ display line. Maintaining such a full list would be very expensive. We
+ settle for having it include information for all areas which we happen to
+ generate anyhow (i.e. the region currently being displayed) and for those
+ areas we need to work with.
+
+ In order to ensure that the cache accurately represents what redisplay
+ would actually show, it is necessary to invalidate it in many situations.
+ If the buffer changes, the starting positions may no longer be correct.
+ If a face or an extent has changed then the line heights may have altered.
+ These events happen frequently enough that the cache can end up being
+ constantly disabled. With this potentially constant invalidation when is
+ the cache ever useful?
+
+ Even if the cache is invalidated before every single usage, it is
+ necessary. Scrolling often requires knowledge about display lines which
+ are actually above or below the visible region. The cache provides a
+ convenient light-weight method of storing this information for multiple
+ display regions. This knowledge is necessary for the scrolling code to
+ always obey the First Golden Rule of Redisplay.
+
+ If the cache already contains all of the information that the scrolling
+ routines happen to need so that it doesn't have to go generate it, then we
+ are able to obey the Third Golden Rule of Redisplay. The first thing we
+ do to help out the cache is to always add the displayed region. This
+ region had to be generated anyway, so the cache ends up getting the
+ information basically for free. In those cases where a user is simply
+ scrolling around viewing a buffer there is a high probability that this is
+ sufficient to always provide the needed information. The second thing we
+ can do is be smart about invalidating the cache.
+
+ TODO -- Be smart about invalidating the cache. Potential places:
+
+ + Insertions at end-of-line which don't cause line-wraps do not alter the
+ starting positions of any display lines. These types of buffer
+ modifications should not invalidate the cache. This is actually a large
+ optimization for redisplay speed as well.
+
+ + Buffer modifications frequently only affect the display of lines at and
+ below where they occur. In these situations we should only invalidate
+ the part of the cache starting at where the modification occurs.
+
+ In case you're wondering, the Second Golden Rule of Redisplay is not
+ applicable.
+ ****************************************************************************/
+
+/* This will get used quite a bit so we don't want to be constantly
+ allocating and freeing it. */
+static line_start_cache_dynarr *internal_cache;
+
+/* Makes internal_cache represent the TYPE display structs and only
+ the TYPE display structs. */
+
+static void
+update_internal_cache_list (struct window *w, int type)
+{
+ int line;
+ display_line_dynarr *dla = window_display_lines (w, type);
+
+ Dynarr_reset (internal_cache);
+ for (line = 0; line < Dynarr_length (dla); line++)
+ {
+ struct display_line *dl = Dynarr_atp (dla, line);
+
+ if (dl->modeline)
+ continue;
+ else
+ {
+ struct line_start_cache lsc;
+
+ lsc.start = dl->bufpos;
+ lsc.end = dl->end_bufpos;
+ lsc.height = dl->ascent + dl->descent;
+
+ Dynarr_add (internal_cache, lsc);
+ }
+ }
+}
+
+/* Reset the line cache if necessary. This should be run at the
+ beginning of any function which access the cache. */
+
+static void
+validate_line_start_cache (struct window *w)
+{
+ struct buffer *b = XBUFFER (w->buffer);
+ struct frame *f = XFRAME (w->frame);
+
+ if (!w->line_cache_validation_override)
+ {
+ /* f->extents_changed used to be in here because extent face and
+ size changes can cause text shifting. However, the extent
+ covering the region is constantly having its face set and
+ priority altered by the mouse code. This means that the line
+ start cache is constantly being invalidated. This is bad
+ since the mouse code also triggers heavy usage of the cache.
+ Since it is an unlikely that f->extents being changed
+ indicates that the cache really needs to be updated and if it
+ does redisplay will catch it pretty quickly we no longer
+ invalidate the cache if it is set. This greatly speeds up
+ dragging out regions with the mouse. */
+ if (XINT (w->line_cache_last_updated) < BUF_MODIFF (b)
+ || f->faces_changed
+ || f->clip_changed)
+ {
+ Dynarr_reset (w->line_start_cache);
+ }
+ }
+}
+
+/* Return the very first buffer position contained in the given
+ window's cache, or -1 if the cache is empty. Assumes that the
+ cache is valid. */
+
+static Bufpos
+line_start_cache_start (struct window *w)
+{
+ line_start_cache_dynarr *cache = w->line_start_cache;
+
+ if (!Dynarr_length (cache))
+ return -1;
+ else
+ return Dynarr_atp (cache, 0)->start;
+}
+
+/* Return the very last buffer position contained in the given
+ window's cache, or -1 if the cache is empty. Assumes that the
+ cache is valid. */
+
+static Bufpos
+line_start_cache_end (struct window *w)
+{
+ line_start_cache_dynarr *cache = w->line_start_cache;
+
+ if (!Dynarr_length (cache))
+ return -1;
+ else
+ return Dynarr_atp (cache, Dynarr_length (cache) - 1)->end;
+}
+
+/* Return the index of the line POINT is contained within in window
+ W's line start cache. It will enlarge the cache or move the cache
+ window in order to have POINT be present in the cache. MIN_PAST is
+ a guarantee of the number of entries in the cache present on either
+ side of POINT (unless a buffer boundary is hit). If MIN_PAST is -1
+ then it will be treated as 0, but the cache window will not be
+ allowed to shift. Returns -1 if POINT cannot be found in the cache
+ for any reason. */
+
+int
+point_in_line_start_cache (struct window *w, Bufpos point, int min_past)
+{
+ struct buffer *b = XBUFFER (w->buffer);
+ line_start_cache_dynarr *cache = w->line_start_cache;
+ unsigned int top, bottom;
+ EMACS_INT pos;
+
+ validate_line_start_cache (w);
+ w->line_cache_validation_override++;
+
+ /* Let functions pass in negative values, but we still treat -1
+ specially. */
+ /* #### bogosity alert */
+ if (min_past < 0 && min_past != -1)
+ min_past = -min_past;
+
+ if (!Dynarr_length (cache) || line_start_cache_start (w) > point
+ || line_start_cache_end (w) < point)
+ {
+ int loop;
+ int win_char_height = window_char_height (w, 1);
+
+ /* Occasionally we get here with a 0 height
+ window. find_next_newline_no_quit will ABORT if we pass it a
+ count of 0 so handle that case. */
+ if (!win_char_height)
+ win_char_height = 1;
+
+ if (!Dynarr_length (cache))
+ {
+ Bufpos from = find_next_newline_no_quit (b, point, -1);
+ Bufpos to = find_next_newline_no_quit (b, from, win_char_height);
+
+ update_line_start_cache (w, from, to, point, 0);
+
+ if (!Dynarr_length (cache))
+ {
+ w->line_cache_validation_override--;
+ return -1;
+ }
+ }
+
+ assert (Dynarr_length (cache));
+
+ loop = 0;
+ while (line_start_cache_start (w) > point
+ && (loop < cache_adjustment || min_past == -1))
+ {
+ Bufpos from, to;
+
+ from = line_start_cache_start (w);
+ if (from <= BUF_BEGV (b))
+ break;
+
+ from = find_next_newline_no_quit (b, from, -win_char_height);
+ to = line_start_cache_end (w);
+
+ update_line_start_cache (w, from, to, point, 0);
+ loop++;
+ }
+
+ if (line_start_cache_start (w) > point)
+ {
+ Bufpos from, to;
+
+ from = find_next_newline_no_quit (b, point, -1);
+ if (from >= BUF_ZV (b))
+ {
+ to = find_next_newline_no_quit (b, from, -win_char_height);
+ from = to;
+ to = BUF_ZV (b);
+ }
+ else
+ to = find_next_newline_no_quit (b, from, win_char_height);
+
+ update_line_start_cache (w, from, to, point, 0);
+ }
+
+ loop = 0;
+ while (line_start_cache_end (w) < point
+ && (loop < cache_adjustment || min_past == -1))
+ {
+ Bufpos from, to;
+
+ to = line_start_cache_end (w);
+ if (to >= BUF_ZV (b))
+ break;
+
+ from = line_start_cache_end (w);
+ to = find_next_newline_no_quit (b, from, win_char_height);
+
+ update_line_start_cache (w, from, to, point, 0);
+ loop++;
+ }
+
+ if (line_start_cache_end (w) < point)
+ {
+ Bufpos from, to;
+
+ from = find_next_newline_no_quit (b, point, -1);
+ if (from >= BUF_ZV (b))
+ {
+ to = find_next_newline_no_quit (b, from, -win_char_height);
+ from = to;
+ to = BUF_ZV (b);
+ }
+ else
+ to = find_next_newline_no_quit (b, from, win_char_height);
+
+ update_line_start_cache (w, from, to, point, 0);
+ }
+ }
+
+ assert (Dynarr_length (cache));
+
+ if (min_past == -1)
+ min_past = 0;
+
+ /* This could happen if the buffer is narrowed. */
+ if (line_start_cache_start (w) > point
+ || line_start_cache_end (w) < point)
+ {
+ w->line_cache_validation_override--;
+ return -1;
+ }
+
+find_point_loop:
+
+ top = Dynarr_length (cache) - 1;
+ bottom = 0;
+
+ while (1)
+ {
+ EMACS_INT new_pos;
+ Bufpos start, end;
+
+ pos = (bottom + top + 1) >> 1;
+ start = Dynarr_atp (cache, pos)->start;
+ end = Dynarr_atp (cache, pos)->end;
+
+ if (point >= start && point <= end)
+ {
+ if (pos < min_past && line_start_cache_start (w) > BUF_BEGV (b))
+ {
+ Bufpos from =
+ find_next_newline_no_quit (b, line_start_cache_start (w),
+ -min_past - 1);
+ Bufpos to = line_start_cache_end (w);
+
+ update_line_start_cache (w, from, to, point, 0);
+ goto find_point_loop;
+ }
+ else if ((Dynarr_length (cache) - pos - 1) < min_past
+ && line_start_cache_end (w) < BUF_ZV (b))
+ {
+ Bufpos from = line_start_cache_end (w);
+ Bufpos to = find_next_newline_no_quit (b, from,
+ (min_past
+ ? min_past
+ : 1));
+
+ update_line_start_cache (w, from, to, point, 0);
+ goto find_point_loop;
+ }
+ else
+ {
+ w->line_cache_validation_override--;
+ return pos;
+ }
+ }
+ else if (point > end)
+ bottom = pos + 1;
+ else if (point < start)
+ top = pos - 1;
+ else
+ ABORT ();
+
+ new_pos = (bottom + top + 1) >> 1;
+ if (pos == new_pos)
+ {
+ w->line_cache_validation_override--;
+ return -1;
+ }
+ }
+}
+
+/* Return a boolean indicating if POINT would be visible in window W
+ if display of the window was to begin at STARTP. */
+
+int
+point_would_be_visible (struct window *w, Bufpos startp, Bufpos point)
+{
+ struct buffer *b = XBUFFER (w->buffer);
+ int pixpos = -WINDOW_TEXT_TOP_CLIP(w);
+ int bottom = WINDOW_TEXT_HEIGHT (w);
+ int start_elt;
+
+ /* If point is before the intended start it obviously can't be visible. */
+ if (point < startp)
+ return 0;
+
+ /* If point or start are not in the accessible buffer range, then
+ fail. */
+ if (startp < BUF_BEGV (b) || startp > BUF_ZV (b)
+ || point < BUF_BEGV (b) || point > BUF_ZV (b))
+ return 0;
+
+ validate_line_start_cache (w);
+ w->line_cache_validation_override++;
+
+ start_elt = point_in_line_start_cache (w, startp, 0);
+ if (start_elt == -1)
+ {
+ w->line_cache_validation_override--;
+ return 0;
+ }
+
+ assert (line_start_cache_start (w) <= startp
+ && line_start_cache_end (w) >= startp);
+
+ while (1)
+ {
+ int height;
+
+ /* Expand the cache if necessary. */
+ if (start_elt == Dynarr_length (w->line_start_cache))
+ {
+ Bufpos old_startp =
+ Dynarr_atp (w->line_start_cache, start_elt - 1)->start;
+
+ start_elt = point_in_line_start_cache (w, old_startp,
+ window_char_height (w, 0));
+
+ /* We've already actually processed old_startp, so increment
+ immediately. */
+ start_elt++;
+
+ /* If this happens we didn't add any extra elements. Bummer. */
+ if (start_elt == Dynarr_length (w->line_start_cache))
+ {
+ w->line_cache_validation_override--;
+ return 0;
+ }
+ }
+
+ height = Dynarr_atp (w->line_start_cache, start_elt)->height;
+
+ if (pixpos + height > bottom)
+ {
+ if (bottom - pixpos < VERTICAL_CLIP (w, 0))
+ {
+ w->line_cache_validation_override--;
+ return 0;
+ }
+ }
+
+ pixpos += height;
+ if (point <= Dynarr_atp (w->line_start_cache, start_elt)->end)
+ {
+ w->line_cache_validation_override--;
+ return 1;
+ }
+
+ start_elt++;
+ }
+}
+
+/* For the given window W, if display starts at STARTP, what will be
+ the buffer position at the beginning or end of the last line
+ displayed. The end of the last line is also know as the window end
+ position.
+
+ WARNING: It is possible that redisplay failed to layout any lines for the
+ windows. Under normal circumstances this is rare. However it seems that it
+ does occur in the following situation: A mouse event has come in and we
+ need to compute its location in a window. That code (in
+ pixel_to_glyph_translation) already can handle 0 as an error return value.
+
+ #### With a little work this could probably be reworked as just a
+ call to start_with_line_at_pixpos. */
+
+static Bufpos
+start_end_of_last_line (struct window *w, Bufpos startp, int end,
+ int may_error)
+{
+ struct buffer *b = XBUFFER (w->buffer);
+ line_start_cache_dynarr *cache = w->line_start_cache;
+ int pixpos = 0;
+ int bottom = WINDOW_TEXT_HEIGHT (w);
+ Bufpos cur_start;
+ int start_elt;
+
+ validate_line_start_cache (w);
+ w->line_cache_validation_override++;
+
+ if (startp < BUF_BEGV (b))
+ startp = BUF_BEGV (b);
+ else if (startp > BUF_ZV (b))
+ startp = BUF_ZV (b);
+ cur_start = startp;
+
+ start_elt = point_in_line_start_cache (w, cur_start, 0);
+ if (start_elt == -1)
+ return may_error ? 0 : startp;
+
+ while (1)
+ {
+ int height = Dynarr_atp (cache, start_elt)->height;
+
+ cur_start = Dynarr_atp (cache, start_elt)->start;
+
+ if (pixpos + height > bottom)
+ {
+ /* Adjust for any possible clip. */
+ if (bottom - pixpos < VERTICAL_CLIP (w, 0))
+ start_elt--;
+
+ if (start_elt < 0)
+ {
+ w->line_cache_validation_override--;
+ if (end)
+ return BUF_ZV (b);
+ else
+ return BUF_BEGV (b);
+ }
+ else
+ {
+ w->line_cache_validation_override--;
+ if (end)
+ return Dynarr_atp (cache, start_elt)->end;
+ else
+ return Dynarr_atp (cache, start_elt)->start;
+ }
+ }
+
+ pixpos += height;
+ start_elt++;
+ if (start_elt == Dynarr_length (cache))
+ {
+ Bufpos from = line_start_cache_end (w);
+ int win_char_height = window_char_height (w, 0);
+ Bufpos to = find_next_newline_no_quit (b, from,
+ (win_char_height
+ ? win_char_height
+ : 1));
+
+ /* We've hit the end of the bottom so that's what it is. */
+ if (from >= BUF_ZV (b))
+ {
+ w->line_cache_validation_override--;
+ return BUF_ZV (b);
+ }
+
+ update_line_start_cache (w, from, to, BUF_PT (b), 0);
+
+ /* Updating the cache invalidates any current indexes. */
+ start_elt = point_in_line_start_cache (w, cur_start, -1) + 1;
+ }
+ }
+}
+
+/* For the given window W, if display starts at STARTP, what will be
+ the buffer position at the beginning of the last line displayed. */
+
+Bufpos
+start_of_last_line (struct window *w, Bufpos startp)
+{
+ return start_end_of_last_line (w, startp, 0 , 0);
+}
+
+/* For the given window W, if display starts at STARTP, what will be
+ the buffer position at the end of the last line displayed. This is
+ also know as the window end position. */
+
+Bufpos
+end_of_last_line (struct window *w, Bufpos startp)
+{
+ return start_end_of_last_line (w, startp, 1, 0);
+}
+
+static Bufpos
+end_of_last_line_may_error (struct window *w, Bufpos startp)
+{
+ return start_end_of_last_line (w, startp, 1, 1);
+}
+
+
+/* For window W, what does the starting position have to be so that
+ the line containing POINT will cover pixel position PIXPOS. */
+
+Bufpos
+start_with_line_at_pixpos (struct window *w, Bufpos point, int pixpos)
+{
+ struct buffer *b = XBUFFER (w->buffer);
+ int cur_elt;
+ Bufpos cur_pos, prev_pos = point;
+ int point_line_height;
+ int pixheight = pixpos - WINDOW_TEXT_TOP (w);
+
+ validate_line_start_cache (w);
+ w->line_cache_validation_override++;
+
+ cur_elt = point_in_line_start_cache (w, point, 0);
+ /* #### See comment in update_line_start_cache about big minibuffers. */
+ if (cur_elt < 0)
+ {
+ w->line_cache_validation_override--;
+ return point;
+ }
+
+ point_line_height = Dynarr_atp (w->line_start_cache, cur_elt)->height;
+
+ while (1)
+ {
+ cur_pos = Dynarr_atp (w->line_start_cache, cur_elt)->start;
+
+ pixheight -= Dynarr_atp (w->line_start_cache, cur_elt)->height;
+
+ /* Do not take into account the value of vertical_clip here.
+ That is the responsibility of the calling functions. */
+ if (pixheight < 0)
+ {
+ w->line_cache_validation_override--;
+ if (-pixheight > point_line_height)
+ /* We can't make the target line cover pixpos, so put it
+ above pixpos. That way it will at least be visible. */
+ return prev_pos;
+ else
+ return cur_pos;
+ }
+
+ cur_elt--;
+ while (cur_elt < 0)
+ {
+ Bufpos from, to;
+ int win_char_height;
+
+ if (cur_pos <= BUF_BEGV (b))
+ {
+ w->line_cache_validation_override--;
+ return BUF_BEGV (b);
+ }
+
+ win_char_height = window_char_height (w, 0);
+ if (!win_char_height)
+ win_char_height = 1;
+
+ from = find_next_newline_no_quit (b, cur_pos, -win_char_height);
+ to = line_start_cache_end (w);
+ update_line_start_cache (w, from, to, point, 0);
+
+ cur_elt = point_in_line_start_cache (w, cur_pos, 2) - 1;
+ assert (cur_elt >= -1);
+ /* This used to be cur_elt>=0 under the assumption that if
+ point is in the top line and not at BUF_BEGV, then
+ setting the window_start to a newline before the start of
+ the first line will always cause scrolling.
+
+ However in my (jv) opinion this is wrong. That new line
+ can be hidden in various ways: invisible extents, an
+ explicit window-start not at a newline character etc.
+ The existence of those are indeed known to create crashes
+ on that assert. So we have no option but to continue the
+ search if we found point at the top of the line_start_cache
+ again. */
+ cur_pos = Dynarr_atp (w->line_start_cache,0)->start;
+ }
+ prev_pos = cur_pos;
+ }
+}
+
+/* For window W, what does the starting position have to be so that
+ the line containing point is on display line LINE. If LINE is
+ positive it is considered to be the number of lines from the top of
+ the window (0 is the top line). If it is negative the number is
+ considered to be the number of lines from the bottom (-1 is the
+ bottom line). */
+
+Bufpos
+start_with_point_on_display_line (struct window *w, Bufpos point, int line)
+{
+ validate_line_start_cache (w);
+ w->line_cache_validation_override++;
+
+ if (line >= 0)
+ {
+ int cur_elt = point_in_line_start_cache (w, point, line);
+
+ if (cur_elt - line < 0)
+ cur_elt = 0; /* Hit the top */
+ else
+ cur_elt -= line;
+
+ w->line_cache_validation_override--;
+ return Dynarr_atp (w->line_start_cache, cur_elt)->start;
+ }
+ else
+ {
+ /* The calculated value of pixpos is correct for the bottom line
+ or what we want when line is -1. Therefore we subtract one
+ because we have already handled one line. */
+ int new_line = -line - 1;
+ int cur_elt = point_in_line_start_cache (w, point, new_line);
+ int pixpos = WINDOW_TEXT_BOTTOM (w);
+ Bufpos retval, search_point;
+
+ /* If scroll_on_clipped_lines is false, the last "visible" line of
+ the window covers the pixel at WINDOW_TEXT_BOTTOM (w) - 1.
+ If s_o_c_l is true, then we don't want to count a clipped
+ line, so back up from the bottom by the height of the line
+ containing point. */
+ if (scroll_on_clipped_lines)
+ pixpos -= Dynarr_atp (w->line_start_cache, cur_elt)->height;
+ else
+ pixpos -= 1;
+
+ if (cur_elt + new_line >= Dynarr_length (w->line_start_cache))
+ {
+ /* Hit the bottom of the buffer. */
+ int adjustment =
+ (cur_elt + new_line) - Dynarr_length (w->line_start_cache) + 1;
+ Lisp_Object window;
+ int defheight;
+
+ XSETWINDOW (window, w);
+ default_face_height_and_width (window, &defheight, 0);
+
+ cur_elt = Dynarr_length (w->line_start_cache) - 1;
+
+ pixpos -= (adjustment * defheight);
+ if (pixpos < WINDOW_TEXT_TOP (w))
+ pixpos = WINDOW_TEXT_TOP (w);
+ }
+ else
+ cur_elt = cur_elt + new_line;
+
+ search_point = Dynarr_atp (w->line_start_cache, cur_elt)->start;
+
+ retval = start_with_line_at_pixpos (w, search_point, pixpos);
+ w->line_cache_validation_override--;
+ return retval;
+ }
+}
+
+/* This is used to speed up vertical scrolling by caching the known
+ buffer starting positions for display lines. This allows the
+ scrolling routines to avoid costly calls to regenerate_window. If
+ NO_REGEN is true then it will only add the values in the DESIRED
+ display structs which are in the given range.
+
+ Note also that the FROM/TO values are minimums. It is possible
+ that this function will actually add information outside of the
+ lines containing those positions. This can't hurt but it could
+ possibly help.
+
+ #### We currently force the cache to have only 1 contiguous region.
+ It might help to make the cache a dynarr of caches so that we can
+ cover more areas. This might, however, turn out to be a lot of
+ overhead for too little gain. */
+
+static void
+update_line_start_cache (struct window *w, Bufpos from, Bufpos to,
+ Bufpos point, int no_regen)
+{
+ struct buffer *b = XBUFFER (w->buffer);
+ line_start_cache_dynarr *cache = w->line_start_cache;
+ Bufpos low_bound, high_bound;
+
+ validate_line_start_cache (w);
+ w->line_cache_validation_override++;
+
+ if (from < BUF_BEGV (b))
+ from = BUF_BEGV (b);
+ if (to > BUF_ZV (b))
+ to = BUF_ZV (b);
+
+ if (from > to)
+ {
+ w->line_cache_validation_override--;
+ return;
+ }
+
+ if (Dynarr_length (cache))
+ {
+ low_bound = line_start_cache_start (w);
+ high_bound = line_start_cache_end (w);
+
+ /* Check to see if the desired range is already in the cache. */
+ if (from >= low_bound && to <= high_bound)
+ {
+ w->line_cache_validation_override--;
+ return;
+ }
+
+ /* Check to make sure that the desired range is adjacent to the
+ current cache. If not, invalidate the cache. */
+ if (to < low_bound || from > high_bound)
+ {
+ Dynarr_reset (cache);
+ low_bound = high_bound = -1;
+ }
+ }
+ else
+ {
+ low_bound = high_bound = -1;
+ }
+
+ w->line_cache_last_updated = make_int (BUF_MODIFF (b));
+
+ /* This could be integrated into the next two sections, but it is easier
+ to follow what's going on by having it separate. */
+ if (no_regen)
+ {
+ Bufpos start, end;
+
+ update_internal_cache_list (w, DESIRED_DISP);
+ if (!Dynarr_length (internal_cache))
+ {
+ w->line_cache_validation_override--;
+ return;
+ }
+
+ start = Dynarr_atp (internal_cache, 0)->start;
+ end =
+ Dynarr_atp (internal_cache, Dynarr_length (internal_cache) - 1)->end;
+
+ /* We aren't allowed to generate additional information to fill in
+ gaps, so if the DESIRED structs don't overlap the cache, reset the
+ cache. */
+ if (Dynarr_length (cache))
+ {
+ if (end < low_bound || start > high_bound)
+ Dynarr_reset (cache);
+
+ /* #### What should really happen if what we are doing is
+ extending a line (the last line)? */
+ if (Dynarr_length (cache) == 1
+ && Dynarr_length (internal_cache) == 1)
+ Dynarr_reset (cache);
+ }
+
+ if (!Dynarr_length (cache))
+ {
+ Dynarr_add_many (cache, Dynarr_atp (internal_cache, 0),
+ Dynarr_length (internal_cache));
+ w->line_cache_validation_override--;
+ return;
+ }
+
+ /* An extra check just in case the calling function didn't pass in
+ the bounds of the DESIRED structs in the first place. */
+ if (start >= low_bound && end <= high_bound)
+ {
+ w->line_cache_validation_override--;
+ return;
+ }
+
+ /* At this point we know that the internal cache partially overlaps
+ the main cache. */
+ if (start < low_bound)
+ {
+ int ic_elt = Dynarr_length (internal_cache) - 1;
+ while (ic_elt >= 0)
+ {
+ if (Dynarr_atp (internal_cache, ic_elt)->start < low_bound)
+ break;
+ else
+ ic_elt--;
+ }
+
+ if (!(ic_elt >= 0))
+ {
+ Dynarr_reset (cache);
+ Dynarr_add_many (cache, Dynarr_atp (internal_cache, 0),
+ Dynarr_length (internal_cache));
+ w->line_cache_validation_override--;
+ return;
+ }
+
+ Dynarr_insert_many_at_start (cache, Dynarr_atp (internal_cache, 0),
+ ic_elt + 1);
+ }
+
+ if (end > high_bound)
+ {
+ int ic_elt = 0;
+
+ while (ic_elt < Dynarr_length (internal_cache))
+ {
+ if (Dynarr_atp (internal_cache, ic_elt)->start > high_bound)
+ break;
+ else
+ ic_elt++;
+ }
+
+ if (!(ic_elt < Dynarr_length (internal_cache)))
+ {
+ Dynarr_reset (cache);
+ Dynarr_add_many (cache, Dynarr_atp (internal_cache, 0),
+ Dynarr_length (internal_cache));
+ w->line_cache_validation_override--;
+ return;
+ }
+
+ Dynarr_add_many (cache, Dynarr_atp (internal_cache, ic_elt),
+ Dynarr_length (internal_cache) - ic_elt);
+ }
+
+ w->line_cache_validation_override--;
+ return;
+ }
+
+ if (!Dynarr_length (cache) || from < low_bound)
+ {
+ Bufpos startp = find_next_newline_no_quit (b, from, -1);
+ int marker = 0;
+ int old_lb = low_bound;
+
+ while (startp < old_lb || low_bound == -1)
+ {
+ int ic_elt;
+ Bufpos new_startp;
+
+ regenerate_window (w, startp, point, CMOTION_DISP);
+ update_internal_cache_list (w, CMOTION_DISP);
+
+ /* If this assert is triggered then regenerate_window failed
+ to layout a single line. This is not possible since we
+ force at least a single line to be layout for CMOTION_DISP */
+ assert (Dynarr_length (internal_cache));
+ assert (startp == Dynarr_atp (internal_cache, 0)->start);
+
+ ic_elt = Dynarr_length (internal_cache) - 1;
+ if (low_bound != -1)
+ {
+ while (ic_elt >= 0)
+ {
+ if (Dynarr_atp (internal_cache, ic_elt)->start < old_lb)
+ break;
+ else
+ ic_elt--;
+ }
+ }
+ assert (ic_elt >= 0);
+
+ new_startp = Dynarr_atp (internal_cache, ic_elt)->end + 1;
+
+ /*
+ * Handle invisible text properly:
+ * If the last line we're inserting has the same end as the
+ * line before which it will be added, merge the two lines.
+ */
+ if (Dynarr_length (cache) &&
+ Dynarr_atp (internal_cache, ic_elt)->end ==
+ Dynarr_atp (cache, marker)->end)
+ {
+ Dynarr_atp (cache, marker)->start
+ = Dynarr_atp (internal_cache, ic_elt)->start;
+ Dynarr_atp (cache, marker)->height
+ = Dynarr_atp (internal_cache, ic_elt)->height;
+ ic_elt--;
+ }
+
+ if (ic_elt >= 0) /* we still have lines to add.. */
+ {
+ Dynarr_insert_many (cache, Dynarr_atp (internal_cache, 0),
+ ic_elt + 1, marker);
+ marker += (ic_elt + 1);
+ }
+
+ if (startp < low_bound || low_bound == -1)
+ low_bound = startp;
+ startp = new_startp;
+ if (startp > BUF_ZV (b))
+ {
+ w->line_cache_validation_override--;
+ return;
+ }
+ }
+ }
+
+ assert (Dynarr_length (cache));
+ assert (from >= low_bound);
+
+ /* Readjust the high_bound to account for any changes made while
+ correcting the low_bound. */
+ high_bound = Dynarr_atp (cache, Dynarr_length (cache) - 1)->end;
+
+ if (to > high_bound)
+ {
+ Bufpos startp = Dynarr_atp (cache, Dynarr_length (cache) - 1)->end + 1;
+
+ do
+ {
+ regenerate_window (w, startp, point, CMOTION_DISP);
+ update_internal_cache_list (w, CMOTION_DISP);
+
+ /* See comment above about regenerate_window failing. */
+ assert (Dynarr_length (internal_cache));
+
+ Dynarr_add_many (cache, Dynarr_atp (internal_cache, 0),
+ Dynarr_length (internal_cache));
+ high_bound = Dynarr_atp (cache, Dynarr_length (cache) - 1)->end;
+ startp = high_bound + 1;
+ }
+ while (to > high_bound);
+ }
+
+ w->line_cache_validation_override--;
+ assert (to <= high_bound);
+}
+
+\f
+/* Given x and y coordinates in characters, relative to a window,
+ return the pixel location corresponding to those coordinates. The
+ pixel location returned is the center of the given character
+ position. The pixel values are generated relative to the window,
+ not the frame.
+
+ The modeline is considered to be part of the window. */
+
+void
+glyph_to_pixel_translation (struct window *w, int char_x, int char_y,
+ int *pix_x, int *pix_y)
+{
+ display_line_dynarr *dla = window_display_lines (w, CURRENT_DISP);
+ int num_disp_lines, modeline;
+ Lisp_Object window;
+ int defheight, defwidth;
+
+ XSETWINDOW (window, w);
+ default_face_height_and_width (window, &defheight, &defwidth);
+
+ /* If we get a bogus value indicating somewhere above or to the left of
+ the window, use the first window line or character position
+ instead. */
+ if (char_y < 0)
+ char_y = 0;
+ if (char_x < 0)
+ char_x = 0;
+
+ num_disp_lines = Dynarr_length (dla);
+ modeline = 0;
+ if (num_disp_lines)
+ {
+ if (Dynarr_atp (dla, 0)->modeline)
+ {
+ num_disp_lines--;
+ modeline = 1;
+ }
+ }
+
+ /* First check if the y position intersects the display lines. */
+ if (char_y < num_disp_lines)
+ {
+ struct display_line *dl = Dynarr_atp (dla, char_y + modeline);
+ struct display_block *db = get_display_block_from_line (dl, TEXT);
+
+ *pix_y = (dl->ypos - dl->ascent +
+ ((unsigned int) (dl->ascent + dl->descent - dl->clip) >> 1));
+
+ if (char_x < Dynarr_length (db->runes))
+ {
+ struct rune *rb = Dynarr_atp (db->runes, char_x);
+
+ *pix_x = rb->xpos + (rb->width >> 1);
+ }
+ else
+ {
+ int last_rune = Dynarr_length (db->runes) - 1;
+ struct rune *rb = Dynarr_atp (db->runes, last_rune);
+
+ char_x -= last_rune;
+
+ *pix_x = rb->xpos + rb->width;
+ *pix_x += ((char_x - 1) * defwidth);
+ *pix_x += (defwidth >> 1);
+ }
+ }
+ else
+ {
+ /* It didn't intersect, so extrapolate. #### For now, we include the
+ modeline in this since we don't have true character positions in
+ it. */
+
+ if (!Dynarr_length (w->face_cachels))
+ reset_face_cachels (w);
+
+ char_y -= num_disp_lines;
+
+ if (Dynarr_length (dla))
+ {
+ struct display_line *dl = Dynarr_atp (dla, Dynarr_length (dla) - 1);
+ *pix_y = dl->ypos + dl->descent - dl->clip;
+ }
+ else
+ *pix_y = WINDOW_TEXT_TOP (w);
+
+ *pix_y += (char_y * defheight);
+ *pix_y += (defheight >> 1);
+
+ *pix_x = WINDOW_TEXT_LEFT (w);
+ /* Don't adjust by one because this is still the unadjusted value. */
+ *pix_x += (char_x * defwidth);
+ *pix_x += (defwidth >> 1);
+ }
+
+ if (*pix_x > w->pixel_left + w->pixel_width)
+ *pix_x = w->pixel_left + w->pixel_width;
+ if (*pix_y > w->pixel_top + w->pixel_height)
+ *pix_y = w->pixel_top + w->pixel_height;
+
+ *pix_x -= w->pixel_left;
+ *pix_y -= w->pixel_top;
+}
+
+/* Given a display line and a position, determine if there is a glyph
+ there and return information about it if there is. */
+
+static void
+get_position_object (struct display_line *dl, Lisp_Object *obj1,
+ Lisp_Object *obj2, int x_coord, int *low_x_coord,
+ int *high_x_coord)
+{
+ struct display_block *db;
+ int elt;
+ int block =
+ get_next_display_block (dl->bounds, dl->display_blocks, x_coord, 0);
+
+ /* We use get_next_display_block to get the actual display block
+ that would be displayed at x_coord. */
+
+ if (block == NO_BLOCK)
+ return;
+ else
+ db = Dynarr_atp (dl->display_blocks, block);
+
+ for (elt = 0; elt < Dynarr_length (db->runes); elt++)
+ {
+ struct rune *rb = Dynarr_atp (db->runes, elt);
+
+ if (rb->xpos <= x_coord && x_coord < (rb->xpos + rb->width))
+ {
+ if (rb->type == RUNE_DGLYPH)
+ {
+ *obj1 = rb->object.dglyph.glyph;
+ *obj2 = rb->object.dglyph.extent;
+ }
+ else
+ {
+ *obj1 = Qnil;
+ *obj2 = Qnil;
+ }
+
+ if (low_x_coord)
+ *low_x_coord = rb->xpos;
+ if (high_x_coord)
+ *high_x_coord = rb->xpos + rb->width;
+
+ return;
+ }
+ }
+}
+
+#define UPDATE_CACHE_RETURN \
+ do { \
+ d->pixel_to_glyph_cache.valid = 1; \
+ d->pixel_to_glyph_cache.low_x_coord = low_x_coord; \
+ d->pixel_to_glyph_cache.high_x_coord = high_x_coord; \
+ d->pixel_to_glyph_cache.low_y_coord = low_y_coord; \
+ d->pixel_to_glyph_cache.high_y_coord = high_y_coord; \
+ d->pixel_to_glyph_cache.frame = f; \
+ d->pixel_to_glyph_cache.col = *col; \
+ d->pixel_to_glyph_cache.row = *row; \
+ d->pixel_to_glyph_cache.obj_x = *obj_x; \
+ d->pixel_to_glyph_cache.obj_y = *obj_y; \
+ d->pixel_to_glyph_cache.w = *w; \
+ d->pixel_to_glyph_cache.bufpos = *bufpos; \
+ d->pixel_to_glyph_cache.closest = *closest; \
+ d->pixel_to_glyph_cache.modeline_closest = *modeline_closest; \
+ d->pixel_to_glyph_cache.obj1 = *obj1; \
+ d->pixel_to_glyph_cache.obj2 = *obj2; \
+ d->pixel_to_glyph_cache.retval = position; \
+ RETURN_SANS_WARNINGS position; \
+ } while (0)
+
+/* Given x and y coordinates in pixels relative to a frame, return
+ information about what is located under those coordinates.
+
+ The return value will be one of:
+
+ OVER_TOOLBAR: over one of the 4 frame toolbars
+ OVER_MODELINE: over a modeline
+ OVER_BORDER: over an internal border
+ OVER_NOTHING: over the text area, but not over text
+ OVER_OUTSIDE: outside of the frame border
+ OVER_TEXT: over text in the text area
+
+ OBJ1 is one of
+
+ -- a toolbar button
+ -- a glyph
+ -- nil if the coordinates are not over a glyph or a toolbar button.
+
+ OBJ2 is one of
+
+ -- an extent, if the coordinates are over a glyph in the text area
+ -- nil otherwise.
+
+ If the coordinates are over a glyph, OBJ_X and OBJ_Y give the
+ equivalent coordinates relative to the upper-left corner of the glyph.
+
+ If the coordinates are over a character, OBJ_X and OBJ_Y give the
+ equivalent coordinates relative to the upper-left corner of the character.
+
+ Otherwise, OBJ_X and OBJ_Y are undefined.
+ */
+
+int
+pixel_to_glyph_translation (struct frame *f, int x_coord, int y_coord,
+ int *col, int *row, int *obj_x, int *obj_y,
+ struct window **w, Bufpos *bufpos,
+ Bufpos *closest, Charcount *modeline_closest,
+ Lisp_Object *obj1, Lisp_Object *obj2)
+{
+ struct device *d;
+ struct pixel_to_glyph_translation_cache *cache;
+ Lisp_Object window;
+ int frm_left, frm_right, frm_top, frm_bottom;
+ int low_x_coord, high_x_coord, low_y_coord, high_y_coord;
+ int position = OVER_NOTHING;
+ int device_check_failed = 0;
+ display_line_dynarr *dla;
+
+ /* This is a safety valve in case this got called with a frame in
+ the middle of being deleted. */
+ if (!DEVICEP (f->device) || !DEVICE_LIVE_P (XDEVICE (f->device)))
+ {
+ device_check_failed = 1;
+ d = NULL, cache = NULL; /* Warning suppression */
+ }
+ else
+ {
+ d = XDEVICE (f->device);
+ cache = &d->pixel_to_glyph_cache;
+ }
+
+ if (!device_check_failed
+ && cache->valid
+ && cache->frame == f
+ && cache->low_x_coord <= x_coord
+ && cache->high_x_coord > x_coord
+ && cache->low_y_coord <= y_coord
+ && cache->high_y_coord > y_coord)
+ {
+ *col = cache->col;
+ *row = cache->row;
+ *obj_x = cache->obj_x;
+ *obj_y = cache->obj_y;
+ *w = cache->w;
+ *bufpos = cache->bufpos;
+ *closest = cache->closest;
+ *modeline_closest = cache->modeline_closest;
+ *obj1 = cache->obj1;
+ *obj2 = cache->obj2;
+
+ return cache->retval;
+ }
+ else
+ {
+ *col = 0;
+ *row = 0;
+ *obj_x = 0;
+ *obj_y = 0;
+ *w = 0;
+ *bufpos = 0;
+ *closest = 0;
+ *modeline_closest = -1;
+ *obj1 = Qnil;
+ *obj2 = Qnil;
+
+ low_x_coord = x_coord;
+ high_x_coord = x_coord + 1;
+ low_y_coord = y_coord;
+ high_y_coord = y_coord + 1;
+ }
+
+ if (device_check_failed)
+ return OVER_NOTHING;
+
+ frm_left = FRAME_LEFT_BORDER_END (f);
+ frm_right = FRAME_RIGHT_BORDER_START (f);
+ frm_top = FRAME_TOP_BORDER_END (f);
+ frm_bottom = FRAME_BOTTOM_BORDER_START (f);
+
+ /* Check if the mouse is outside of the text area actually used by
+ redisplay. */
+ if (y_coord < frm_top)
+ {
+ if (y_coord >= FRAME_TOP_BORDER_START (f))
+ {
+ low_y_coord = FRAME_TOP_BORDER_START (f);
+ high_y_coord = frm_top;
+ position = OVER_BORDER;
+ }
+ else if (y_coord >= 0)
+ {
+ low_y_coord = 0;
+ high_y_coord = FRAME_TOP_BORDER_START (f);
+ position = OVER_TOOLBAR;
+ }
+ else
+ {
+ low_y_coord = y_coord;
+ high_y_coord = 0;
+ position = OVER_OUTSIDE;
+ }
+ }
+ else if (y_coord >= frm_bottom)
+ {
+ if (y_coord < FRAME_BOTTOM_BORDER_END (f))
+ {
+ low_y_coord = frm_bottom;
+ high_y_coord = FRAME_BOTTOM_BORDER_END (f);
+ position = OVER_BORDER;
+ }
+ else if (y_coord < FRAME_PIXHEIGHT (f))
+ {
+ low_y_coord = FRAME_BOTTOM_BORDER_END (f);
+ high_y_coord = FRAME_PIXHEIGHT (f);
+ position = OVER_TOOLBAR;
+ }
+ else
+ {
+ low_y_coord = FRAME_PIXHEIGHT (f);
+ high_y_coord = y_coord;
+ position = OVER_OUTSIDE;
+ }
+ }
+
+ if (position != OVER_TOOLBAR && position != OVER_BORDER)
+ {
+ if (x_coord < frm_left)
+ {
+ if (x_coord >= FRAME_LEFT_BORDER_START (f))
+ {
+ low_x_coord = FRAME_LEFT_BORDER_START (f);
+ high_x_coord = frm_left;
+ position = OVER_BORDER;
+ }
+ else if (x_coord >= 0)
+ {
+ low_x_coord = 0;
+ high_x_coord = FRAME_LEFT_BORDER_START (f);
+ position = OVER_TOOLBAR;
+ }
+ else
+ {
+ low_x_coord = x_coord;
+ high_x_coord = 0;
+ position = OVER_OUTSIDE;
+ }
+ }
+ else if (x_coord >= frm_right)
+ {
+ if (x_coord < FRAME_RIGHT_BORDER_END (f))
+ {
+ low_x_coord = frm_right;
+ high_x_coord = FRAME_RIGHT_BORDER_END (f);
+ position = OVER_BORDER;
+ }
+ else if (x_coord < FRAME_PIXWIDTH (f))
+ {
+ low_x_coord = FRAME_RIGHT_BORDER_END (f);
+ high_x_coord = FRAME_PIXWIDTH (f);
+ position = OVER_TOOLBAR;
+ }
+ else
+ {
+ low_x_coord = FRAME_PIXWIDTH (f);
+ high_x_coord = x_coord;
+ position = OVER_OUTSIDE;
+ }
+ }
+ }
+
+#ifdef HAVE_TOOLBARS
+ if (position == OVER_TOOLBAR)
+ {
+ *obj1 = toolbar_button_at_pixpos (f, x_coord, y_coord);
+ *obj2 = Qnil;
+ *w = 0;
+ UPDATE_CACHE_RETURN;
+ }
+#endif /* HAVE_TOOLBARS */
+
+ /* We still have to return the window the pointer is next to and its
+ relative y position even if it is outside the x boundary. */
+ if (x_coord < frm_left)
+ x_coord = frm_left;
+ else if (x_coord > frm_right)
+ x_coord = frm_right;
+
+ /* Same in reverse. */
+ if (y_coord < frm_top)
+ y_coord = frm_top;
+ else if (y_coord > frm_bottom)
+ y_coord = frm_bottom;
+
+ /* Find what window the given coordinates are actually in. */
+ window = f->root_window;
+ *w = find_window_by_pixel_pos (x_coord, y_coord, window);
+
+ /* If we didn't find a window, we're done. */
+ if (!*w)
+ {
+ UPDATE_CACHE_RETURN;
+ }
+ else if (position != OVER_NOTHING)
+ {
+ *closest = 0;
+ *modeline_closest = -1;
+
+ if (high_y_coord <= frm_top || high_y_coord >= frm_bottom)
+ {
+ *w = 0;
+ UPDATE_CACHE_RETURN;
+ }
+ }
+
+ /* Check if the window is a minibuffer but isn't active. */
+ if (MINI_WINDOW_P (*w) && !minibuf_level)
+ {
+ /* Must reset the window value since some callers will ignore
+ the return value if it is set. */
+ *w = 0;
+ UPDATE_CACHE_RETURN;
+ }
+
+ /* See if the point is over window vertical divider */
+ if (window_needs_vertical_divider (*w))
+ {
+ int div_x_high = WINDOW_RIGHT (*w);
+ int div_x_low = div_x_high - window_divider_width (*w);
+ int div_y_high = WINDOW_BOTTOM (*w);
+ int div_y_low = WINDOW_TOP (*w);
+
+ if (div_x_low < x_coord && x_coord <= div_x_high &&
+ div_y_low < y_coord && y_coord <= div_y_high)
+ {
+ low_x_coord = div_x_low;
+ high_x_coord = div_x_high;
+ low_y_coord = div_y_low;
+ high_y_coord = div_y_high;
+ position = OVER_V_DIVIDER;
+ UPDATE_CACHE_RETURN;
+ }
+ }
+
+ dla = window_display_lines (*w, CURRENT_DISP);
+
+ for (*row = 0; *row < Dynarr_length (dla); (*row)++)
+ {
+ int really_over_nothing = 0;
+ struct display_line *dl = Dynarr_atp (dla, *row);
+
+ if ((int) (dl->ypos - dl->ascent) <= y_coord
+ && y_coord <= (int) (dl->ypos + dl->descent))
+ {
+ int check_margin_glyphs = 0;
+ struct display_block *db = get_display_block_from_line (dl, TEXT);
+ struct rune *rb = 0;
+
+ if (x_coord < dl->bounds.left_white
+ || x_coord >= dl->bounds.right_white)
+ check_margin_glyphs = 1;
+
+ low_y_coord = dl->ypos - dl->ascent;
+ high_y_coord = dl->ypos + dl->descent + 1;
+
+ if (position == OVER_BORDER
+ || position == OVER_OUTSIDE
+ || check_margin_glyphs)
+ {
+ int x_check, left_bound;
+
+ if (check_margin_glyphs)
+ {
+ x_check = x_coord;
+ left_bound = dl->bounds.left_white;
+ }
+ else
+ {
+ x_check = high_x_coord;
+ left_bound = frm_left;
+ }
+
+ if (Dynarr_length (db->runes))
+ {
+ if (x_check <= left_bound)
+ {
+ if (dl->modeline)
+ *modeline_closest = Dynarr_atp (db->runes, 0)->bufpos;
+ else
+ *closest = Dynarr_atp (db->runes, 0)->bufpos;
+ }
+ else
+ {
+ if (dl->modeline)
+ *modeline_closest =
+ Dynarr_atp (db->runes,
+ Dynarr_length (db->runes) - 1)->bufpos;
+ else
+ *closest =
+ Dynarr_atp (db->runes,
+ Dynarr_length (db->runes) - 1)->bufpos;
+ }
+
+ if (dl->modeline)
+ *modeline_closest += dl->offset;
+ else
+ *closest += dl->offset;
+ }
+ else
+ {
+ /* #### What should be here. */
+ if (dl->modeline)
+ *modeline_closest = 0;
+ else
+ *closest = 0;
+ }
+
+ if (check_margin_glyphs)
+ {
+ if (x_coord < dl->bounds.left_in
+ || x_coord >= dl->bounds.right_in)
+ {
+ /* If we are over the outside margins then we
+ know the loop over the text block isn't going
+ to accomplish anything. So we go ahead and
+ set what information we can right here and
+ return. */
+ (*row)--;
+ *obj_y = y_coord - (dl->ypos - dl->ascent);
+ get_position_object (dl, obj1, obj2, x_coord,
+ &low_x_coord, &high_x_coord);
+
+ UPDATE_CACHE_RETURN;
+ }
+ }
+ else
+ UPDATE_CACHE_RETURN;
+ }
+
+ for (*col = 0; *col <= Dynarr_length (db->runes); (*col)++)
+ {
+ int past_end = (*col == Dynarr_length (db->runes));
+
+ if (!past_end)
+ rb = Dynarr_atp (db->runes, *col);
+
+ if (past_end ||
+ (rb->xpos <= x_coord && x_coord < rb->xpos + rb->width))
+ {
+ if (past_end)
+ {
+ (*col)--;
+ rb = Dynarr_atp (db->runes, *col);
+ }
+
+ *bufpos = rb->bufpos + dl->offset;
+ low_x_coord = rb->xpos;
+ high_x_coord = rb->xpos + rb->width;
+
+ if (rb->type == RUNE_DGLYPH)
+ {
+ int elt = *col + 1;
+
+ /* Find the first character after the glyph. */
+ while (elt < Dynarr_length (db->runes))
+ {
+ if (Dynarr_atp (db->runes, elt)->type != RUNE_DGLYPH)
+ {
+ if (dl->modeline)
+ *modeline_closest =
+ (Dynarr_atp (db->runes, elt)->bufpos +
+ dl->offset);
+ else
+ *closest =
+ (Dynarr_atp (db->runes, elt)->bufpos +
+ dl->offset);
+ break;
+ }
+
+ elt++;
+ }
+
+ /* In this case we failed to find a non-glyph
+ character so we return the last position
+ displayed on the line. */
+ if (elt == Dynarr_length (db->runes))
+ {
+ if (dl->modeline)
+ *modeline_closest = dl->end_bufpos + dl->offset;
+ else
+ *closest = dl->end_bufpos + dl->offset;
+ really_over_nothing = 1;
+ }
+ }
+ else
+ {
+ if (dl->modeline)
+ *modeline_closest = rb->bufpos + dl->offset;
+ else
+ *closest = rb->bufpos + dl->offset;
+ }
+
+ if (dl->modeline)
+ {
+ *row = window_displayed_height (*w);
+
+ if (position == OVER_NOTHING)
+ position = OVER_MODELINE;
+
+ if (rb->type == RUNE_DGLYPH)
+ {
+ *obj1 = rb->object.dglyph.glyph;
+ *obj2 = rb->object.dglyph.extent;
+ }
+ else if (rb->type == RUNE_CHAR)
+ {
+ *obj1 = Qnil;
+ *obj2 = Qnil;
+ }
+ else
+ {
+ *obj1 = Qnil;
+ *obj2 = Qnil;
+ }
+
+ UPDATE_CACHE_RETURN;
+ }
+ else if (past_end
+ || (rb->type == RUNE_CHAR
+ && rb->object.chr.ch == '\n'))
+ {
+ (*row)--;
+ /* At this point we may have glyphs in the right
+ inside margin. */
+ if (check_margin_glyphs)
+ get_position_object (dl, obj1, obj2, x_coord,
+ &low_x_coord, &high_x_coord);
+ UPDATE_CACHE_RETURN;
+ }
+ else
+ {
+ (*row)--;
+ if (rb->type == RUNE_DGLYPH)
+ {
+ *obj1 = rb->object.dglyph.glyph;
+ *obj2 = rb->object.dglyph.extent;
+ }
+ else if (rb->type == RUNE_CHAR)
+ {
+ *obj1 = Qnil;
+ *obj2 = Qnil;
+ }
+ else
+ {
+ *obj1 = Qnil;
+ *obj2 = Qnil;
+ }
+
+ *obj_x = x_coord - rb->xpos;
+ *obj_y = y_coord - (dl->ypos - dl->ascent);
+
+ /* At this point we may have glyphs in the left
+ inside margin. */
+ if (check_margin_glyphs)
+ get_position_object (dl, obj1, obj2, x_coord, 0, 0);
+
+ if (position == OVER_NOTHING && !really_over_nothing)
+ position = OVER_TEXT;
+
+ UPDATE_CACHE_RETURN;
+ }
+ }
+ }
+ }
+ }
+
+ *row = Dynarr_length (dla) - 1;
+ if (FRAME_WIN_P (f))
+ {
+ int bot_elt = Dynarr_length (dla) - 1;
+
+ if (bot_elt >= 0)
+ {
+ struct display_line *dl = Dynarr_atp (dla, bot_elt);
+ int adj_area = y_coord - (dl->ypos + dl->descent);
+ Lisp_Object lwin;
+ int defheight;
+
+ XSETWINDOW (lwin, *w);
+ default_face_height_and_width (lwin, 0, &defheight);
+
+ *row += (adj_area / defheight);
+ }
+ }
+
+ /* #### This should be checked out some more to determine what
+ should really be going on. */
+ if (!MARKERP ((*w)->start[CURRENT_DISP]))
+ *closest = 0;
+ else
+ *closest = end_of_last_line_may_error (*w,
+ marker_position ((*w)->start[CURRENT_DISP]));
+ *col = 0;
+ UPDATE_CACHE_RETURN;
+}
+#undef UPDATE_CACHE_RETURN
+
+\f
+/***************************************************************************/
+/* */
+/* Lisp functions */
+/* */
+/***************************************************************************/
+
+DEFUN ("redisplay-echo-area", Fredisplay_echo_area, 0, 0, 0, /*
+Ensure that all minibuffers are correctly showing the echo area.
+*/
+ ())
+{
+ Lisp_Object devcons, concons;
+
+ DEVICE_LOOP_NO_BREAK (devcons, concons)
+ {
+ struct device *d = XDEVICE (XCAR (devcons));
+ Lisp_Object frmcons;
+
+ DEVICE_FRAME_LOOP (frmcons, d)
+ {
+ struct frame *f = XFRAME (XCAR (frmcons));
+
+ if (FRAME_REPAINT_P (f) && FRAME_HAS_MINIBUF_P (f))
+ {
+ Lisp_Object window = FRAME_MINIBUF_WINDOW (f);
+
+ MAYBE_DEVMETH (d, frame_output_begin, (f));
+
+ /*
+ * If the frame size has changed, there may be random
+ * chud on the screen left from previous messages
+ * because redisplay_frame hasn't been called yet.
+ * Clear the screen to get rid of the potential mess.
+ */
+ if (f->echo_area_garbaged)
+ {
+ MAYBE_DEVMETH (d, clear_frame, (f));
+ f->echo_area_garbaged = 0;
+ }
+ redisplay_window (window, 0);
+ MAYBE_DEVMETH (d, frame_output_end, (f));
+
+ call_redisplay_end_triggers (XWINDOW (window), 0);
+ }
+ }
+ }
+
+ return Qnil;
+}
+
+static Lisp_Object
+restore_disable_preemption_value (Lisp_Object value)
+{
+ disable_preemption = XINT (value);
+ return Qnil;
+}
+
+DEFUN ("redraw-frame", Fredraw_frame, 0, 2, 0, /*
+Clear frame FRAME and output again what is supposed to appear on it.
+FRAME defaults to the selected frame if omitted.
+Normally, redisplay is preempted as normal if input arrives. However,
+if optional second arg NO-PREEMPT is non-nil, redisplay will not stop for
+input and is guaranteed to proceed to completion.
+*/
+ (frame, no_preempt))
+{
+ struct frame *f = decode_frame (frame);
+ int count = specpdl_depth ();
+
+ if (!NILP (no_preempt))
+ {
+ record_unwind_protect (restore_disable_preemption_value,
+ make_int (disable_preemption));
+ disable_preemption++;
+ }
+
+ f->clear = 1;
+ redisplay_frame (f, 1);
+
+ /* See the comment in Fredisplay_frame. */
+ RESET_CHANGED_SET_FLAGS;
+
+ return unbind_to (count, Qnil);
+}
+
+DEFUN ("redisplay-frame", Fredisplay_frame, 0, 2, 0, /*
+Ensure that FRAME's contents are correctly displayed.
+This differs from `redraw-frame' in that it only redraws what needs to
+be updated, as opposed to unconditionally clearing and redrawing
+the frame.
+FRAME defaults to the selected frame if omitted.
+Normally, redisplay is preempted as normal if input arrives. However,
+if optional second arg NO-PREEMPT is non-nil, redisplay will not stop for
+input and is guaranteed to proceed to completion.
+*/
+ (frame, no_preempt))
+{
+ struct frame *f = decode_frame (frame);
+ int count = specpdl_depth ();
+
+ if (!NILP (no_preempt))
+ {
+ record_unwind_protect (restore_disable_preemption_value,
+ make_int (disable_preemption));
+ disable_preemption++;
+ }
+
+ redisplay_frame (f, 1);
+
+ /* If we don't reset the global redisplay flags here, subsequent
+ changes to the display will not get registered by redisplay
+ because it thinks it already has registered changes. If you
+ really knew what you were doing you could confuse redisplay by
+ calling Fredisplay_frame while updating another frame. We assume
+ that if you know what you are doing you will not be that
+ stupid. */
+ RESET_CHANGED_SET_FLAGS;
+
+ return unbind_to (count, Qnil);
+}
+
+DEFUN ("redraw-device", Fredraw_device, 0, 2, 0, /*
+Clear device DEVICE and output again what is supposed to appear on it.
+DEVICE defaults to the selected device if omitted.
+Normally, redisplay is preempted as normal if input arrives. However,
+if optional second arg NO-PREEMPT is non-nil, redisplay will not stop for
+input and is guaranteed to proceed to completion.
+*/
+ (device, no_preempt))
+{
+ struct device *d = decode_device (device);
+ Lisp_Object frmcons;
+ int count = specpdl_depth ();
+
+ if (!NILP (no_preempt))
+ {
+ record_unwind_protect (restore_disable_preemption_value,
+ make_int (disable_preemption));
+ disable_preemption++;
+ }
+
+ DEVICE_FRAME_LOOP (frmcons, d)
+ {
+ XFRAME (XCAR (frmcons))->clear = 1;
+ }
+ redisplay_device (d, 0);
+
+ /* See the comment in Fredisplay_frame. */
+ RESET_CHANGED_SET_FLAGS;
+
+ return unbind_to (count, Qnil);
+}
+
+DEFUN ("redisplay-device", Fredisplay_device, 0, 2, 0, /*
+Ensure that DEVICE's contents are correctly displayed.
+This differs from `redraw-device' in that it only redraws what needs to
+be updated, as opposed to unconditionally clearing and redrawing
+the device.
+DEVICE defaults to the selected device if omitted.
+Normally, redisplay is preempted as normal if input arrives. However,
+if optional second arg NO-PREEMPT is non-nil, redisplay will not stop for
+input and is guaranteed to proceed to completion.
+*/
+ (device, no_preempt))
+{
+ struct device *d = decode_device (device);
+ int count = specpdl_depth ();
+
+ if (!NILP (no_preempt))
+ {
+ record_unwind_protect (restore_disable_preemption_value,
+ make_int (disable_preemption));
+ disable_preemption++;
+ }
+
+ redisplay_device (d, 0);
+
+ /* See the comment in Fredisplay_frame. */
+ RESET_CHANGED_SET_FLAGS;
+
+ return unbind_to (count, Qnil);
+}
+
+/* Big lie. Big lie. This will force all modelines to be updated
+ regardless if the all flag is set or not. It remains in existence
+ solely for backwards compatibility. */
+DEFUN ("redraw-modeline", Fredraw_modeline, 0, 1, 0, /*
+Force the modeline of the current buffer to be redisplayed.
+With optional non-nil ALL, force redisplay of all modelines.
+*/
+ (all))
+{
+ MARK_MODELINE_CHANGED;
+ return Qnil;
+}
+
+DEFUN ("force-cursor-redisplay", Fforce_cursor_redisplay, 0, 1, 0, /*
+Force an immediate update of the cursor on FRAME.
+FRAME defaults to the selected frame if omitted.
+*/
+ (frame))
+{
+ redisplay_redraw_cursor (decode_frame (frame), 1);
+ return Qnil;
+}
+
+\f
+/***************************************************************************/
+/* */
+/* Lisp-variable change triggers */
+/* */
+/***************************************************************************/
+
+static void
+margin_width_changed_in_frame (Lisp_Object specifier, struct frame *f,
+ Lisp_Object oldval)
+{
+ /* Nothing to be done? */
+}
+
+int
+redisplay_variable_changed (Lisp_Object sym, Lisp_Object *val,
+ Lisp_Object in_object, int flags)
+{
+ /* #### clip_changed should really be renamed something like
+ global_redisplay_change. */
+ MARK_CLIP_CHANGED;
+ return 0;
+}
+
+/* This is called if the built-in glyphs have their properties
+ changed. */
+void
+redisplay_glyph_changed (Lisp_Object glyph, Lisp_Object property,
+ Lisp_Object locale)
+{
+ if (WINDOWP (locale))
+ {
+ MARK_FRAME_GLYPHS_CHANGED (XFRAME (WINDOW_FRAME (XWINDOW (locale))));
+ }
+ else if (FRAMEP (locale))
+ {
+ MARK_FRAME_GLYPHS_CHANGED (XFRAME (locale));
+ }
+ else if (DEVICEP (locale))
+ {
+ Lisp_Object frmcons;
+ DEVICE_FRAME_LOOP (frmcons, XDEVICE (locale))
+ MARK_FRAME_GLYPHS_CHANGED (XFRAME (XCAR (frmcons)));
+ }
+ else if (CONSOLEP (locale))
+ {
+ Lisp_Object frmcons, devcons;
+ CONSOLE_FRAME_LOOP_NO_BREAK (frmcons, devcons, XCONSOLE (locale))
+ MARK_FRAME_GLYPHS_CHANGED (XFRAME (XCAR (frmcons)));
+ }
+ else /* global or buffer */
+ {
+ Lisp_Object frmcons, devcons, concons;
+ FRAME_LOOP_NO_BREAK (frmcons, devcons, concons)
+ MARK_FRAME_GLYPHS_CHANGED (XFRAME (XCAR (frmcons)));
+ }
+}
+
+static void
+text_cursor_visible_p_changed (Lisp_Object specifier, struct window *w,
+ Lisp_Object oldval)
+{
+ if (XFRAME (w->frame)->init_finished)
+ Fforce_cursor_redisplay (w->frame);
+}
+
+#ifdef MEMORY_USAGE_STATS
+
+\f
+/***************************************************************************/
+/* */
+/* memory usage computation */
+/* */
+/***************************************************************************/
+
+static int
+compute_rune_dynarr_usage (rune_dynarr *dyn, struct overhead_stats *ovstats)
+{
+ return dyn ? Dynarr_memory_usage (dyn, ovstats) : 0;
+}
+
+static int
+compute_display_block_dynarr_usage (display_block_dynarr *dyn,
+ struct overhead_stats *ovstats)
+{
+ int total, i;
+
+ if (!dyn)
+ return 0;
+
+ total = Dynarr_memory_usage (dyn, ovstats);
+ for (i = 0; i < Dynarr_largest (dyn); i++)
+ total += compute_rune_dynarr_usage (Dynarr_at (dyn, i).runes, ovstats);
+
+ return total;
+}
+
+static int
+compute_glyph_block_dynarr_usage (glyph_block_dynarr *dyn,
+ struct overhead_stats *ovstats)
+{
+ return dyn ? Dynarr_memory_usage (dyn, ovstats) : 0;
+}
+
+int
+compute_display_line_dynarr_usage (display_line_dynarr *dyn,
+ struct overhead_stats *ovstats)
+{
+ int total, i;
+
+ if (!dyn)
+ return 0;
+
+ total = Dynarr_memory_usage (dyn, ovstats);
+ for (i = 0; i < Dynarr_largest (dyn); i++)
+ {
+ struct display_line *dl = &Dynarr_at (dyn, i);
+ total += compute_display_block_dynarr_usage(dl->display_blocks, ovstats);
+ total += compute_glyph_block_dynarr_usage (dl->left_glyphs, ovstats);
+ total += compute_glyph_block_dynarr_usage (dl->right_glyphs, ovstats);
+ }
+
+ return total;
+}
+
+int
+compute_line_start_cache_dynarr_usage (line_start_cache_dynarr *dyn,
+ struct overhead_stats *ovstats)
+{
+ return dyn ? Dynarr_memory_usage (dyn, ovstats) : 0;
+}
+
+#endif /* MEMORY_USAGE_STATS */
+
+\f
+/***************************************************************************/
+/* */
+/* initialization */
+/* */
+/***************************************************************************/
+
+void
+init_redisplay (void)
+{
+ disable_preemption = 0;
+ preemption_count = 0;
+ max_preempts = INIT_MAX_PREEMPTS;
+
+#ifndef PDUMP
+ if (!initialized)
+#endif
+ {
+ if (!cmotion_display_lines)
+ cmotion_display_lines = Dynarr_new (display_line);
+ if (!mode_spec_bufbyte_string)
+ mode_spec_bufbyte_string = Dynarr_new (Bufbyte);
+ if (!formatted_string_extent_dynarr)
+ formatted_string_extent_dynarr = Dynarr_new (EXTENT);
+ if (!formatted_string_extent_start_dynarr)
+ formatted_string_extent_start_dynarr = Dynarr_new (Bytecount);
+ if (!formatted_string_extent_end_dynarr)
+ formatted_string_extent_end_dynarr = Dynarr_new (Bytecount);
+ if (!internal_cache)
+ internal_cache = Dynarr_new (line_start_cache);
+ }
+
+ /* window system is nil when in -batch mode */
+ if (!initialized || noninteractive)
+ return;
+
+ /* If the user wants to use a window system, we shouldn't bother
+ initializing the terminal. This is especially important when the
+ terminal is so dumb that emacs gives up before and doesn't bother
+ using the window system.
+
+ If the DISPLAY environment variable is set, try to use X, and die
+ with an error message if that doesn't work. */
+
+#ifdef HAVE_X_WINDOWS
+ if (!strcmp (display_use, "x"))
+ {
+ /* Some stuff checks this way early. */
+ Vwindow_system = Qx;
+ Vinitial_window_system = Qx;
+ return;
+ }
+#endif /* HAVE_X_WINDOWS */
+
+#ifdef HAVE_GTK
+ if (!strcmp (display_use, "gtk"))
+ {
+ Vwindow_system = Qgtk;
+ Vinitial_window_system = Qgtk;
+ return;
+ }
+#endif
+
+#ifdef HAVE_MS_WINDOWS
+ if (!strcmp (display_use, "mswindows"))
+ {
+ /* Some stuff checks this way early. */
+ Vwindow_system = Qmswindows;
+ Vinitial_window_system = Qmswindows;
+ return;
+ }
+#endif /* HAVE_MS_WINDOWS */
+
+#ifdef HAVE_TTY
+ /* If no window system has been specified, try to use the terminal. */
+ if (!isatty (0))
+ {
+ stderr_out ("XEmacs: standard input is not a tty\n");
+ exit (1);
+ }
+
+ /* Look at the TERM variable */
+ if (!getenv ("TERM"))
+ {
+ stderr_out ("Please set the environment variable TERM; see tset(1).\n");
+ exit (1);
+ }
+
+ Vinitial_window_system = Qtty;
+ return;
+#else /* not HAVE_TTY */
+ /* No DISPLAY specified, and no TTY support. */
+ stderr_out ("XEmacs: Cannot open display.\n\
+Please set the environmental variable DISPLAY to an appropriate value.\n");
+ exit (1);
+#endif
+ /* Unreached. */
+}
+
+void
+syms_of_redisplay (void)
+{
+ defsymbol (&Qcursor_in_echo_area, "cursor-in-echo-area");
+#ifndef INHIBIT_REDISPLAY_HOOKS
+ defsymbol (&Qpre_redisplay_hook, "pre-redisplay-hook");
+ defsymbol (&Qpost_redisplay_hook, "post-redisplay-hook");
+#endif /* INHIBIT_REDISPLAY_HOOKS */
+ defsymbol (&Qdisplay_warning_buffer, "display-warning-buffer");
+ defsymbol (&Qbar_cursor, "bar-cursor");
+ defsymbol (&Qredisplay_end_trigger_functions,
+ "redisplay-end-trigger-functions");
+ defsymbol (&Qtop_bottom, "top-bottom");
+ defsymbol (&Qbuffer_list_changed_hook, "buffer-list-changed-hook");
+
+ DEFSUBR (Fredisplay_echo_area);
+ DEFSUBR (Fredraw_frame);
+ DEFSUBR (Fredisplay_frame);
+ DEFSUBR (Fredraw_device);
+ DEFSUBR (Fredisplay_device);
+ DEFSUBR (Fredraw_modeline);
+ DEFSUBR (Fforce_cursor_redisplay);
+}
+
+void
+vars_of_redisplay (void)
+{
+
+#if 0
+ staticpro (&last_arrow_position);
+ staticpro (&last_arrow_string);
+ last_arrow_position = Qnil;
+ last_arrow_string = Qnil;
+#endif /* 0 */
+
+ /* #### Probably temporary */
+ DEFVAR_INT ("redisplay-cache-adjustment", &cache_adjustment /*
+\(Temporary) Setting this will impact the performance of the internal
+line start cache.
+*/ );
+ cache_adjustment = 2;
+
+ DEFVAR_INT_MAGIC ("pixel-vertical-clip-threshold", &vertical_clip /*
+Minimum pixel height for clipped bottom display line.
+A clipped line shorter than this won't be displayed.
+*/ ,
+ redisplay_variable_changed);
+ vertical_clip = 5;
+
+ DEFVAR_INT_MAGIC ("pixel-horizontal-clip-threshold", &horizontal_clip /*
+Minimum visible area for clipped glyphs at right boundary.
+Clipped glyphs shorter than this won't be displayed.
+Only pixmap glyph instances are currently allowed to be clipped.
+*/ ,
+ redisplay_variable_changed);
+ horizontal_clip = 5;
+
+ DEFVAR_LISP ("global-mode-string", &Vglobal_mode_string /*
+String displayed by modeline-format's "%m" specification.
+*/ );
+ Vglobal_mode_string = Qnil;
+
+ DEFVAR_LISP_MAGIC ("overlay-arrow-position", &Voverlay_arrow_position /*
+Marker for where to display an arrow on top of the buffer text.
+This must be the beginning of a line in order to work.
+See also `overlay-arrow-string'.
+*/ ,
+ redisplay_variable_changed);
+ Voverlay_arrow_position = Qnil;
+
+ DEFVAR_LISP_MAGIC ("overlay-arrow-string", &Voverlay_arrow_string /*
+String or glyph to display as an arrow. See also `overlay-arrow-position'.
+\(Note that despite the name of this variable, it can be set to a glyph as
+well as a string.)
+*/ ,
+ redisplay_variable_changed);
+ Voverlay_arrow_string = Qnil;
+
+ DEFVAR_INT ("scroll-step", &scroll_step /*
+*The number of lines to try scrolling a window by when point moves out.
+If that fails to bring point back on frame, point is centered instead.
+If this is zero, point is always centered after it moves off screen.
+*/ );
+ scroll_step = 0;
+
+ DEFVAR_INT ("scroll-conservatively", &scroll_conservatively /*
+*Scroll up to this many lines, to bring point back on screen.
+*/ );
+ scroll_conservatively = 0;
+
+ DEFVAR_BOOL_MAGIC ("truncate-partial-width-windows",
+ &truncate_partial_width_windows /*
+*Non-nil means truncate lines in all windows less than full frame wide.
+*/ ,
+ redisplay_variable_changed);
+ truncate_partial_width_windows = 1;
+
+ DEFVAR_LISP ("visible-bell", &Vvisible_bell /*
+*Non-nil substitutes a visual signal for the audible bell.
+
+Default behavior is to flash the whole screen. On some platforms,
+special effects are available using the following values:
+
+'display Flash the whole screen (ie, the default behavior).
+'top-bottom Flash only the top and bottom lines of the selected frame.
+
+When effects are unavailable on a platform, the visual bell is the
+default, whole screen. (Currently only X supports any special effects.)
+*/ );
+ Vvisible_bell = Qnil;
+
+ DEFVAR_BOOL ("no-redraw-on-reenter", &no_redraw_on_reenter /*
+*Non-nil means no need to redraw entire frame after suspending.
+A non-nil value is useful if the terminal can automatically preserve
+Emacs's frame display when you reenter Emacs.
+It is up to you to set this variable if your terminal can do that.
+*/ );
+ no_redraw_on_reenter = 0;
+
+ DEFVAR_LISP ("window-system", &Vwindow_system /*
+A symbol naming the window-system under which Emacs is running,
+such as `x', or nil if emacs is running on an ordinary terminal.
+
+Do not use this variable, except for GNU Emacs compatibility, as it
+gives wrong values in a multi-device environment. Use `console-type'
+instead.
+*/ );
+ Vwindow_system = Qnil;
+
+ /* #### Temporary shit until window-system is eliminated. */
+ DEFVAR_CONST_LISP ("initial-window-system", &Vinitial_window_system /*
+DON'T TOUCH
+*/ );
+ Vinitial_window_system = Qnil;
+
+ DEFVAR_BOOL ("cursor-in-echo-area", &cursor_in_echo_area /*
+Non-nil means put cursor in minibuffer, at end of any message there.
+*/ );
+ cursor_in_echo_area = 0;
+
+ /* #### Shouldn't this be generalized as follows:
+
+ if nil, use block cursor.
+ if a number, use a bar cursor of that width.
+ Otherwise, use a 1-pixel bar cursor.
+
+ #### Or better yet, this variable should be trashed entirely
+ (use a Lisp-magic variable to maintain compatibility)
+ and a specifier `cursor-shape' added, which allows a block
+ cursor, a bar cursor, a flashing block or bar cursor,
+ maybe a caret cursor, etc. */
+
+ DEFVAR_LISP ("bar-cursor", &Vbar_cursor /*
+*Use vertical bar cursor if non-nil. If t width is 1 pixel, otherwise 2.
+*/ );
+ Vbar_cursor = Qnil;
+
+#ifndef INHIBIT_REDISPLAY_HOOKS
+ xxDEFVAR_LISP ("pre-redisplay-hook", &Vpre_redisplay_hook /*
+Function or functions to run before every redisplay.
+*/ );
+ Vpre_redisplay_hook = Qnil;
+
+ xxDEFVAR_LISP ("post-redisplay-hook", &Vpost_redisplay_hook /*
+Function or functions to run after every redisplay.
+*/ );
+ Vpost_redisplay_hook = Qnil;
+#endif /* INHIBIT_REDISPLAY_HOOKS */
+
+ DEFVAR_LISP ("buffer-list-changed-hook", &Vbuffer_list_changed_hook /*
+Function or functions to call when a frame's buffer list has changed.
+This is called during redisplay, before redisplaying each frame.
+Functions on this hook are called with one argument, the frame.
+*/ );
+ Vbuffer_list_changed_hook = Qnil;
+
+ DEFVAR_INT ("display-warning-tick", &display_warning_tick /*
+Bump this to tell the C code to call `display-warning-buffer'
+at next redisplay. You should not normally change this; the function
+`display-warning' automatically does this at appropriate times.
+*/ );
+ display_warning_tick = 0;
+
+ DEFVAR_BOOL ("inhibit-warning-display", &inhibit_warning_display /*
+Non-nil means inhibit display of warning messages.
+You should *bind* this, not set it. Any pending warning messages
+will be displayed when the binding no longer applies.
+*/ );
+ /* reset to 0 by startup.el after the splash screen has displayed.
+ This way, the warnings don't obliterate the splash screen. */
+ inhibit_warning_display = 1;
+
+ DEFVAR_LISP ("window-size-change-functions",
+ &Vwindow_size_change_functions /*
+Not currently implemented.
+Functions called before redisplay, if window sizes have changed.
+The value should be a list of functions that take one argument.
+Just before redisplay, for each frame, if any of its windows have changed
+size since the last redisplay, or have been split or deleted,
+all the functions in the list are called, with the frame as argument.
+*/ );
+ Vwindow_size_change_functions = Qnil;
+
+ DEFVAR_LISP ("window-scroll-functions", &Vwindow_scroll_functions /*
+Not currently implemented.
+Functions to call before redisplaying a window with scrolling.
+Each function is called with two arguments, the window
+and its new display-start position. Note that the value of `window-end'
+is not valid when these functions are called.
+*/ );
+ Vwindow_scroll_functions = Qnil;
+
+ DEFVAR_LISP ("redisplay-end-trigger-functions",
+ &Vredisplay_end_trigger_functions /*
+See `set-window-redisplay-end-trigger'.
+*/ );
+ Vredisplay_end_trigger_functions = Qnil;
+
+ DEFVAR_BOOL ("column-number-start-at-one", &column_number_start_at_one /*
+*Non-nil means column display number starts at 1.
+*/ );
+ column_number_start_at_one = 0;
+}
+
+void
+specifier_vars_of_redisplay (void)
+{
+ DEFVAR_SPECIFIER ("left-margin-width", &Vleft_margin_width /*
+*Width of left margin.
+This is a specifier; use `set-specifier' to change it.
+*/ );
+ Vleft_margin_width = Fmake_specifier (Qnatnum);
+ set_specifier_fallback (Vleft_margin_width, list1 (Fcons (Qnil, Qzero)));
+ set_specifier_caching (Vleft_margin_width,
+ offsetof (struct window, left_margin_width),
+ some_window_value_changed,
+ offsetof (struct frame, left_margin_width),
+ margin_width_changed_in_frame, 0);
+
+ DEFVAR_SPECIFIER ("right-margin-width", &Vright_margin_width /*
+*Width of right margin.
+This is a specifier; use `set-specifier' to change it.
+*/ );
+ Vright_margin_width = Fmake_specifier (Qnatnum);
+ set_specifier_fallback (Vright_margin_width, list1 (Fcons (Qnil, Qzero)));
+ set_specifier_caching (Vright_margin_width,
+ offsetof (struct window, right_margin_width),
+ some_window_value_changed,
+ offsetof (struct frame, right_margin_width),
+ margin_width_changed_in_frame, 0);
+
+ DEFVAR_SPECIFIER ("minimum-line-ascent", &Vminimum_line_ascent /*
+*Minimum ascent height of lines.
+This is a specifier; use `set-specifier' to change it.
+*/ );
+ Vminimum_line_ascent = Fmake_specifier (Qnatnum);
+ set_specifier_fallback (Vminimum_line_ascent, list1 (Fcons (Qnil, Qzero)));
+ set_specifier_caching (Vminimum_line_ascent,
+ offsetof (struct window, minimum_line_ascent),
+ some_window_value_changed,
+ 0, 0, 0);
+
+ DEFVAR_SPECIFIER ("minimum-line-descent", &Vminimum_line_descent /*
+*Minimum descent height of lines.
+This is a specifier; use `set-specifier' to change it.
+*/ );
+ Vminimum_line_descent = Fmake_specifier (Qnatnum);
+ set_specifier_fallback (Vminimum_line_descent, list1 (Fcons (Qnil, Qzero)));
+ set_specifier_caching (Vminimum_line_descent,
+ offsetof (struct window, minimum_line_descent),
+ some_window_value_changed,
+ 0, 0, 0);
+
+ DEFVAR_SPECIFIER ("use-left-overflow", &Vuse_left_overflow /*
+*Non-nil means use the left outside margin as extra whitespace when
+displaying 'whitespace or 'inside-margin glyphs.
+This is a specifier; use `set-specifier' to change it.
+*/ );
+ Vuse_left_overflow = Fmake_specifier (Qboolean);
+ set_specifier_fallback (Vuse_left_overflow, list1 (Fcons (Qnil, Qnil)));
+ set_specifier_caching (Vuse_left_overflow,
+ offsetof (struct window, use_left_overflow),
+ some_window_value_changed,
+ 0, 0, 0);
+
+ DEFVAR_SPECIFIER ("use-right-overflow", &Vuse_right_overflow /*
+*Non-nil means use the right outside margin as extra whitespace when
+displaying 'whitespace or 'inside-margin glyphs.
+This is a specifier; use `set-specifier' to change it.
+*/ );
+ Vuse_right_overflow = Fmake_specifier (Qboolean);
+ set_specifier_fallback (Vuse_right_overflow, list1 (Fcons (Qnil, Qnil)));
+ set_specifier_caching (Vuse_right_overflow,
+ offsetof (struct window, use_right_overflow),
+ some_window_value_changed,
+ 0, 0, 0);
+
+ DEFVAR_SPECIFIER ("text-cursor-visible-p", &Vtext_cursor_visible_p /*
+*Non-nil means the text cursor is visible (this is usually the case).
+This is a specifier; use `set-specifier' to change it.
+*/ );
+ Vtext_cursor_visible_p = Fmake_specifier (Qboolean);
+ set_specifier_fallback (Vtext_cursor_visible_p, list1 (Fcons (Qnil, Qt)));
+ set_specifier_caching (Vtext_cursor_visible_p,
+ offsetof (struct window, text_cursor_visible_p),
+ text_cursor_visible_p_changed,
+ 0, 0, 0);
+
+}