+DEFUN ("window-line-height", Fwindow_line_height,
+ Swindow_line_height, 0, 2, 0,
+ doc: /* Return height in pixels of text line LINE in window WINDOW.
+If WINDOW is nil or omitted, use selected window.
+
+Return height of current line if LINE is omitted or nil. Return height of
+header or mode line if LINE is `header-line' and `mode-line'.
+Otherwise, LINE is a text line number starting from 0. A negative number
+counts from the end of the window.
+
+Value is a list (HEIGHT VPOS YPOS OFFBOT), where HEIGHT is the height
+in pixels of the visible part of the line, VPOS and YPOS are the
+vertical position in lines and pixels of the line, relative to the top
+of the first text line, and OFFBOT is the number of off-window pixels at
+the bottom of the text line. If there are off-window pixels at the top
+of the (first) text line, YPOS is negative.
+
+Return nil if window display is not up-to-date. In that case, use
+`pos-visible-in-window-p' to obtain the information. */)
+ (line, window)
+ Lisp_Object line, window;
+{
+ register struct window *w;
+ register struct buffer *b;
+ struct glyph_row *row, *end_row;
+ int max_y, crop, i, n;
+
+ w = decode_window (window);
+
+ if (noninteractive
+ || w->pseudo_window_p)
+ return Qnil;
+
+ CHECK_BUFFER (w->buffer);
+ b = XBUFFER (w->buffer);
+
+ /* Fail if current matrix is not up-to-date. */
+ if (NILP (w->window_end_valid)
+ || current_buffer->clip_changed
+ || current_buffer->prevent_redisplay_optimizations_p
+ || XFASTINT (w->last_modified) < BUF_MODIFF (b)
+ || XFASTINT (w->last_overlay_modified) < BUF_OVERLAY_MODIFF (b))
+ return Qnil;
+
+ if (NILP (line))
+ {
+ i = w->cursor.vpos;
+ if (i < 0 || i >= w->current_matrix->nrows
+ || (row = MATRIX_ROW (w->current_matrix, i), !row->enabled_p))
+ return Qnil;
+ max_y = window_text_bottom_y (w);
+ goto found_row;
+ }
+
+ if (EQ (line, Qheader_line))
+ {
+ if (!WINDOW_WANTS_HEADER_LINE_P (w))
+ return Qnil;
+ row = MATRIX_HEADER_LINE_ROW (w->current_matrix);
+ if (!row->enabled_p)
+ return Qnil;
+ return list4 (make_number (row->height),
+ make_number (0), make_number (0),
+ make_number (0));
+ }
+
+ if (EQ (line, Qmode_line))
+ {
+ row = MATRIX_MODE_LINE_ROW (w->current_matrix);
+ if (!row->enabled_p)
+ return Qnil;
+ return list4 (make_number (row->height),
+ make_number (0), /* not accurate */
+ make_number (WINDOW_HEADER_LINE_HEIGHT (w)
+ + window_text_bottom_y (w)),
+ make_number (0));
+ }
+
+ CHECK_NUMBER (line);
+ n = XINT (line);
+
+ row = MATRIX_FIRST_TEXT_ROW (w->current_matrix);
+ end_row = MATRIX_BOTTOM_TEXT_ROW (w->current_matrix, w);
+ max_y = window_text_bottom_y (w);
+ i = 0;
+
+ while ((n < 0 || i < n)
+ && row <= end_row && row->enabled_p
+ && row->y + row->height < max_y)
+ row++, i++;
+
+ if (row > end_row || !row->enabled_p)
+ return Qnil;
+
+ if (++n < 0)
+ {
+ if (-n > i)
+ return Qnil;
+ row += n;
+ i += n;
+ }
+
+ found_row:
+ crop = max (0, (row->y + row->height) - max_y);
+ return list4 (make_number (row->height + min (0, row->y) - crop),
+ make_number (i),
+ make_number (row->y),
+ make_number (crop));
+}
+
+