Function: split-window-internal

split-window-internal is a function defined in window.c.

Signature

(split-window-internal OLD PIXEL-SIZE SIDE NORMAL-SIZE &optional REFER)

Documentation

Split window OLD.

Second argument PIXEL-SIZE specifies the number of pixels of the new window. It must be a positive integer.

Third argument SIDE nil (or below) specifies that the new window shall be located below WINDOW. SIDE above means the new window shall be located above WINDOW. In both cases PIXEL-SIZE specifies the pixel height of the new window including space reserved for the mode and/or header/tab line.

SIDE t (or right) specifies that the new window shall be located on the right side of WINDOW. SIDE left means the new window shall be located on the left of WINDOW. In both cases PIXEL-SIZE specifies the width of the new window including space reserved for fringes and the scroll bar or a divider column.

Fourth argument NORMAL-SIZE specifies the normal size of the new window according to the SIDE argument. Optional fifth argument REFER is as for
'split-window'.

The new pixel and normal sizes of all involved windows must have been set correctly. See the code of split-window for how this is done.

Source Code

// Defined in /usr/src/emacs/src/window.c
// Skipping highlighting due to helpful-max-highlight.
{
  /* OLD (*o) is the window to split.  REFER (*r) is a reference window,
     either an arbitrary live window or a former live, now deleted
     window on the same frame as OLD.  NEW (*n) is the new window
     created anew or resurrected from REFER (*r), if specified.  *p
     refers either to OLD's parent window that will become NEW's parent
     window too or to a new internal window that becomes OLD's and NEW's
     new parent.  */
  struct window *o = decode_valid_window (old);
  Lisp_Object frame = WINDOW_FRAME (o);
  struct frame *f = XFRAME (frame);
  struct window *p, *n, *r, *c;
  bool horflag
    /* HORFLAG is true when we split side-by-side, false otherwise.  */
    = EQ (side, Qt) || EQ (side, Qleft) || EQ (side, Qright);
  Lisp_Object new, parent = Qnil;
  bool dead = false;

  CHECK_FIXNUM (pixel_size);
  EMACS_INT total_size
    = XFIXNUM (pixel_size) / (horflag
			   ? FRAME_COLUMN_WIDTH (f)
			   : FRAME_LINE_HEIGHT (f));

  /* Set combination_limit if we have to make a new parent window.
     We do that if either `window-combination-limit' is t, or OLD has no
     parent, or OLD is ortho-combined.  */
  bool combination_limit
    = (EQ (Vwindow_combination_limit, Qt)
       || NILP (o->parent)
       || (horflag
	   ? WINDOW_VERTICAL_COMBINATION_P (XWINDOW (o->parent))
	   : WINDOW_HORIZONTAL_COMBINATION_P (XWINDOW (o->parent))));

  /* Set up reference window.  */
  if (NILP (refer))
    {
      if (WINDOW_LIVE_P (old))
	/* OLD is live, use it as reference window.  */
	refer = old;
      else
	/* Use the frame's selected window as reference window.  */
	refer = FRAME_SELECTED_WINDOW (f);

      r = XWINDOW (refer);
    }
  else if (CONSP (refer))
    {
      /* If REFER is a cons, then its car must be a deleted, former live
	 window and its cdr must be a deleted former parent window.  Set
	 PARENT to the cdr of REFER and REFER to its car.  WINDOW and
	 REFER end up as the sole children of PARENT which replaces
	 WINDOW in the window tree.  As a special case, if REFER's cdr
	 is t, reuse REFER's car's old parent as new parent provided it
	 is a deleted former parent window.  */
      parent = Fcdr (refer);
      refer = Fcar (refer);
      r = decode_any_window (refer);

      if (!NILP (r->contents) || !BUFFERP (r->old_buffer))
	error ("REFER's car must specify a deleted, former live window");
      else if (!BUFFER_LIVE_P (XBUFFER (r->old_buffer)))
	error ("The buffer formerly shown by REFER's car has been killed");
      else if (!EQ (r->frame, frame))
	error ("REFER's car must specify a window on same frame as WINDOW");

      if (EQ (parent, Qt))
	/* If REFER's cdr is t, use the old parent of REFER's car as new
	   parent.  */
	parent = r->parent;

      p = decode_any_window (parent);

      if (!NILP (p->contents) || BUFFERP (p->old_buffer))
	error ("REFER's cdr must specify a deleted, former parent window");
      else if (!EQ (p->frame, frame))
	error ("REFER's cdr must specify window on same frame as WINDOW");

      dead = true;
    }
  else
    {
      r = decode_any_window (refer);

      if (NILP (r->contents))
	/* Presumably a deleted, former live window.  Check whether its
	   contents can be used.  */
	{
	  if (!BUFFERP (r->old_buffer))
	    error ("REFER must specify a former live window (must have shown a buffer)");
	  else if (!BUFFER_LIVE_P (XBUFFER (r->old_buffer)))
	    error ("The buffer formerly shown by REFER has been killed");
	  else if (!EQ (r->frame, frame))
	    error ("REFER must specify a window on same frame as WINDOW");

	  dead = true;
	}
      else if (!NILP (parent))
	error ("If REFER is a cons, its car must not specify a live window");
      else if (!WINDOW_LIVE_P (refer))
	error ("REFER is not a live window (does not show a buffer)");
    }

  /* The following bugs are caught by `split-window'.  */
  if (MINI_WINDOW_P (o))
    error ("Attempt to split minibuffer window");
  else if (total_size < (horflag ? 2 : 1))
    error ("Size of new window too small (after split)");
  else if (!combination_limit && !NILP (Vwindow_combination_resize))
    /* `window-combination-resize' non-nil means try to resize OLD's siblings
       proportionally.  */
    {
      struct window *op = XWINDOW (o->parent);

      /* Temporarily pretend we split the parent window.  */
      wset_new_pixel
	(op, make_fixnum ((horflag ? op->pixel_width : op->pixel_height)
			 - XFIXNUM (pixel_size)));
      if (!window_resize_check (op, horflag))
	error ("Window sizes don't fit");
      else
	/* Undo the temporary pretension.  */
	wset_new_pixel
	  (op, make_fixnum (horflag ? op->pixel_width : op->pixel_height));
    }
  else
    {
      if (!window_resize_check (o, horflag))
	error ("Resizing old window failed");
      else if (XFIXNUM (pixel_size) + XFIXNUM (o->new_pixel)
	       != (horflag ? o->pixel_width : o->pixel_height))
	error ("Sum of sizes of old and new window don't fit");
    }

  /* This is our point of no return.  */
  if (combination_limit)
    {
      /* Save the old value of o->normal_cols/lines.  It gets corrupted
	 by make_parent_window and we need it below for assigning it to
	 p->new_normal.  */
      Lisp_Object new_normal
	= horflag ? o->normal_cols : o->normal_lines;

      if (NILP (parent))
	{
	  parent = make_parent_window (frame);
	  p = XWINDOW (parent);
	}
      else
	/* Pacify GCC.  */
	p = XWINDOW (parent);

      replace_window (old, parent, true);
      wset_next (o, Qnil);
      wset_prev (o, Qnil);
      wset_parent (o, parent);
      wset_combination (p, horflag, old);

      if (EQ (Vwindow_combination_limit, Qt))
	/* Store t in the new parent's combination_limit slot to avoid
	   that its children get merged into another window.  */
	wset_combination_limit (p, Qt);
      /* These get applied below.  */
      wset_new_pixel
	(p, make_fixnum (horflag ? o->pixel_width : o->pixel_height));
      wset_new_total
	(p, make_fixnum (horflag ? o->total_cols : o->total_lines));
      wset_new_normal (p, new_normal);
    }
  else
    p = XWINDOW (o->parent);

  fset_redisplay (f);

  if (dead)
    new = refer;
  else
    new = make_window ();

  n = XWINDOW (new);
  wset_frame (n, frame);
  wset_parent (n, o->parent);

  if (EQ (side, Qabove) || EQ (side, Qleft))
    {
      wset_prev (n, o->prev);
      if (NILP (n->prev))
	wset_combination (p, horflag, new);
      else
	wset_next (XWINDOW (n->prev), new);
      wset_next (n, old);
      wset_prev (o, new);
    }
  else
    {
      wset_next (n, o->next);
      if (!NILP (n->next))
	wset_prev (XWINDOW (n->next), new);
      wset_prev (n, old);
      wset_next (o, new);
    }

  n->window_end_valid = false;
  n->last_cursor_vpos = 0;

  if (!dead)
    {
      /* Get special geometry settings from reference window.  */
      n->left_margin_cols = r->left_margin_cols;
      n->right_margin_cols = r->right_margin_cols;
      n->left_fringe_width = r->left_fringe_width;
      n->right_fringe_width = r->right_fringe_width;
      n->fringes_outside_margins = r->fringes_outside_margins;
      n->scroll_bar_width = r->scroll_bar_width;
      n->scroll_bar_height = r->scroll_bar_height;
      wset_vertical_scroll_bar_type (n, r->vertical_scroll_bar_type);
      wset_horizontal_scroll_bar_type (n, r->horizontal_scroll_bar_type);
    }

  /* Directly assign orthogonal coordinates and sizes.  */
  if (horflag)
    {
      n->pixel_top = o->pixel_top;
      n->top_line = o->top_line;
      n->pixel_height = o->pixel_height;
      n->total_lines = o->total_lines;
    }
  else
    {
      n->pixel_left = o->pixel_left;
      n->left_col = o->left_col;
      n->pixel_width = o->pixel_width;
      n->total_cols = o->total_cols;
    }

  /* Iso-coordinates and sizes are assigned by window_resize_apply,
     get them ready here.  */
  wset_new_pixel (n, pixel_size);
  EMACS_INT sum = 0;
  c = XWINDOW (p->contents);
  while (c)
    {
      if (c != n)
	sum = sum + XFIXNUM (c->new_total);
      c = NILP (c->next) ? 0 : XWINDOW (c->next);
    }

  wset_new_total (n, make_fixnum ((horflag
				   ? p->total_cols
				   : p->total_lines)
				  - sum));
  wset_new_normal (n, normal_size);

  block_input ();

  if (dead)
    {
      /* Get dead window back its old buffer and markers.  */
      wset_buffer (n, n->old_buffer);
      set_marker_restricted
	(n->start, make_fixnum (XMARKER (n->start)->charpos), n->contents);
      set_marker_restricted
	(n->pointm, make_fixnum (XMARKER (n->pointm)->charpos), n->contents);
      set_marker_restricted
	(n->old_pointm, make_fixnum (XMARKER (n->old_pointm)->charpos),
	 n->contents);

      Vwindow_list = Qnil;
      /* Remove window from the table of dead windows.  */
      Fremhash (make_fixnum (n->sequence_number),
		window_dead_windows_table);
    }

  window_resize_apply (p, horflag);
  adjust_frame_glyphs (f);

  set_window_buffer (new, r->contents, true, true);

  FRAME_WINDOW_CHANGE (f) = true;
  unblock_input ();

  return new;
}