Fix bug #9470 with slow redisplay in huge single-paragraph buffers.
[bpt/emacs.git] / src / xdisp.c
index 5cbc774..3cb9f30 100644 (file)
@@ -606,7 +606,7 @@ int current_mode_line_height, current_header_line_height;
     if (CACHE)                                 \
       bidi_unshelve_cache (CACHE, 1);          \
     ITCOPY = ITORIG;                           \
-    CACHE = bidi_shelve_cache();               \
+    CACHE = bidi_shelve_cache ();              \
   } while (0)
 
 #define RESTORE_IT(pITORIG,pITCOPY,CACHE)      \
@@ -1916,7 +1916,7 @@ get_phys_cursor_geometry (struct window *w, struct glyph_row *row,
      rectangle as wide as the glyph, but use a canonical character
      width instead.  */
   wd = glyph->pixel_width - 1;
-#if defined(HAVE_NTGUI) || defined(HAVE_NS)
+#if defined (HAVE_NTGUI) || defined (HAVE_NS)
   wd++; /* Why? */
 #endif
 
@@ -2478,10 +2478,7 @@ init_iterator (struct it *it, struct window *w,
   else if (INTEGERP (w->redisplay_end_trigger))
     it->redisplay_end_trigger_charpos = XINT (w->redisplay_end_trigger);
 
-  /* Correct bogus values of tab_width.  */
-  it->tab_width = XINT (BVAR (current_buffer, tab_width));
-  if (it->tab_width <= 0 || it->tab_width > 1000)
-    it->tab_width = 8;
+  it->tab_width = SANE_TAB_WIDTH (current_buffer);
 
   /* Are lines in the display truncated?  */
   if (base_face_id != DEFAULT_FACE_ID
@@ -3135,9 +3132,9 @@ next_overlay_change (EMACS_INT pos)
 }
 
 /* How many characters forward to search for a display property or
-   display string.  Enough for a screenful of 100 lines x 50
-   characters in a line.  */
-#define MAX_DISP_SCAN 5000
+   display string.  Searching too far forward makes the bidi display
+   sluggish, especially in small windows.  */
+#define MAX_DISP_SCAN 250
 
 /* Return the character position of a display string at or after
    position specified by POSITION.  If no display string exists at or
@@ -5725,6 +5722,9 @@ reseat_at_next_visible_line_start (struct it *it, int on_newline_p)
 {
   int newline_found_p, skipped_p = 0;
   struct bidi_it bidi_it_prev;
+  int new_paragraph, first_elt, disp_prop;
+  EMACS_INT paragraph_end, disp_pos;
+  bidi_dir_t paragraph_dir;
 
   newline_found_p = forward_to_next_line_start (it, &skipped_p, &bidi_it_prev);
 
@@ -5741,6 +5741,23 @@ reseat_at_next_visible_line_start (struct it *it, int on_newline_p)
          forward_to_next_line_start (it, &skipped_p, &bidi_it_prev);
       }
 
+  /* Under bidi iteration, save the attributes of the paragraph we are
+     in, to be restored after the call to `reseat' below.  That's
+     because `reseat' overwrites them, which requires unneeded and
+     potentially expensive backward search for paragraph beginning.
+     This search is unnecessary because we will be `reseat'ed to the
+     same position where we are now, for which we already have all the
+     information we need in the bidi iterator.  */
+  if (it->bidi_p && !STRINGP (it->string))
+    {
+      new_paragraph = it->bidi_it.new_paragraph;
+      first_elt = it->bidi_it.first_elt;
+      paragraph_end = it->bidi_it.separator_limit;
+      paragraph_dir = it->bidi_it.paragraph_dir;
+      disp_pos = it->bidi_it.disp_pos;
+      disp_prop = it->bidi_it.disp_prop;
+    }
+
   /* Position on the newline if that's what's requested.  */
   if (on_newline_p && newline_found_p)
     {
@@ -5780,10 +5797,30 @@ reseat_at_next_visible_line_start (struct it *it, int on_newline_p)
              IT_BYTEPOS (*it) = it->bidi_it.bytepos;
            }
          reseat (it, it->current.pos, 0);
+         if (it->bidi_p)
+           {
+             it->bidi_it.new_paragraph = new_paragraph;
+             it->bidi_it.first_elt = first_elt;
+             it->bidi_it.separator_limit = paragraph_end;
+             it->bidi_it.paragraph_dir = paragraph_dir;
+             it->bidi_it.disp_pos = disp_pos;
+             it->bidi_it.disp_prop = disp_prop;
+           }
        }
     }
   else if (skipped_p)
