# HG changeset patch # User Gerd Moellmann # Date 932593432 0 # Node ID 5e507b813c1d8274246eb624f7610741386f48e1 # Parent 429ec2c075e4c1f7e4ff1f5adea575289ee2fcc3 (Fcoordinates_in_window_p): Rewritten. (scroll_command): If not acting on current_buffer, make redisplay consider all windows. (coordinates_in_window): Rewritten. (Fdelete_other_windows): Use FRAME_TOP_MARGIN instead of FRAME_MENU_BAR_LINES. (check_frame_size): Ditto. (struct save_window_data): Add frame_toolbar_lines. (Fcurrent_window_configuration): Save toolbar lines. (window_from_coordinates): Rewritten. (Vscroll_preserve_screen_position): Replacement for scroll_preserve_screen_position. (window_scroll_pixel_based): New. (Fsplit_window): Rewritten. (window_internal_width): Rewritten. (set_window_buffer): Extracted from Fset_window_buffer, with an additional argument specifying whether or not hooks may be called. (Fset_window_buffer): Call it. (Fset_window_vscroll): New. (Fwindow_vscroll): New. (Fpos_visible_in_window_p): Rewrite. (change_window_height): Return void. (make_dummy_parent): Return void. (init_window_once): Return void. (syms_of_window): Ditto. (keys_of_window): Ditto. (delete_window): Correct return with and without value. (unshow_buffer): Return void. (delete_window): Ditto. (set_window_height): Return void. (set_window_width): Ditto. (get_phys_cursor_glyph): New. (change_window_height): Ditto. (Fset_window_margins): Ditto. (window_scroll_line_based): Formerly window_scroll. (window_scroll): Call both. (window_topmost_p, window_rightmost_p): Removed because not used. (Fset_window_margins): New. (Fwindow_margins): New. (make_window): Rewritten. (replace_window): Rewritten. (Fset_window_configuration): Rewritten. (get_leaf_windows): New. (Fdelete_window): Free window matrices. (Fdelete_window): Adjust glyphs. (change_window_height): Ditto. diff -r 429ec2c075e4 -r 5e507b813c1d src/window.c --- a/src/window.c Wed Jul 21 21:43:52 1999 +0000 +++ b/src/window.c Wed Jul 21 21:43:52 1999 +0000 @@ -29,17 +29,30 @@ #include "termchar.h" #include "disptab.h" #include "keyboard.h" +#include "dispextern.h" #include "blockinput.h" -#include "dispextern.h" +#include "intervals.h" + #ifdef HAVE_X_WINDOWS #include "xterm.h" +#endif /* HAVE_X_WINDOWS */ + +#ifndef max +#define max(a, b) ((a) < (b) ? (b) : (a)) #endif + Lisp_Object Qwindowp, Qwindow_live_p, Qwindow_configuration_p; static struct window *decode_window P_ ((Lisp_Object)); - static Lisp_Object select_window_1 P_ ((Lisp_Object, int)); +static int count_windows P_ ((struct window *)); +static int get_leaf_windows P_ ((struct window *, struct window **, int)); +static void window_scroll P_ ((Lisp_Object, int, int, int)); +static void window_scroll_pixel_based P_ ((Lisp_Object, int, int, int)); +static void window_scroll_line_based P_ ((Lisp_Object, int, int, int)); + + /* This is the window in which the terminal's cursor should be left when nothing is being done with it. This must @@ -51,79 +64,102 @@ Lisp_Object selected_window; -/* The minibuffer window of the selected frame. - Note that you cannot test for minibufferness of an arbitrary window - by comparing against this; but you can test for minibufferness of +/* The mini-buffer window of the selected frame. + Note that you cannot test for mini-bufferness of an arbitrary window + by comparing against this; but you can test for mini-bufferness of the selected window. */ + Lisp_Object minibuf_window; /* Non-nil means it is the window for C-M-v to scroll - when the minibuffer is selected. */ + when the mini-buffer is selected. */ + Lisp_Object Vminibuf_scroll_window; /* Non-nil means this is the buffer whose window C-M-v should scroll. */ + Lisp_Object Vother_window_scroll_buffer; /* Non-nil means it's function to call to display temp buffers. */ + Lisp_Object Vtemp_buffer_show_function; /* If a window gets smaller than either of these, it is removed. */ + int window_min_height; int window_min_width; /* Nonzero implies Fdisplay_buffer should create windows. */ + int pop_up_windows; /* Nonzero implies make new frames for Fdisplay_buffer. */ + int pop_up_frames; /* Non-nil means use this function instead of default */ + Lisp_Object Vpop_up_frame_function; /* Function to call to handle Fdisplay_buffer. */ + Lisp_Object Vdisplay_buffer_function; /* List of buffer *names* for buffers that should have their own frames. */ + Lisp_Object Vspecial_display_buffer_names; /* List of regexps for buffer names that should have their own frames. */ + Lisp_Object Vspecial_display_regexps; /* Function to pop up a special frame. */ + Lisp_Object Vspecial_display_function; /* List of buffer *names* for buffers to appear in selected window. */ + Lisp_Object Vsame_window_buffer_names; /* List of regexps for buffer names to appear in selected window. */ + Lisp_Object Vsame_window_regexps; /* Hook run at end of temp_output_buffer_show. */ + Lisp_Object Qtemp_buffer_show_hook; /* Fdisplay_buffer always splits the largest window if that window is more than this high. */ + int split_height_threshold; /* Number of lines of continuity in scrolling by screenfuls. */ + int next_screen_context_lines; /* Incremented for each window created. */ + static int sequence_number; /* Nonzero after init_window_once has finished. */ + static int window_initialized; /* Hook to run when window config changes. */ + Lisp_Object Qwindow_configuration_change_hook; Lisp_Object Vwindow_configuration_change_hook; /* Nonzero means scroll commands try to put point at the same screen height as previously. */ -static int scroll_preserve_screen_position; - + +Lisp_Object Vscroll_preserve_screen_position; + +#if 0 /* This isn't used anywhere. */ /* Nonzero means we can split a frame even if it is "unsplittable". */ static int inhibit_frame_unsplittable; +#endif /* 0 */ #define min(a, b) ((a) < (b) ? (a) : (b)) @@ -159,23 +195,32 @@ for (i = 0; i < VECSIZE (struct window); i++) vec->contents[i] = Qnil; vec->size = VECSIZE (struct window); - p = (struct window *)vec; + p = (struct window *) vec; XSETFASTINT (p->sequence_number, ++sequence_number); XSETFASTINT (p->left, 0); XSETFASTINT (p->top, 0); XSETFASTINT (p->height, 0); XSETFASTINT (p->width, 0); XSETFASTINT (p->hscroll, 0); - XSETFASTINT (p->last_point, 0); - XSETFASTINT (p->last_point_x, 0); - XSETFASTINT (p->last_point_y, 0); p->start = Fmake_marker (); p->pointm = Fmake_marker (); XSETFASTINT (p->use_time, 0); p->frame = Qnil; p->display_table = Qnil; p->dedicated = Qnil; + p->pseudo_window_p = 0; + bzero (&p->cursor, sizeof (p->cursor)); + bzero (&p->last_cursor, sizeof (p->last_cursor)); + bzero (&p->phys_cursor, sizeof (p->phys_cursor)); + p->desired_matrix = p->current_matrix = 0; + p->phys_cursor_type = -1; + p->must_be_updated_p = 0; + XSETFASTINT (p->window_end_vpos, 0); + XSETFASTINT (p->window_end_pos, 0); + p->window_end_valid = Qnil; + p->vscroll = 0; XSETWINDOW (val, p); + XSETFASTINT (p->last_point, 0); return val; } @@ -219,12 +264,9 @@ Lisp_Object pos, window; { register struct window *w; - register int top; - register int height; + struct text_pos top; register int posint; register struct buffer *buf; - struct position posval; - int hscroll; if (NILP (pos)) posint = PT; @@ -235,50 +277,38 @@ } w = decode_window (window); - top = marker_position (w->start); - hscroll = XINT (w->hscroll); - - if (posint < top) + SET_TEXT_POS_FROM_MARKER (top, w->start); + + /* If position above window, it's not visible. */ + if (posint < CHARPOS (top)) return Qnil; - height = XFASTINT (w->height) - ! MINI_WINDOW_P (w); - buf = XBUFFER (w->buffer); if (XFASTINT (w->last_modified) >= BUF_MODIFF (buf) && XFASTINT (w->last_overlay_modified) >= BUF_OVERLAY_MODIFF (buf)) { /* If frame is up to date, - use the info recorded about how much text fit on it. */ - if (posint < BUF_Z (buf) - XFASTINT (w->window_end_pos) - || (XFASTINT (w->window_end_vpos) < height)) + use the info recorded about how much text fit on it. */ + if (posint < BUF_Z (buf) - XFASTINT (w->window_end_pos)) return Qt; return Qnil; } else { + struct it it; + if (posint > BUF_ZV (buf)) return Qnil; /* w->start can be out of range. If it is, do something reasonable. */ - if (top < BUF_BEGV (buf) || top > BUF_ZV (buf)) + if (CHARPOS (top) < BUF_BEGV (buf) + || CHARPOS (top) > BUF_ZV (buf)) return Qnil; - /* If that info is not correct, calculate afresh */ - /* BUG FIX for the 7th arg (TOHPOS). - - '0' is harmless, however, ' - (1 << (BITS_PER_SHORT - 1))' is - more appropriate here. In case of HSCROLL > 0, this can avoid - needless calculation done until (HPOS == 0). - - We want to determine if the position POSINT is in HEIGHT or - not. We don't have to do calculation until (HPOS == 0). We - can stop it when VPOS goes beyond HEIGHT. */ - posval = *compute_motion (top, 0, (hscroll ? 1 - hscroll : 0), 0, - posint, height, - (1 << (BITS_PER_SHORT - 1)), - window_internal_width (w) - 1, - hscroll, 0, w); - - return posval.vpos < height ? Qt : Qnil; + start_display (&it, w, top); + move_it_to (&it, posint, 0, it.last_visible_y, -1, + MOVE_TO_POS | MOVE_TO_X | MOVE_TO_Y); + return IT_CHARPOS (it) == posint ? Qt : Qnil; } } @@ -399,37 +429,69 @@ return 1; if it is on the window's modeline, return 2; if it is on the border between the window and its right sibling, - return 3. */ + return 3. + if it is on the window's top line, return 4; + + X and Y are frame relative pixel coordinates. */ + static int coordinates_in_window (w, x, y) register struct window *w; register int *x, *y; { - register int left = XINT (w->left); - register int right_edge = WINDOW_RIGHT_EDGE (w); - register int left_margin = WINDOW_LEFT_MARGIN (w); - register int right_margin = WINDOW_RIGHT_MARGIN (w); - register int window_height = XINT (w->height); - register int top = XFASTINT (w->top); - - if ( *x < left || *x >= right_edge - || *y < top || *y >= top + window_height) + struct frame *f = XFRAME (WINDOW_FRAME (w)); + int left_x, right_x, top_y, bottom_y; + int flags_area_width = FRAME_FLAGS_AREA_WIDTH (f); + + if (w->pseudo_window_p) + { + left_x = 0; + right_x = XFASTINT (w->width) * CANON_Y_UNIT (f); + top_y = WINDOW_DISPLAY_TOP_EDGE_PIXEL_Y (w); + bottom_y = WINDOW_DISPLAY_BOTTOM_EDGE_PIXEL_Y (w); + } + else + { + left_x = WINDOW_DISPLAY_LEFT_EDGE_PIXEL_X (w); + right_x = WINDOW_DISPLAY_RIGHT_EDGE_PIXEL_X (w); + top_y = WINDOW_DISPLAY_TOP_EDGE_PIXEL_Y (w); + bottom_y = WINDOW_DISPLAY_BOTTOM_EDGE_PIXEL_Y (w); + } + + if (*y < top_y + || *y >= bottom_y + || *x < (left_x + - flags_area_width + - (FRAME_LEFT_SCROLL_BAR_WIDTH (f) + * CANON_X_UNIT (f))) + || *x > right_x + flags_area_width) + /* Completely outside anything interesting. */ return 0; - - if (left_margin != left && *x < left_margin && *x >= left) + else if (WINDOW_WANTS_MODELINE_P (w) + && *y >= bottom_y - CURRENT_MODE_LINE_HEIGHT (w)) + /* On the mode line. */ + return 2; + else if (WINDOW_WANTS_TOP_LINE_P (w) + && *y < top_y + CURRENT_TOP_LINE_HEIGHT (w)) + /* On the top line. */ + return 4; + else if (*x < left_x || *x >= right_x) + /* Other lines than the mode line don't include flags areas and + scroll bars on the left. */ + return 0; + else if (!w->pseudo_window_p + && !WINDOW_RIGHTMOST_P (w) + && *x >= right_x - CANON_X_UNIT (f)) + /* On the border on the right side of the window? Assume that + this area begins at RIGHT_X minus a canonical char width. */ return 3; - - if (right_margin != right_edge && *x >= right_margin && *x < right_edge) - return 3; - - /* Is the character is the mode line? */ - if (*y == top + window_height - 1 - && ! MINI_WINDOW_P (w)) - return 2; - - *x -= WINDOW_LEFT_MARGIN (w); - *y -= top; - return 1; + else + { + /* Convert X and Y to window-relative pixel coordinates. */ + *x -= left_x; + *y -= top_y; + return 1; + } } DEFUN ("coordinates-in-window-p", Fcoordinates_in_window_p, @@ -442,25 +504,39 @@ If COORDINATES are in the text portion of WINDOW,\n\ the coordinates relative to the window are returned.\n\ If they are in the mode line of WINDOW, `mode-line' is returned.\n\ +If they are in the top mode line of WINDOW, `top-line' is returned.\n\ If they are on the border between WINDOW and its right sibling,\n\ `vertical-line' is returned.") (coordinates, window) register Lisp_Object coordinates, window; { + struct window *w; + struct frame *f; int x, y; + Lisp_Object lx, ly; CHECK_LIVE_WINDOW (window, 0); + w = XWINDOW (window); + f = XFRAME (w->frame); CHECK_CONS (coordinates, 1); - x = XINT (Fcar (coordinates)); - y = XINT (Fcdr (coordinates)); - - switch (coordinates_in_window (XWINDOW (window), &x, &y)) + lx = Fcar (coordinates); + ly = Fcdr (coordinates); + CHECK_NUMBER_OR_FLOAT (lx, 1); + CHECK_NUMBER_OR_FLOAT (ly, 1); + x = PIXEL_X_FROM_CANON_X (f, lx); + y = PIXEL_Y_FROM_CANON_Y (f, ly); + + switch (coordinates_in_window (w, &x, &y)) { case 0: /* NOT in window at all. */ return Qnil; case 1: /* In text part of window. */ - return Fcons (make_number (x), make_number (y)); + /* X and Y are now window relative pixel coordinates. + Convert them to canonical char units before returning + them. */ + return Fcons (CANON_X_FROM_PIXEL_X (f, x), + CANON_Y_FROM_PIXEL_Y (f, y)); case 2: /* In mode line of window. */ return Qmode_line; @@ -468,29 +544,36 @@ case 3: /* On right border of window. */ return Qvertical_line; + case 4: + return Qtop_line; + default: abort (); } } -/* Find the window containing column x, row y, and return it as a - Lisp_Object. If x, y is on the window's modeline, set *part - to 1; if it is on the separating line between the window and its - right sibling, set it to 2; otherwise set it to 0. If there is no - window under x, y return nil and leave *part unmodified. */ +/* Find the window containing frame-relative pixel position X/Y and + return it as a Lisp_Object. If X, Y is on the window's modeline, + set *PART to 1; if it is on the separating line between the window + and its right sibling, set it to 2; otherwise set it to 0. If + there is no window under X, Y return nil and leave *PART + unmodified. TOOLBAR_P non-zero means detect toolbar windows. */ + Lisp_Object -window_from_coordinates (frame, x, y, part) +window_from_coordinates (frame, x, y, part, toolbar_p) FRAME_PTR frame; int x, y; int *part; + int toolbar_p; { register Lisp_Object tem, first; + int found; tem = first = FRAME_SELECTED_WINDOW (frame); do { - int found = coordinates_in_window (XWINDOW (tem), &x, &y); + found = coordinates_in_window (XWINDOW (tem), &x, &y); if (found) { @@ -500,7 +583,17 @@ tem = Fnext_window (tem, Qt, Qlambda); } - while (! EQ (tem, first)); + while (!EQ (tem, first)); + + /* See if it's in the toolbar window, if a toolbar exists. */ + if (toolbar_p + && WINDOWP (frame->toolbar_window) + && XFASTINT (XWINDOW (frame->toolbar_window)->height) + && coordinates_in_window (XWINDOW (frame->toolbar_window), &x, &y)) + { + *part = 0; + return frame->toolbar_window; + } return Qnil; } @@ -514,17 +607,22 @@ Lisp_Object x, y, frame; { int part; + struct frame *f; if (NILP (frame)) XSETFRAME (frame, selected_frame); else CHECK_LIVE_FRAME (frame, 2); - CHECK_NUMBER (x, 0); - CHECK_NUMBER (y, 1); - - return window_from_coordinates (XFRAME (frame), - XINT (x), XINT (y), - &part); + f = XFRAME (frame); + + /* Check that arguments are integers or floats. */ + CHECK_NUMBER_OR_FLOAT (x, 0); + CHECK_NUMBER_OR_FLOAT (y, 1); + + return window_from_coordinates (f, + PIXEL_X_FROM_CANON_X (f, x), + PIXEL_Y_FROM_CANON_Y (f, y), + &part, 0); } DEFUN ("window-point", Fwindow_point, Swindow_point, 0, 1, 0, @@ -691,10 +789,10 @@ return decode_window (window)->display_table; } -/* Get the display table for use currently on window W. - This is either W's display table or W's buffer's display table. - Ignore the specified tables if they are not valid; - if no valid table is specified, return 0. */ +/* Get the display table for use on window W. This is either W's + display table or W's buffer's display table. Ignore the specified + tables if they are not valid; if no valid table is specified, + return 0. */ struct Lisp_Char_Table * window_display_table (w) @@ -722,7 +820,6 @@ register Lisp_Object window, table; { register struct window *w; - register Lisp_Object z; /* Return value. */ w = decode_window (window); w->display_table = table; @@ -792,6 +889,17 @@ p->top = o->top; p->width = o->width; p->height = o->height; + p->desired_matrix = p->current_matrix = 0; + p->vscroll = 0; + bzero (&p->cursor, sizeof (p->cursor)); + bzero (&p->last_cursor, sizeof (p->last_cursor)); + bzero (&p->phys_cursor, sizeof (p->phys_cursor)); + p->phys_cursor_type = -1; + p->must_be_updated_p = 0; + p->pseudo_window_p = 0; + XSETFASTINT (p->window_end_vpos, 0); + XSETFASTINT (p->window_end_pos, 0); + p->window_end_valid = Qnil; p->next = tem = o->next; if (!NILP (tem)) @@ -836,6 +944,7 @@ register Lisp_Object tem, parent, sib; register struct window *p; register struct window *par; + FRAME_PTR frame; /* Because this function is called by other C code on non-leaf windows, the CHECK_LIVE_WINDOW macro would choke inappropriately, @@ -858,7 +967,8 @@ par = XWINDOW (parent); windows_or_buffers_changed++; - FRAME_WINDOW_SIZES_CHANGED (XFRAME (WINDOW_FRAME (p))) = 1; + frame = XFRAME (WINDOW_FRAME (p)); + FRAME_WINDOW_SIZES_CHANGED (frame) = 1; /* Are we trying to delete any frame's selected window? */ { @@ -904,6 +1014,11 @@ unchain_marker (p->start); } + /* Free window glyph matrices. + It is sure that they are allocated again when ADJUST_GLYPHS + is called. */ + free_window_matrices (XWINDOW (FRAME_ROOT_WINDOW (frame))); + tem = p->next; if (!NILP (tem)) XWINDOW (tem)->prev = p->prev; @@ -957,6 +1072,9 @@ /* Mark this window as deleted. */ p->buffer = p->hchild = p->vchild = Qnil; + + /* Adjust glyph matrices. */ + adjust_glyphs (frame); } @@ -1366,8 +1484,6 @@ best_window = Qnil; for (;;) { - FRAME_PTR w_frame = XFRAME (WINDOW_FRAME (XWINDOW (w))); - /* Pick the next window now, since some operations will delete the current window. */ next_window = Fnext_window (w, mini ? Qt : Qnil, frame_arg); @@ -1619,7 +1735,7 @@ w = XWINDOW (window); startpos = marker_position (w->start); - top = XFASTINT (w->top) - FRAME_MENU_BAR_LINES (XFRAME (WINDOW_FRAME (w))); + top = XFASTINT (w->top) - FRAME_TOP_MARGIN (XFRAME (WINDOW_FRAME (w))); if (MINI_WINDOW_P (w) && top > 0) error ("Can't expand minibuffer to full frame"); @@ -1652,6 +1768,7 @@ set_buffer_internal (obuf); } + return Qnil; } @@ -1680,6 +1797,7 @@ CHECK_BUFFER (buffer, 0); window_loop (DELETE_BUFFER_WINDOWS, buffer, 0, frame); } + return Qnil; } @@ -1723,6 +1841,7 @@ /* The smallest acceptable dimensions for a window. Anything smaller might crash Emacs. */ + #define MIN_SAFE_WINDOW_WIDTH (2) #define MIN_SAFE_WINDOW_HEIGHT (2) @@ -1741,6 +1860,7 @@ /* If *ROWS or *COLS are too small a size for FRAME, set them to the minimum allowable size. */ + void check_frame_size (frame, rows, cols) FRAME_PTR frame; @@ -1754,8 +1874,9 @@ (FRAME_MINIBUF_ONLY_P (frame) ? MIN_SAFE_WINDOW_HEIGHT - 1 : (! FRAME_HAS_MINIBUF_P (frame)) ? MIN_SAFE_WINDOW_HEIGHT : 2 * MIN_SAFE_WINDOW_HEIGHT - 1); - if (FRAME_MENU_BAR_LINES (frame) > 0) - min_height += FRAME_MENU_BAR_LINES (frame); + + if (FRAME_TOP_MARGIN (frame) > 0) + min_height += FRAME_TOP_MARGIN (frame); if (*rows < min_height) *rows = min_height; @@ -1763,9 +1884,9 @@ *cols = MIN_SAFE_WINDOW_WIDTH; } -/* Normally the window is deleted if it gets too small. - nodelete nonzero means do not do this. - (The caller should check later and do so if appropriate) */ +/* Normally the window is deleted if it gets too small. nodelete + nonzero means do not do this. (The caller should check later and + do so if appropriate) */ void set_window_height (window, height, nodelete) @@ -1910,6 +2031,77 @@ return Qnil; } + +/* Make WINDOW display BUFFER as its contents. RUN_HOOKS_P non-zero + means it's allowed to run hooks. See make_frame for a case where + it's not allowed. */ + +void +set_window_buffer (window, buffer, run_hooks_p) + Lisp_Object window, buffer; + int run_hooks_p; +{ + struct window *w = XWINDOW (window); + struct buffer *b = XBUFFER (buffer); + int count = specpdl_ptr - specpdl; + + w->buffer = buffer; + + if (EQ (window, selected_window)) + b->last_selected_window = window; + + /* Update time stamps of buffer display. */ + if (INTEGERP (b->display_count)) + XSETINT (b->display_count, XINT (b->display_count) + 1); + b->display_time = Fcurrent_time (); + + XSETFASTINT (w->window_end_pos, 0); + XSETFASTINT (w->window_end_vpos, 0); + bzero (&w->last_cursor, sizeof w->last_cursor); + w->window_end_valid = Qnil; + XSETFASTINT (w->hscroll, 0); + set_marker_both (w->pointm, buffer, BUF_PT (b), BUF_PT_BYTE (b)); + set_marker_restricted (w->start, + make_number (b->last_window_start), + buffer); + w->start_at_line_beg = Qnil; + w->force_start = Qnil; + XSETFASTINT (w->last_modified, 0); + XSETFASTINT (w->last_overlay_modified, 0); + windows_or_buffers_changed++; + + /* We must select BUFFER for running the window-scroll-functions. + If WINDOW is selected, switch permanently. + Otherwise, switch but go back to the ambient buffer afterward. */ + if (EQ (window, selected_window)) + Fset_buffer (buffer); + /* We can't check ! NILP (Vwindow_scroll_functions) here + because that might itself be a local variable. */ + else if (window_initialized) + { + record_unwind_protect (Fset_window_buffer_unwind, Fcurrent_buffer ()); + Fset_buffer (buffer); + } + + /* Set left and right marginal area width from buffer. */ + Fset_window_margins (b->left_margin_width, b->right_margin_width, + window); + + if (run_hooks_p) + { + if (! NILP (Vwindow_scroll_functions)) + run_hook_with_args_2 (Qwindow_scroll_functions, window, + Fmarker_position (w->start)); + + if (! NILP (Vwindow_configuration_change_hook) + && ! NILP (Vrun_hooks)) + call1 (Vrun_hooks, Qwindow_configuration_change_hook); + } + + unbind_to (count, Qnil); +} + + DEFUN ("set-window-buffer", Fset_window_buffer, Sset_window_buffer, 2, 2, 0, "Make WINDOW display BUFFER as its contents.\n\ BUFFER can be a buffer or buffer name.") @@ -1918,7 +2110,7 @@ { register Lisp_Object tem; register struct window *w = decode_window (window); - int count = specpdl_ptr - specpdl; + struct buffer *b; buffer = Fget_buffer (buffer); CHECK_BUFFER (buffer, 1); @@ -1939,54 +2131,7 @@ unshow_buffer (w); } - w->buffer = buffer; - - if (EQ (window, selected_window)) - XBUFFER (w->buffer)->last_selected_window = window; - - /* Update time stamps of buffer display. */ - if (INTEGERP (XBUFFER (buffer)->display_count)) - XSETINT (XBUFFER (buffer)->display_count, - XINT (XBUFFER (buffer)->display_count) + 1); - XBUFFER (buffer)->display_time = Fcurrent_time (); - - XSETFASTINT (w->window_end_pos, 0); - w->window_end_valid = Qnil; - XSETFASTINT (w->hscroll, 0); - set_marker_both (w->pointm, buffer, - BUF_PT (XBUFFER (buffer)), BUF_PT_BYTE (XBUFFER (buffer))); - set_marker_restricted (w->start, - make_number (XBUFFER (buffer)->last_window_start), - buffer); - w->start_at_line_beg = Qnil; - w->force_start = Qnil; - XSETFASTINT (w->last_modified, 0); - XSETFASTINT (w->last_overlay_modified, 0); - windows_or_buffers_changed++; - - /* We must select BUFFER for running the window-scroll-functions. - If WINDOW is selected, switch permanently. - Otherwise, switch but go back to the ambient buffer afterward. */ - if (EQ (window, selected_window)) - Fset_buffer (buffer); - /* We can't check ! NILP (Vwindow_scroll_functions) here - because that might itself be a local variable. */ - else if (window_initialized) - { - record_unwind_protect (Fset_window_buffer_unwind, Fcurrent_buffer ()); - Fset_buffer (buffer); - } - - if (! NILP (Vwindow_scroll_functions)) - run_hook_with_args_2 (Qwindow_scroll_functions, window, - Fmarker_position (w->start)); - - if (! NILP (Vwindow_configuration_change_hook) - && ! NILP (Vrun_hooks)) - call1 (Vrun_hooks, Qwindow_configuration_change_hook); - - unbind_to (count, Qnil); - + set_window_buffer (window, buffer, 1); return Qnil; } @@ -2533,6 +2678,8 @@ o->next = new; p->parent = o->parent; p->buffer = Qt; + p->window_end_valid = Qnil; + bzero (&p->last_cursor, sizeof p->last_cursor); /* Apportion the available frame space among the two new windows */ @@ -2553,8 +2700,9 @@ XSETFASTINT (p->top, XFASTINT (o->top) + size_int); } + /* Adjust glyph matrices. */ + adjust_glyphs (fo); Fset_window_buffer (new, o->buffer); - return new; } @@ -2780,12 +2928,38 @@ XSETFASTINT (p->last_modified, 0); XSETFASTINT (p->last_overlay_modified, 0); + + /* Adjust glyph matrices. */ + adjust_glyphs (XFRAME (WINDOW_FRAME (XWINDOW (window)))); } #undef MINSIZE #undef CURBEG #undef CURSIZE - + +/* Mark window cursors off for all windows in the window tree rooted + at W by setting their phys_cursor_on_p flag to zero. Called from + xterm.c, e.g. when a frame is cleared and thereby all cursors on + the frame are cleared. */ + +void +mark_window_cursors_off (w) + struct window *w; +{ + while (w) + { + if (!NILP (w->hchild)) + mark_window_cursors_off (XWINDOW (w->hchild)); + else if (!NILP (w->vchild)) + mark_window_cursors_off (XWINDOW (w->vchild)); + else + w->phys_cursor_on_p = 0; + + w = NILP (w->next) ? 0 : XWINDOW (w->next); + } +} + + /* Return number of lines of text (not counting mode line) in W. */ int @@ -2809,28 +2983,41 @@ /* Return the number of columns in W. Don't count columns occupied by scroll bars or the vertical bar separating W from the sibling to its right. */ + int window_internal_width (w) struct window *w; { - FRAME_PTR f = XFRAME (WINDOW_FRAME (w)); + struct frame *f = XFRAME (WINDOW_FRAME (w)); int width = XINT (w->width); - /* Scroll bars occupy a few columns. */ if (FRAME_HAS_VERTICAL_SCROLL_BARS (f)) - return width - FRAME_SCROLL_BAR_COLS (f); - - /* The column of `|' characters separating side-by-side windows - occupies one column only. */ - if (!WINDOW_RIGHTMOST_P (w) && !WINDOW_FULL_WIDTH_P (w)) - return width - 1; + /* Scroll bars occupy a few columns. */ + width -= FRAME_SCROLL_BAR_COLS (f); + else if (!WINDOW_RIGHTMOST_P (w) && !WINDOW_FULL_WIDTH_P (w)) + /* The column of `|' characters separating side-by-side windows + occupies one column only. */ + width -= 1; + + /* On window-systems, areas to the left and right of the window + are used to display bitmaps there. */ + if (FRAME_WINDOW_P (f)) + width -= 2 * FRAME_FLAGS_AREA_COLS (f); return width; } - -/* Scroll contents of window WINDOW up N lines. - If WHOLE is nonzero, it means scroll N screenfuls instead. */ + +/************************************************************************ + Window Scrolling + ***********************************************************************/ + +/* Scroll contents of window WINDOW up. If WHOLE is non-zero, scroll + one screen-full, which is defined as the height of the window minus + next_screen_context_lines. If WHOLE is zero, scroll up N lines + instead. Negative values of N mean scroll down. NOERROR non-zero + means don't signal an error if we try to move over BEGV or ZV, + respectively. */ static void window_scroll (window, n, whole, noerror) @@ -2839,14 +3026,169 @@ int whole; int noerror; { + /* If we must, use the pixel-based version which is much slower than + the line-based one but can handle varying line heights. */ + if (FRAME_WINDOW_P (XFRAME (XWINDOW (window)->frame))) + window_scroll_pixel_based (window, n, whole, noerror); + else + window_scroll_line_based (window, n, whole, noerror); +} + + +/* Implementation of window_scroll that works based on pixel line + heights. See the comment of window_scroll for parameter + descriptions. */ + +static void +window_scroll_pixel_based (window, n, whole, noerror) + Lisp_Object window; + int n; + int whole; + int noerror; +{ + struct it it; + struct window *w = XWINDOW (window); + struct text_pos start; + Lisp_Object tem; + int this_scroll_margin; + int preserve_y; + + SET_TEXT_POS_FROM_MARKER (start, w->start); + + /* If PT is not visible in WINDOW, move back one half of + the screen. */ + XSETFASTINT (tem, PT); + tem = Fpos_visible_in_window_p (tem, window); + if (NILP (tem)) + { + /* Move backward half the height of the window. Performance note: + vmotion used here is about 10% faster, but would give wrong + results for variable height lines. */ + init_iterator (&it, w, PT, PT_BYTE, NULL, DEFAULT_FACE_ID); + it.current_y = it.last_visible_y; + move_it_vertically (&it, -it.last_visible_y / 2); + + /* The function move_iterator_vertically may move over more than + the specified y-distance. If it->w is small, e.g. a + mini-buffer window, we may end up in front of the window's + display area. This is the case when Start displaying at the + start of the line containing PT in this case. */ + if (it.current_y <= 0) + { + init_iterator (&it, w, PT, PT_BYTE, NULL, DEFAULT_FACE_ID); + move_it_vertically (&it, 0); + it.current_y = 0; + } + + start = it.current.pos; + } + + /* If scroll_preserve_screen_position is non-zero, we try to set + point in the same window line as it is now, so get that line. */ + if (!NILP (Vscroll_preserve_screen_position)) + { + start_display (&it, w, start); + move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS); + preserve_y = it.current_y; + } + else + preserve_y = -1; + + /* Move iterator it from start the specified distance forward or + backward. The result is the new window start. */ + start_display (&it, w, start); + if (whole) + { + int screen_full = (it.last_visible_y + - next_screen_context_lines * CANON_Y_UNIT (it.f)); + int direction = n < 0 ? -1 : 1; + move_it_vertically (&it, direction * screen_full); + } + else + move_it_by_lines (&it, n, 1); + + /* End if we end up at ZV or BEGV. */ + if ((n > 0 && IT_CHARPOS (it) == ZV) + || (n < 0 && IT_CHARPOS (it) == CHARPOS (start))) + { + if (noerror) + return; + else if (IT_CHARPOS (it) == ZV) + Fsignal (Qend_of_buffer, Qnil); + else + Fsignal (Qbeginning_of_buffer, Qnil); + } + + /* Set the window start, and set up the window for redisplay. */ + set_marker_restricted (w->start, make_number (IT_CHARPOS (it)), w->buffer); + w->start_at_line_beg = Fbolp (); + w->update_mode_line = Qt; + XSETFASTINT (w->last_modified, 0); + XSETFASTINT (w->last_overlay_modified, 0); + /* Set force_start so that redisplay_window will run the + window-scroll-functions. */ + w->force_start = Qt; + + it.current_y = it.vpos = 0; + + /* Preserve the screen position if we must. */ + if (preserve_y >= 0) + { + move_it_to (&it, -1, -1, preserve_y, -1, MOVE_TO_Y); + SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it)); + } + else + { + /* Move PT out of scroll margins. */ + this_scroll_margin = max (0, scroll_margin); + this_scroll_margin = min (this_scroll_margin, XFASTINT (w->height) / 4); + this_scroll_margin *= CANON_Y_UNIT (it.f); + + if (n > 0) + { + /* We moved the window start towards ZV, so PT may be now + in the scroll margin at the top. */ + move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS); + while (it.current_y < this_scroll_margin) + move_it_by_lines (&it, 1, 1); + SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it)); + } + else if (n < 0) + { + /* We moved the window start towards BEGV, so PT may be now + in the scroll margin at the bottom. */ + move_it_to (&it, PT, -1, + it.last_visible_y - this_scroll_margin - 1, -1, + MOVE_TO_POS | MOVE_TO_Y); + + /* Don't put point on a partially visible line at the end. */ + if (it.current_y + it.max_ascent + it.max_descent + > it.last_visible_y) + move_it_by_lines (&it, -1, 0); + + SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it)); + } + } +} + + +/* Implementation of window_scroll that works based on screen lines. + See the comment of window_scroll for parameter descriptions. */ + +static void +window_scroll_line_based (window, n, whole, noerror) + Lisp_Object window; + int n; + int whole; + int noerror; +{ register struct window *w = XWINDOW (window); - register int opoint = PT; - register int opoint_byte = PT_BYTE; + register int opoint = PT, opoint_byte = PT_BYTE; register int pos, pos_byte; register int ht = window_internal_height (w); register Lisp_Object tem; int lose; - Lisp_Object bolp, nmoved; + Lisp_Object bolp; int startpos; struct position posit; int original_vpos; @@ -2904,7 +3246,7 @@ the window-scroll-functions. */ w->force_start = Qt; - if (whole && scroll_preserve_screen_position) + if (whole && !NILP (Vscroll_preserve_screen_position)) { SET_PT_BOTH (pos, pos_byte); Fvertical_motion (make_number (original_vpos), window); @@ -2926,7 +3268,7 @@ if (top_margin <= opoint) SET_PT_BOTH (opoint, opoint_byte); - else if (scroll_preserve_screen_position) + else if (!NILP (Vscroll_preserve_screen_position)) { SET_PT_BOTH (pos, pos_byte); Fvertical_motion (make_number (original_vpos), window); @@ -2951,7 +3293,7 @@ SET_PT_BOTH (opoint, opoint_byte); else { - if (scroll_preserve_screen_position) + if (!NILP (Vscroll_preserve_screen_position)) { SET_PT_BOTH (pos, pos_byte); Fvertical_motion (make_number (original_vpos), window); @@ -2969,23 +3311,33 @@ Fsignal (Qend_of_buffer, Qnil); } } - -/* This is the guts of Fscroll_up and Fscroll_down. */ + + +/* Scroll selected_window up or down. If N is nil, scroll a + screen-full which is defined as the height of the window minus + next_screen_context_lines. If N is the symbol `-', scroll. + DIRECTION may be 1 meaning to scroll down, or -1 meaning to scroll + up. This is the guts of Fscroll_up and Fscroll_down. */ static void scroll_command (n, direction) - register Lisp_Object n; + Lisp_Object n; int direction; { register int defalt; int count = specpdl_ptr - specpdl; - /* If selected window's buffer isn't current, make it current for the moment. - But don't screw up if window_scroll gets an error. */ + xassert (abs (direction) == 1); + + /* If selected window's buffer isn't current, make it current for + the moment. But don't screw up if window_scroll gets an error. */ if (XBUFFER (XWINDOW (selected_window)->buffer) != current_buffer) { record_unwind_protect (save_excursion_restore, save_excursion_save ()); Fset_buffer (XWINDOW (selected_window)->buffer); + + /* Make redisplay consider other windows than just selected_window. */ + ++windows_or_buffers_changed; } defalt = (window_internal_height (XWINDOW (selected_window)) @@ -3102,6 +3454,7 @@ /* Don't screw up if window_scroll gets an error. */ record_unwind_protect (save_excursion_restore, save_excursion_save ()); + ++windows_or_buffers_changed; Fset_buffer (w->buffer); SET_PT (marker_position (w->pointm)); @@ -3244,12 +3597,19 @@ return Fvertical_motion (arg, window); } + + +/*********************************************************************** + Window Configuration + ***********************************************************************/ + struct save_window_data { EMACS_INT size_from_Lisp_Vector_struct; struct Lisp_Vector *next_from_Lisp_Vector_struct; Lisp_Object frame_width, frame_height, frame_menu_bar_lines; + Lisp_Object frame_toolbar_lines; Lisp_Object selected_frame; Lisp_Object current_window; Lisp_Object current_buffer; @@ -3352,7 +3712,10 @@ { register struct window *w; register struct saved_window *p; - int k; + struct window *root_window; + struct window **leaf_windows; + int n_leaf_windows; + int k, i; /* If the frame has been resized since this window configuration was made, we change the frame to the size specified in the @@ -3361,6 +3724,7 @@ int previous_frame_height = FRAME_HEIGHT (f); int previous_frame_width = FRAME_WIDTH (f); int previous_frame_menu_bar_lines = FRAME_MENU_BAR_LINES (f); + int previous_frame_toolbar_lines = FRAME_TOOLBAR_LINES (f); /* The mouse highlighting code could get screwed up if it runs during this. */ @@ -3374,6 +3738,9 @@ if (XFASTINT (data->frame_menu_bar_lines) != previous_frame_menu_bar_lines) x_set_menu_bar_lines (f, data->frame_menu_bar_lines, make_number (0)); + if (XFASTINT (data->frame_toolbar_lines) + != previous_frame_toolbar_lines) + x_set_toolbar_lines (f, data->frame_toolbar_lines, make_number (0)); #endif if (! NILP (XWINDOW (selected_window)->buffer)) @@ -3388,6 +3755,16 @@ windows_or_buffers_changed++; FRAME_WINDOW_SIZES_CHANGED (f) = 1; + /* Problem: Freeing all matrices and later allocating them again + is a serious redisplay flickering problem. What we would + really like to do is to free only those matrices not reused + below. */ + root_window = XWINDOW (FRAME_ROOT_WINDOW (f)); + leaf_windows + = (struct window **) alloca (count_windows (root_window) + * sizeof (struct window *)); + n_leaf_windows = get_leaf_windows (root_window, leaf_windows, 0); + /* Temporarily avoid any problems with windows that are smaller than they are supposed to be. */ window_min_height = 1; @@ -3526,8 +3903,24 @@ if (previous_frame_menu_bar_lines != FRAME_MENU_BAR_LINES (f)) x_set_menu_bar_lines (f, make_number (previous_frame_menu_bar_lines), make_number (0)); + if (previous_frame_toolbar_lines != FRAME_TOOLBAR_LINES (f)) + x_set_toolbar_lines (f, make_number (previous_frame_toolbar_lines), + make_number (0)); #endif + /* Now, free glyph matrices in windows that were not reused. */ + for (i = 0; i < n_leaf_windows; ++i) + if (NILP (leaf_windows[i]->buffer)) + { + /* Assert it's not reused as a combination. */ + xassert (NILP (leaf_windows[i]->hchild) + && NILP (leaf_windows[i]->vchild)); + free_window_matrices (leaf_windows[i]); + SET_FRAME_GARBAGED (f); + } + + adjust_glyphs (f); + UNBLOCK_INPUT; /* Fselect_window will have made f the selected frame, so we @@ -3605,6 +3998,56 @@ return count; } + +/* Fill vector FLAT with leaf windows under W, starting at index I. + Value is last index + 1. */ + +static int +get_leaf_windows (w, flat, i) + struct window *w; + struct window **flat; + int i; +{ + while (w) + { + if (!NILP (w->hchild)) + i = get_leaf_windows (XWINDOW (w->hchild), flat, i); + else if (!NILP (w->vchild)) + i = get_leaf_windows (XWINDOW (w->vchild), flat, i); + else + flat[i++] = w; + + w = NILP (w->next) ? 0 : XWINDOW (w->next); + } + + return i; +} + + +/* Return a pointer to the glyph W's physical cursor is on. Value is + null if W's current matrix is invalid, so that no meaningfull glyph + can be returned. */ + +struct glyph * +get_phys_cursor_glyph (w) + struct window *w; +{ + struct glyph_row *row; + struct glyph *glyph; + + if (w->phys_cursor.vpos >= 0 + && w->phys_cursor.vpos < w->current_matrix->nrows + && (row = MATRIX_ROW (w->current_matrix, w->phys_cursor.vpos), + row->enabled_p) + && row->used[TEXT_AREA] > w->phys_cursor.hpos) + glyph = row->glyphs[TEXT_AREA] + w->phys_cursor.hpos; + else + glyph = NULL; + + return glyph; +} + + static int save_window_save (window, vector, i) Lisp_Object window; @@ -3715,6 +4158,7 @@ XSETFASTINT (data->frame_width, FRAME_WIDTH (f)); XSETFASTINT (data->frame_height, FRAME_HEIGHT (f)); XSETFASTINT (data->frame_menu_bar_lines, FRAME_MENU_BAR_LINES (f)); + XSETFASTINT (data->frame_toolbar_lines, FRAME_TOOLBAR_LINES (f)); XSETFRAME (data->selected_frame, selected_frame); data->current_window = FRAME_SELECTED_WINDOW (f); XSETBUFFER (data->current_buffer, current_buffer); @@ -3753,7 +4197,137 @@ val = Fprogn (args); return unbind_to (count, val); } + +/*********************************************************************** + Marginal Areas + ***********************************************************************/ + +DEFUN ("set-window-margins", Fset_window_margins, Sset_window_margins, + 2, 3, "", + "Set width of marginal areas of window WINDOW.\n\ +If window is nil or omitted, set margins of the currently selected window.\n\ +First parameter LEFT-WIDTH specifies the number of character\n\ +cells to reserve for the left marginal area. Second parameter\n\ +RIGHT-WIDTH does the same for the right marginal area.\n\ +A nil width parameter means no margin.") + (left, right, window) + Lisp_Object window, left, right; +{ + struct window *w = decode_window (window); + struct frame *f = XFRAME (w->frame); + + if (!NILP (left)) + CHECK_NUMBER_OR_FLOAT (left, 0); + if (!NILP (right)) + CHECK_NUMBER_OR_FLOAT (right, 0); + + /* Check widths < 0 and translate a zero width to nil. + Margins that are too wide have to be checked elsewhere. */ + if ((INTEGERP (left) && XINT (left) < 0) + || (FLOATP (left) && XFLOAT (left)->data <= 0)) + XSETFASTINT (left, 0); + if (INTEGERP (left) && XFASTINT (left) == 0) + left = Qnil; + + if ((INTEGERP (right) && XINT (right) < 0) + || (FLOATP (right) && XFLOAT (right)->data <= 0)) + XSETFASTINT (right, 0); + if (INTEGERP (right) && XFASTINT (right) == 0) + right = Qnil; + + w->left_margin_width = left; + w->right_margin_width = right; + + ++windows_or_buffers_changed; + adjust_glyphs (XFRAME (WINDOW_FRAME (w))); + return Qnil; +} + + +DEFUN ("window-margins", Fwindow_margins, Swindow_margins, + 0, 1, 0, + "Get width of marginal areas of window WINDOW.\n\ +If WINDOW is omitted or nil, use the currently selected window.\n\ +Value is a cons of the form (LEFT-WIDTH . RIGHT-WIDTH).\n\ +If a marginal area does not exist, its width will be returned\n\ +as nil.") + (window) + Lisp_Object window; +{ + struct window *w = decode_window (window); + return Fcons (w->left_margin_width, w->right_margin_width); +} + + + +/*********************************************************************** + Smooth scrolling + ***********************************************************************/ + +DEFUN ("window-vscroll", Fwindow_vscroll, Swindow_vscroll, 0, 1, 0, + "Return the amount by which WINDOW is scrolled vertically.\n\ +Use the selected window if WINDOW is nil or omitted.\n\ +Value is a multiple of the canonical character height of WINDOW.") + (window) + Lisp_Object window; +{ + struct frame *f; + struct window *w; + + if (NILP (window)) + window = selected_window; + w = XWINDOW (window); + f = XFRAME (w->frame); + + if (FRAME_WINDOW_P (f)) + return CANON_Y_FROM_PIXEL_Y (f, w->vscroll); + else + return make_number (0); +} + + +DEFUN ("set-window-vscroll", Fset_window_vscroll, Sset_window_vscroll, + 1, 2, 0, + "Set amount by WINDOW should be scrolled vertically to VSCROLL.\n\ +WINDOW nil or omitted means use the selected window. VSCROLL is a\n\ +multiple of the canonical character height of WINDOW.") + (vscroll, window) + Lisp_Object vscroll, window; +{ + struct window *w; + struct frame *f; + + CHECK_NUMBER_OR_FLOAT (vscroll, 0); + + if (NILP (window)) + window = selected_window; + w = XWINDOW (window); + f = XFRAME (w->frame); + + if (FRAME_WINDOW_P (f)) + { + int old_dy = w->vscroll; + w->vscroll = min (0, CANON_Y_UNIT (f) * XFLOATINT (vscroll)); + + /* Adjust glyph matrix of the frame if the virtual display + area becomes larger than before. */ + if (w->vscroll < 0 && w->vscroll < old_dy) + adjust_glyphs (f); + + /* Prevent redisplay shortcuts. */ + XBUFFER (w->buffer)->clip_changed = 1; + } + + return Qnil; +} + + + +/*********************************************************************** + Initialization + ***********************************************************************/ + /* Return 1 if window configurations C1 and C2 describe the same state of affairs. This is used by Fequal. */ @@ -4032,10 +4606,10 @@ "*Delete any window less than this wide."); window_min_width = 10; - DEFVAR_BOOL ("scroll-preserve-screen-position", - &scroll_preserve_screen_position, + DEFVAR_LISP ("scroll-preserve-screen-position", + &Vscroll_preserve_screen_position, "*Nonzero means scroll commands move point to keep its screen line unchanged."); - scroll_preserve_screen_position = 0; + Vscroll_preserve_screen_position = Qnil; DEFVAR_LISP ("window-configuration-change-hook", &Vwindow_configuration_change_hook, @@ -4099,6 +4673,10 @@ defsubr (&Sset_window_configuration); defsubr (&Scurrent_window_configuration); defsubr (&Ssave_window_excursion); + defsubr (&Sset_window_margins); + defsubr (&Swindow_margins); + defsubr (&Swindow_vscroll); + defsubr (&Sset_window_vscroll); defsubr (&Scompare_window_configurations); }