-    reseat (it, it->current.pos, 0);
+    {
+      reseat (it, it->current.pos, 0);
+      if (it->bidi_p)
+       {
+         it->bidi_it.new_paragraph = new_paragraph;
+         it->bidi_it.first_elt = first_elt;
+         it->bidi_it.separator_limit = paragraph_end;
+         it->bidi_it.paragraph_dir = paragraph_dir;
+         it->bidi_it.disp_pos = disp_pos;
+         it->bidi_it.disp_prop = disp_prop;
+       }
+    }
 
   CHECK_IT (it);
 }
@@ -6444,6 +6481,8 @@ get_next_display_element (struct it *it)
 
              c = ' ';
              for (i = 0; i < cmp->glyph_len; i++)
+               /* TAB in a composition means display glyphs with
+                  padding space on the left or right.  */
                if ((c = COMPOSITION_GLYPH (cmp, i)) != '\t')
                  break;
            }
@@ -8119,7 +8158,8 @@ move_it_in_display_line_to (struct it *it,
                      && !saw_smaller_pos
                      && IT_CHARPOS (*it) > to_charpos))
                {
-                 if (!at_eob_p && IT_CHARPOS (ppos_it) < ZV)
+                 if (it->bidi_p
+                     && !at_eob_p && IT_CHARPOS (ppos_it) < ZV)
                    RESTORE_IT (it, &ppos_it, ppos_data);
                  result = MOVE_POS_MATCH_OR_ZV;
                  break;
@@ -8363,7 +8403,14 @@ move_it_to (struct it *it, EMACS_INT to_charpos, int to_x, int to_y, int to_vpos
       else if (BUFFERP (it->object)
               && (it->method == GET_FROM_BUFFER
                   || it->method == GET_FROM_STRETCH)
-              && IT_CHARPOS (*it) >= to_charpos)
+              && IT_CHARPOS (*it) >= to_charpos
+              /* Under bidi iteration, a call to set_iterator_to_next
+                 can scan far beyond to_charpos if the initial
+                 portion of the next line needs to be reordered.  In
+                 that case, give move_it_in_display_line_to another
+                 chance below.  */
+              && !(it->bidi_p
+                   && it->bidi_it.scan_dir == -1))
        skip = MOVE_POS_MATCH_OR_ZV;
       else
        skip = move_it_in_display_line_to (it, to_charpos, -1, MOVE_TO_POS);
@@ -8498,7 +8545,8 @@ move_it_vertically_backward (struct it *it, int dy)
   reseat_1 (it, it->current.pos, 1);
 
   /* We are now surely at a line start.  */
-  it->current_x = it->hpos = 0;
+  it->current_x = it->hpos = 0;        /* FIXME: this is incorrect when bidi
+                                  reordering is in effect.  */
   it->continuation_lines_width = 0;
 
   /* Move forward and see what y-distance we moved.  First move to the
@@ -8532,10 +8580,25 @@ move_it_vertically_backward (struct it *it, int dy)
   if (dy == 0)
     {
       /* DY == 0 means move to the start of the screen line.  The
-        value of nlines is > 0 if continuation lines were involved.  */
+        value of nlines is > 0 if continuation lines were involved,
+        or if the original IT position was at start of a line.  */
       RESTORE_IT (it, it, it2data);
       if (nlines > 0)
        move_it_by_lines (it, nlines);
+      /* The above code moves us to some position NLINES down,
+        usually to its first glyph (leftmost in an L2R line), but
+        that's not necessarily the start of the line, under bidi
+        reordering.  We want to get to the character position
+        that is immediately after the newline of the previous
+        line.  */
+      if (it->bidi_p && IT_CHARPOS (*it) > BEGV
+         && FETCH_BYTE (IT_BYTEPOS (*it) - 1) != '\n')
+       {
+         EMACS_INT nl_pos =
+           find_next_newline_no_quit (IT_CHARPOS (*it) - 1, -1);
+
+         move_it_to (it, nl_pos, -1, -1, -1, MOVE_TO_POS);
+       }
       bidi_unshelve_cache (it3data, 1);
     }
   else
@@ -10440,13 +10503,14 @@ static void
 store_mode_line_noprop_char (char c)
 {
   /* If output position has reached the end of the allocated buffer,
-     double the buffer's size.  */
+     increase the buffer's size.  */
   if (mode_line_noprop_ptr == mode_line_noprop_buf_end)
     {
-      int len = MODE_LINE_NOPROP_LEN (0);
-      int new_size = 2 * len * sizeof *mode_line_noprop_buf;
-      mode_line_noprop_buf = (char *) xrealloc (mode_line_noprop_buf, new_size);
-      mode_line_noprop_buf_end = mode_line_noprop_buf + new_size;
+      ptrdiff_t len = MODE_LINE_NOPROP_LEN (0);
+      ptrdiff_t size = len;
+      mode_line_noprop_buf =
+       xpalloc (mode_line_noprop_buf, &size, 1, STRING_BYTES_BOUND, 1);
+      mode_line_noprop_buf_end = mode_line_noprop_buf + size;
       mode_line_noprop_ptr = mode_line_noprop_buf + len;
     }
 
@@ -10508,9 +10572,9 @@ x_consider_frame_title (Lisp_Object frame)
       /* Do we have more than one visible frame on this X display?  */
       Lisp_Object tail;
       Lisp_Object fmt;
-      int title_start;
+      ptrdiff_t title_start;
       char *title;
-      int len;
+      ptrdiff_t len;
       struct it it;
       int count = SPECPDL_INDEX ();
 
@@ -11894,9 +11958,9 @@ hscroll_window_tree (Lisp_Object window)
                }
              hscroll = max (hscroll, XFASTINT (w->min_hscroll));
 
-             /* Don't call Fset_window_hscroll if value hasn't
-                changed because it will prevent redisplay
-                optimizations.  */
+             /* Don't prevent redisplay optimizations if hscroll
+                hasn't changed, as it will unnecessarily slow down
+                redisplay.  */
              if (XFASTINT (w->hscroll) != hscroll)
                {
                  XBUFFER (w->buffer)->prevent_redisplay_optimizations_p = 1;
@@ -13613,15 +13677,17 @@ set_cursor_from_row (struct window *w, struct glyph_row *row,
               /* A truncated row may not include PT among its
                  character positions.  Setting the cursor inside the
                  scroll margin will trigger recalculation of hscroll
-                 in hscroll_window_tree.  */
-              || (row->truncated_on_left_p && pt_old < bpos_min)
-              || (row->truncated_on_right_p && pt_old > bpos_max)
-              /* Zero-width characters produce no glyphs.  */
+                 in hscroll_window_tree.  But if a display string
+                 covers point, defer to the string-handling code
+                 below to figure this out.  */
               || (!string_seen
-                  && !empty_line_p
-                  && (row->reversed_p
-                      ? glyph_after > glyphs_end
-                      : glyph_after < glyphs_end)))
+                  && ((row->truncated_on_left_p && pt_old < bpos_min)
+                      || (row->truncated_on_right_p && pt_old > bpos_max)
+                      /* Zero-width characters produce no glyphs.  */
+                      || (!empty_line_p
+                          && (row->reversed_p
+                              ? glyph_after > glyphs_end
+                              : glyph_after < glyphs_end)))))
        {
          cursor = glyph_after;
          x = -1;
@@ -14593,19 +14659,42 @@ try_cursor_movement (Lisp_Object window, struct text_pos startp, int *scroll_ste
 
              do
                {
+                 int at_zv_p = 0, exact_match_p = 0;
+
                  if (MATRIX_ROW_START_CHARPOS (row) <= PT
                      && PT <= MATRIX_ROW_END_CHARPOS (row)
                      && cursor_row_p (row))
                    rv |= set_cursor_from_row (w, row, w->current_matrix,
                                               0, 0, 0, 0);
-                 /* As soon as we've found the first suitable row
-                    whose ends_at_zv_p flag is set, we are done.  */
-                 if (rv
-                     && MATRIX_ROW (w->current_matrix, w->cursor.vpos)->ends_at_zv_p)
+                 /* As soon as we've found the exact match for point,
+                    or the first suitable row whose ends_at_zv_p flag
+                    is set, we are done.  */
+                 at_zv_p =
+                   MATRIX_ROW (w->current_matrix, w->cursor.vpos)->ends_at_zv_p;
+                 if (rv && !at_zv_p
+                     && w->cursor.hpos >= 0
+                     && w->cursor.hpos < MATRIX_ROW_USED (w->current_matrix,
+                                                          w->cursor.vpos))
+                   {
+                     struct glyph_row *candidate =
+                       MATRIX_ROW (w->current_matrix, w->cursor.vpos);
+                     struct glyph *g =
+                       candidate->glyphs[TEXT_AREA] + w->cursor.hpos;
+                     EMACS_INT endpos = MATRIX_ROW_END_CHARPOS (candidate);
+
+                     exact_match_p =
+                       (BUFFERP (g->object) && g->charpos == PT)
+                       || (INTEGERP (g->object)
+                           && (g->charpos == PT
+                               || (g->charpos == 0 && endpos - 1 == PT)));
+                   }
+                 if (rv && (at_zv_p || exact_match_p))
                    {
                      rc = CURSOR_MOVEMENT_SUCCESS;
                      break;
                    }
+                 if (MATRIX_ROW_BOTTOM_Y (row) == last_y)
+                   break;
                  ++row;
                }
              while (((MATRIX_ROW_CONTINUATION_LINE_P (row)
@@ -14617,7 +14706,9 @@ try_cursor_movement (Lisp_Object window, struct text_pos startp, int *scroll_ste
                 loop before all the candidates were examined, signal
                 to the caller that this method failed.  */
              if (rc != CURSOR_MOVEMENT_SUCCESS
-                 && (!rv || MATRIX_ROW_CONTINUATION_LINE_P (row)))
+                 && !(rv
+                      && !MATRIX_ROW_CONTINUATION_LINE_P (row)
+                      && !row->continued_p))
                rc = CURSOR_MOVEMENT_MUST_SCROLL;
              else if (rv)
                rc = CURSOR_MOVEMENT_SUCCESS;
@@ -15077,6 +15168,7 @@ redisplay_window (Lisp_Object window, int just_this_one_p)
               || (XFASTINT (w->last_modified) >= MODIFF
                   && XFASTINT (w->last_overlay_modified) >= OVERLAY_MODIFF)))
     {
+      int d1, d2, d3, d4, d5, d6;
 
       /* If first window line is a continuation line, and window start
         is inside the modified region, but the first change is before
@@ -15098,7 +15190,14 @@ redisplay_window (Lisp_Object window, int just_this_one_p)
             compute_window_start_on_continuation_line.  (See also
             bug#197).  */
          && XMARKER (w->start)->buffer == current_buffer
-         && compute_window_start_on_continuation_line (w))
+         && compute_window_start_on_continuation_line (w)
+         /* It doesn't make sense to force the window start like we
+            do at label force_start if it is already known that point
+            will not be visible in the resulting window, because
+            doing so will move point from its correct position
+            instead of scrolling the window to bring point into view.
+            See bug#9324.  */
+         && pos_visible_p (w, PT, &d1, &d2, &d3, &d4, &d5, &d6))
        {
          w->force_start = Qt;
          SET_TEXT_POS_FROM_MARKER (startp, w->start);
@@ -16025,7 +16124,7 @@ try_window_reusing_current_matrix (struct window *w)
          if (row < bottom_row)
            {
              struct glyph *glyph = row->glyphs[TEXT_AREA] + w->cursor.hpos;
-             struct glyph *end = glyph + row->used[TEXT_AREA];
+             struct glyph *end = row->glyphs[TEXT_AREA] + row->used[TEXT_AREA];
 
              /* Can't use this optimization with bidi-reordered glyph
                 rows, unless cursor is already at point. */
@@ -16868,8 +16967,8 @@ try_window_id (struct window *w)
   {
     int this_scroll_margin, cursor_height;
 
-    this_scroll_margin = max (0, scroll_margin);
-    this_scroll_margin = min (this_scroll_margin, WINDOW_TOTAL_LINES (w) / 4);
+    this_scroll_margin =
+      max (0, min (scroll_margin, WINDOW_TOTAL_LINES (w) / 4));
     this_scroll_margin *= FRAME_LINE_HEIGHT (it.f);
     cursor_height = MATRIX_ROW (w->desired_matrix, w->cursor.vpos)->height;
 
@@ -21241,7 +21340,7 @@ else if the text is replaced by an ellipsis.  */)
       ? XFLOATINT (X)                          \
       : - 1)
 
-int
+static int
 calc_pixel_width_or_height (double *res, struct it *it, Lisp_Object prop,
                            struct font *font, int width_p, int *align_to)
 {
@@ -21374,7 +21473,7 @@ calc_pixel_width_or_height (double *res, struct it *it, Lisp_Object prop,
          if (FRAME_WINDOW_P (it->f)
              && valid_image_p (prop))
            {
-             int id = lookup_image (it->f, prop);
+             ptrdiff_t id = lookup_image (it->f, prop);
              struct image *img = IMAGE_FROM_ID (it->f, id);
 
              return OK_PIXELS (width_p ? img->width : img->height);
@@ -21671,6 +21770,8 @@ fill_composite_glyph_string (struct glyph_string *s, struct face *base_face,
     {
       int c = COMPOSITION_GLYPH (s->cmp, i);
 
+      /* TAB in a composition means display glyphs with padding space
+        on the left or right.  */
       if (c != '\t')
        {
          int face_id = FACE_FOR_CHAR (s->f, base_face->ascii_face, c,
@@ -22246,7 +22347,7 @@ compute_overhangs_and_x (struct glyph_string *s, int x, int backward_p)
   do {                                                                     \
     int face_id = (row)->glyphs[area][START].face_id;                      \
     struct face *base_face = FACE_FROM_ID (f, face_id);                            \
-    int cmp_id = (row)->glyphs[area][START].u.cmp.id;                      \
+    ptrdiff_t cmp_id = (row)->glyphs[area][START].u.cmp.id;                \
     struct composition *cmp = composition_table[cmp_id];                   \
     XChar2b *char2b;                                                       \
     struct glyph_string *first_s IF_LINT (= NULL);                         \
@@ -23035,6 +23136,7 @@ append_stretch_glyph (struct it *it, Lisp_Object object,
     IT_EXPAND_MATRIX_WIDTH (it, area);
 }
 
+#endif /* HAVE_WINDOW_SYSTEM */
 
 /* Produce a stretch glyph for iterator IT.  IT->object is the value
    of the glyph property displayed.  The value must be a list
@@ -23067,19 +23169,28 @@ append_stretch_glyph (struct it *it, Lisp_Object object,
    of the stretch should be used for the ascent of the stretch.
    ASCENT must be in the range 0 <= ASCENT <= 100.  */
 
-static void
+void
 produce_stretch_glyph (struct it *it)
 {
   /* (space :width WIDTH :height HEIGHT ...)  */
   Lisp_Object prop, plist;
   int width = 0, height = 0, align_to = -1;
-  int zero_width_ok_p = 0, zero_height_ok_p = 0;
+  int zero_width_ok_p = 0;
   int ascent = 0;
   double tem;
-  struct face *face = FACE_FROM_ID (it->f, it->face_id);
-  struct font *font = face->font ? face->font : FRAME_FONT (it->f);
+  struct face *face = NULL;
+  struct font *font = NULL;
 
-  PREPARE_FACE_FOR_DISPLAY (it->f, face);
+#ifdef HAVE_WINDOW_SYSTEM
+  int zero_height_ok_p = 0;
+
+  if (FRAME_WINDOW_P (it->f))
+    {
+      face = FACE_FROM_ID (it->f, it->face_id);
+      font = face->font ? face->font : FRAME_FONT (it->f);
+      PREPARE_FACE_FOR_DISPLAY (it->f, face);
+    }
+#endif
 
   /* List should start with `space'.  */
   xassert (CONSP (it->object) && EQ (XCAR (it->object), Qspace));
@@ -23093,8 +23204,9 @@ produce_stretch_glyph (struct it *it)
       zero_width_ok_p = 1;
       width = (int)tem;
     }
-  else if (prop = Fplist_get (plist, QCrelative_width),
-          NUMVAL (prop) > 0)
+#ifdef HAVE_WINDOW_SYSTEM
+  else if (FRAME_WINDOW_P (it->f)
+          && (prop = Fplist_get (plist, QCrelative_width), NUMVAL (prop) > 0))
     {
       /* Relative width `:relative-width FACTOR' specified and valid.
         Compute the width of the characters having the `glyph'
@@ -23117,6 +23229,7 @@ produce_stretch_glyph (struct it *it)
       x_produce_glyphs (&it2);
       width = NUMVAL (prop) * it2.pixel_width;
     }
+#endif /* HAVE_WINDOW_SYSTEM */
   else if ((prop = Fplist_get (plist, QCalign_to), !NILP (prop))
           && calc_pixel_width_or_height (&tem, it, prop, font, 1, &align_to))
     {
@@ -23136,33 +23249,40 @@ produce_stretch_glyph (struct it *it)
   if (width <= 0 && (width < 0 || !zero_width_ok_p))
     width = 1;
 
+#ifdef HAVE_WINDOW_SYSTEM
   /* Compute height.  */
-  if ((prop = Fplist_get (plist, QCheight), !NILP (prop))
-      && calc_pixel_width_or_height (&tem, it, prop, font, 0, 0))
+  if (FRAME_WINDOW_P (it->f))
     {
-      height = (int)tem;
-      zero_height_ok_p = 1;
-    }
-  else if (prop = Fplist_get (plist, QCrelative_height),
-          NUMVAL (prop) > 0)
-    height = FONT_HEIGHT (font) * NUMVAL (prop);
-  else
-    height = FONT_HEIGHT (font);
+      if ((prop = Fplist_get (plist, QCheight), !NILP (prop))
+         && calc_pixel_width_or_height (&tem, it, prop, font, 0, 0))
+       {
+         height = (int)tem;
+         zero_height_ok_p = 1;
+       }
+      else if (prop = Fplist_get (plist, QCrelative_height),
+              NUMVAL (prop) > 0)
+       height = FONT_HEIGHT (font) * NUMVAL (prop);
+      else
+       height = FONT_HEIGHT (font);
 
-  if (height <= 0 && (height < 0 || !zero_height_ok_p))
-    height = 1;
+      if (height <= 0 && (height < 0 || !zero_height_ok_p))
+       height = 1;
 
-  /* Compute percentage of height used for ascent.  If
-     `:ascent ASCENT' is present and valid, use that.  Otherwise,
-     derive the ascent from the font in use.  */
-  if (prop = Fplist_get (plist, QCascent),
-      NUMVAL (prop) > 0 && NUMVAL (prop) <= 100)
-    ascent = height * NUMVAL (prop) / 100.0;
-  else if (!NILP (prop)
-          && calc_pixel_width_or_height (&tem, it, prop, font, 0, 0))
-    ascent = min (max (0, (int)tem), height);
+      /* Compute percentage of height used for ascent.  If
+        `:ascent ASCENT' is present and valid, use that.  Otherwise,
+        derive the ascent from the font in use.  */
+      if (prop = Fplist_get (plist, QCascent),
+          NUMVAL (prop) > 0 && NUMVAL (prop) <= 100)
+       ascent = height * NUMVAL (prop) / 100.0;
+      else if (!NILP (prop)
+              && calc_pixel_width_or_height (&tem, it, prop, font, 0, 0))
+       ascent = min (max (0, (int)tem), height);
+      else
+       ascent = (height * FONT_BASE (font)) / FONT_HEIGHT (font);
+    }
   else
-    ascent = (height * FONT_BASE (font)) / FONT_HEIGHT (font);
+#endif /* HAVE_WINDOW_SYSTEM */
+    height = 1;
 
   if (width > 0 && it->line_wrap != TRUNCATE
       && it->current_x + width > it->last_visible_x)
@@ -23170,20 +23290,37 @@ produce_stretch_glyph (struct it *it)
 
   if (width > 0 && height > 0 && it->glyph_row)
     {
+      Lisp_Object o_object = it->object;
       Lisp_Object object = it->stack[it->sp - 1].string;
+      int n = width;
+
       if (!STRINGP (object))
        object = it->w->buffer;
-      append_stretch_glyph (it, object, width, height, ascent);
+#ifdef HAVE_WINDOW_SYSTEM
+      if (FRAME_WINDOW_P (it->f))
+       {
+         append_stretch_glyph (it, object, width, height, ascent);
+         it->pixel_width = width;
+         it->ascent = it->phys_ascent = ascent;
+         it->descent = it->phys_descent = height - it->ascent;
+         it->nglyphs = width > 0 && height > 0 ? 1 : 0;
+         take_vertical_position_into_account (it);
+       }
+      else
+#endif
+       {
+         it->object = object;
+         it->char_to_display = ' ';
+         it->pixel_width = it->len = 1;
+         while (n--)
+           tty_append_glyph (it);
+         it->object = o_object;
+       }
     }
-
-  it->pixel_width = width;
-  it->ascent = it->phys_ascent = ascent;
-  it->descent = it->phys_descent = height - it->ascent;
-  it->nglyphs = width > 0 && height > 0 ? 1 : 0;
-
-  take_vertical_position_into_account (it);
 }
 
+#ifdef HAVE_WINDOW_SYSTEM
+
 /* Calculate line-height and line-spacing properties.
    An integer value specifies explicit pixel value.
    A float value specifies relative value to current face height.