/* windows.c -- System-independant window handling
   Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>

   This file is part of Jade.

   Jade 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.

   Jade 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 Jade; see the file COPYING.	If not, write to
   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include "jade.h"
#include "jade_protos.h"

#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

_PR void messagen(u_char *, int);
_PR void message(u_char *);
_PR void messagef(u_char *, ...);
_PR void no_message(VW *);
_PR void std_message(VW *);
_PR void reset_message(VW *);
_PR void refresh_message(VW *);
_PR void windows_init(void);
_PR void windows_kill(void);
_PR void window_sweep(void);
_PR void window_prin(VALUE, VALUE);

VALUE sym_make_window_hook, sym_destroy_window_hook;

/* This can contain `dead' windows, ie vw_Window==NULL, they have been
   close'd but must hang around until we're sure all refs are dead.  */
_PR   VW	 *view_chain;
/* curr_vw is the active window */
_PR   VW	 *curr_vw;
_PR   int	  window_count;
_PR   bool  log_messages;

VW   *view_chain;
VW   *curr_vw;
int   window_count;
bool  log_messages;

_PR short def_dims[4];
short def_dims[4] = { 0, 0, 80, 24 };

static void
copy_prefs(VW *dest, VW *src)
{
    if(src)
    {
	dest->vw_MaxScroll = src->vw_MaxScroll;
	dest->vw_XStepRatio = src->vw_XStepRatio;
	dest->vw_YStepRatio = src->vw_YStepRatio;
	dest->vw_Flags = src->vw_Flags;
	dest->vw_FontName = src->vw_FontName;
#ifdef HAVE_AMIGA
	dest->vw_WindowSys.ws_FontSize = src->vw_WindowSys.ws_FontSize;
	dest->vw_WindowSys.ws_ScreenName = src->vw_WindowSys.ws_ScreenName;
#endif
    }
    else
    {
	dest->vw_MaxScroll = 20;
	dest->vw_XStepRatio = 4;
#ifdef HAVE_AMIGA
	dest->vw_YStepRatio = 0;
#else
	dest->vw_YStepRatio = 4;
#endif
	dest->vw_Flags = 0;
	dest->vw_FontName = def_font_str;
#ifdef HAVE_AMIGA
	dest->vw_WindowSys.ws_FontSize = ami_def_font_size;
	dest->vw_WindowSys.ws_ScreenName = ami_def_pub_screen;
#endif
    }
}

_PR VALUE cmd_make_window(VALUE xv, VALUE yv, VALUE wv, VALUE hv);
DEFUN("make-window", cmd_make_window, subr_make_window, (VALUE xv, VALUE yv, VALUE wv, VALUE hv), V_Subr4, DOC_make_window) /*
::doc:make_window::
make-window [X] [Y] [WIDTH] [HEIGHT]

Return a new window, it will be displaying the same buffer as the currently
active window.
::end:: */
{
    VW *vw;
    TX *tx = curr_vw ? curr_vw->vw_Tx : NULL;
    if(NUMBERP(xv))
	def_dims[0] = VNUM(xv);
    if(NUMBERP(yv))
	def_dims[1] = VNUM(yv);
    if(NUMBERP(wv))
	def_dims[2] = VNUM(wv);
    if(NUMBERP(hv))
	def_dims[3] = VNUM(hv);
    vw = mycalloc(sizeof(VW));
    if(vw)
    {
	vw->vw_Type = V_Window;
	vw->vw_Next = view_chain;
	view_chain = vw;
	copy_prefs(vw, curr_vw);
	if(sys_set_font(vw))
	{
	    vw->vw_Window = sys_new_window(curr_vw, vw, TRUE);
	    if(vw->vw_Window)
	    {
		window_count++;
		sys_new_vw(vw);
		vw->vw_BlockStatus = -1;
		vw->vw_BufferList = sym_nil;
		sys_update_dimensions(vw);
		if(tx)
		{
		    vw->vw_Tx = tx;
		    vw->vw_CursorPos = tx->tx_SavedCPos;
		    vw->vw_StartLine = tx->tx_SavedWPos.pos_Line;
		    vw->vw_StartCol = tx->tx_SavedWPos.pos_Col;
		    std_message(vw);
		    cmd_eval_hook2(sym_make_window_hook, VAL(vw));
#ifndef NOSCRLBAR
		    sys_update_scroller(vw);
#endif
		}
		vw->vw_Flags |= VWFF_FORCE_REFRESH;
		return(VAL(vw));
	    }
	    sys_unset_font(vw);
	}
	myfree(vw);
    }
    return(NULL);
}

_PR VALUE cmd_destroy_window(VALUE win);
DEFUN("destroy-window", cmd_destroy_window, subr_destroy_window, (VALUE win), V_Subr1, DOC_destroy_window) /*
::doc:destroy_window::
destroy-window [WINDOW]

Close WINDOW (or the current window), if this was the last one all files in
memory are flushed and jade will exit.
::end:: */
{
    VW *vw = WINDOWP(win) ? VWIN(win) : curr_vw;
    cmd_eval_hook2(sym_destroy_window_hook, VAL(vw));
    no_message(vw);
    /* This function is to take care of OS-independant stuff:
       releasing GCs etc...  */
    sys_kill_vw(vw);
    sys_kill_window(vw);
    sys_unset_font(vw);
    window_count--;
    /* This flags that this window is dead.  */
    vw->vw_Window = WINDOW_NIL;
    vw->vw_Tx = NULL;
    vw->vw_BufferList = sym_nil;
    if(curr_vw == vw)
    {
	while((vw = vw->vw_Next))
	{
	    if(vw->vw_Window)
	    {
		curr_vw = vw;
		return(VAL(vw));
	    }
	}
	vw = view_chain;
	while(vw && (vw != curr_vw))
	{
	    if(vw->vw_Window)
	    {
		curr_vw = vw;
		return(VAL(vw));
	    }
	    vw = vw->vw_Next;
	}
	/* No living windows left :-( we'll die soon :-(  */
	curr_vw = NULL;
	throw_value = cmd_cons(sym_quit, make_number(0)); /* experimental. */
	return(NULL);
    }
    return(VAL(curr_vw));
}

_PR VALUE cmd_sleep_window(VALUE vw);
DEFUN_INT("sleep-window", cmd_sleep_window, subr_sleep_window, (VALUE vw), V_Subr1, DOC_sleep_window, "") /*
::doc:sleep_window::
sleep-window [WINDOW]

Iconifies the current window.
::end:: */
{
    if(!WINDOWP(vw))
	vw = VAL(curr_vw);
    if(((VWIN(vw)->vw_Flags & VWFF_SLEEPING) == 0)
       && sys_sleep_vw(VWIN(vw)))
	return(vw);
    return(sym_nil);
}

_PR VALUE cmd_unsleep_window(VALUE vw);
DEFUN_INT("unsleep-window", cmd_unsleep_window, subr_unsleep_window, (VALUE vw), V_Subr1, DOC_unsleep_window, "") /*
::doc:unsleep_window::
unsleep-window [WINDOW]

Uniconifies the current window.
::end:: */
{
    if(!WINDOWP(vw))
	vw = VAL(curr_vw);
    if((VWIN(vw)->vw_Flags & VWFF_SLEEPING) && sys_unsleep_vw(VWIN(vw)))
	return(vw);
    return(sym_nil);
}

_PR VALUE cmd_next_window(VALUE vw, VALUE activ);
DEFUN_INT("next-window", cmd_next_window, subr_next_window, (VALUE vw, VALUE activ), V_Subr2, DOC_next_window, "!\np") /*
::doc:next_window::
next-window [WINDOW] [ACTIVATE]

Cycles through the open windows forwards.
::end:: */
{
    if(!WINDOWP(vw))
	vw = VAL(curr_vw->vw_Next);
    while(VWIN(vw) != curr_vw)
    {
	if(!vw)
	    vw = VAL(view_chain);
	if(VWIN(vw)->vw_Window)
	{
	    if(!NILP(activ))
	    {
		curr_vw = VWIN(vw);
		sys_activate_win(VWIN(vw));
	    }
	    return(vw);
	}
	vw = VAL(VWIN(vw)->vw_Next);
    }
    return(VAL(curr_vw));
}

void
messagen(u_char *title, int length)
{
    VW *vw = curr_vw;
    if(log_messages)
    {
	fwrite(title, 1, length, stderr);
	fputc('\n', stderr);
    }
    if((vw->vw_Flags & VWFF_SLEEPING) == 0)
    {
	str_free(vw->vw_Message);
	vw->vw_Message = str_dupn(title, length);
	vw->vw_MessageLen = length;
	vw->vw_Flags |= VWFF_REFRESH_STATUS | VWFF_MESSAGE;
    }
}

void
message(u_char *msg)
{
    messagen(msg, strlen(msg));
}

void
messagef(u_char *fmt, ...)
{
    VW *vw = curr_vw;
    va_list args;
    if((vw->vw_Flags & VWFF_SLEEPING) == 0)
    {
	u_char fmtbuff[256];
	u_long len;
	va_start(args, fmt);
	vsprintf(fmtbuff, fmt, args);
	va_end(args);
	if(log_messages)
	   fprintf(stderr, "%s\n", fmtbuff);
	str_free(vw->vw_Message);
	len = strlen(fmtbuff);
	vw->vw_Message = str_dupn(fmtbuff, len);
	vw->vw_MessageLen = len;
	vw->vw_Flags |= VWFF_REFRESH_STATUS | VWFF_MESSAGE;
    }
}

void
no_message(VW *vw)
{
    if(((vw->vw_Flags & VWFF_SLEEPING) == 0) && vw->vw_Message)
    {
	str_free(vw->vw_Message);
	vw->vw_Message = NULL;
	vw->vw_MessageLen = 0;
	vw->vw_Flags &= ~VWFF_MESSAGE;
	vw->vw_Flags |= VWFF_REFRESH_STATUS;
    }
}

void
std_message(VW *vw)
{
    if(((vw->vw_Flags & VWFF_MESSAGE) == 0)
       && ((vw->vw_Flags & VWFF_SLEEPING) == 0))
    {
	TX *tx = vw->vw_Tx;
	u_char *blk;
	u_char fmtbuff[100];
	u_long len;
	if(vw->vw_BlockStatus >= 0)
	{
	    if(vw->vw_BlockStatus == 0)
		blk = "B";
	    else
		blk = "b";
	}
	else
	    blk = "";
	str_free(vw->vw_Message);
	calc_cursor_offset(vw);
	sprintf(fmtbuff, "%s%s %c%s%s%c (%ld,%ld) %ld line(s) %s",
	    VSTR(tx->tx_BufferName),
	    ((tx->tx_Changes != tx->tx_ProperSaveChanges) && (!(tx->tx_Flags & TXFF_SPECIAL))) ? "+" : (tx->tx_Flags & TXFF_RDONLY ? "-" : ""),
	    (recurse_depth ? '[' : '('),
	    (tx->tx_ModeName ? (char *)VSTR(tx->tx_ModeName) : "generic"),
	    VSTR(tx->tx_MinorModeNameString),
	    (recurse_depth ? ']' : ')'),
	    vw->vw_LastCursorOffset + 1,
	    vw->vw_CursorPos.pos_Line + 1,
	    tx->tx_NumLines,
	    blk);
	len = strlen(fmtbuff);
	vw->vw_Message = str_dupn(fmtbuff, len);
	vw->vw_MessageLen = len;
	vw->vw_Flags |= VWFF_REFRESH_STATUS;
    }
}

void
reset_message(VW *vw)
{
    vw->vw_Flags &= ~VWFF_MESSAGE;
}

void
refresh_message(VW *vw)
{
    if(vw->vw_Message)
    {
	int len = vw->vw_MessageLen;
	if(len >= vw->vw_MaxX)
	    redraw_message(vw, vw->vw_Message + (len - vw->vw_MaxX) + 1,
			 vw->vw_MaxX - 1);
	else
	    redraw_message(vw, vw->vw_Message, len);
    }
    else
	redraw_message(vw, "", 0);
}

_PR VALUE cmd_message(VALUE string, VALUE now);
DEFUN("message", cmd_message, subr_message, (VALUE string, VALUE now), V_Subr2, DOC_message) /*
::doc:message::
message STRING [DISPLAY-NOW]

Temporarily sets the status display to STRING, this won't happen until the
window is next refreshed unless DISPLAY-NOW is non-nil.
::end:: */
{
    DECLARE1(string, STRINGP);
    message(VSTR(string));
    if(!NILP(now))
    {
	refresh_message(curr_vw);
	curr_vw->vw_Flags &= ~VWFF_REFRESH_STATUS;
#ifdef HAVE_X11
	XFlush(x11_display);
#endif
    }
    return(string);
}

_PR VALUE cmd_font_name(VALUE vw);
DEFUN("font-name", cmd_font_name, subr_font_name, (VALUE vw), V_Subr1, DOC_font_name) /*
::doc:font_name::
font-name [WINDOW]

Returns the name of the font being used in this window.
::end:: */
{
    if(!WINDOWP(vw))
	vw = VAL(curr_vw);
    return(VWIN(vw)->vw_FontName);
}

_PR VALUE var_max_scroll(VALUE val);
DEFUN("max-scroll", var_max_scroll, subr_max_scroll, (VALUE val), V_Var, DOC_max_scroll) /*
::doc:max_scroll::
Maximum scroll distance (number of lines). If a set of lines has to be
scrolled further than this the whole window is redrawn.
::end:: */
{
    VW *vw = curr_vw;
    if(val)
    {
	if(NUMBERP(val))
	    vw->vw_MaxScroll = VNUM(val);
	return(NULL);
    }
    return(make_number(vw->vw_MaxScroll));
}

_PR VALUE var_y_scroll_step_ratio(VALUE val);
DEFUN("y-scroll-step-ratio", var_y_scroll_step_ratio, subr_y_scroll_step_ratio, (VALUE val), V_Var, DOC_y_scroll_step_ratio) /*
::doc:y_scroll_step_ratio::
Controls the actual number of lines scrolled when the cursor moves out of
view. The number of lines to move the display origin is calcualted with the
formula:
  LINES_TO_SCROLL = TOTAL_LINES_IN_WINDOW / y-scroll-step-ratio
If the value is 0 then the window will be scrolled by one line.
::end:: */
{
    VW *vw = curr_vw;
    if(val)
    {
	if(NUMBERP(val))
	{
	    vw->vw_YStepRatio = VNUM(val);
	    sys_update_dimensions(vw);
	}
	return(NULL);
    }
    return(make_number(vw->vw_YStepRatio));
}

_PR VALUE var_x_scroll_step_ratio(VALUE val);
DEFUN("x-scroll-step-ratio", var_x_scroll_step_ratio, subr_x_scroll_step_ratio, (VALUE val), V_Var, DOC_x_scroll_step_ratio) /*
::doc:x_scroll_step_ratio::
Controls the actual number of columns scrolled when the cursor moves out of
view. The number of lines to move the display origin is calcualted with the
formula:
  COLUMNS_TO_SCROLL = TOTAL_COLUMNS_IN_WINDOW / x-scroll-step-ratio
If the value is 0 then the window will be scrolled by one column.
::end:: */
{
    VW *vw = curr_vw;
    if(val)
    {
	if(NUMBERP(val))
	{
	    vw->vw_XStepRatio = VNUM(val);
	    sys_update_dimensions(vw);
	}
	return(NULL);
    }
    return(make_number(vw->vw_XStepRatio));
}

_PR VALUE cmd_rect_blocks_p(VALUE vw);
DEFUN("rect-blocks-p", cmd_rect_blocks_p, subr_rect_blocks_p, (VALUE vw), V_Subr1, DOC_rect_blocks_p) /*
::doc:rect_blocks_p::
rect-blocks-p [WINDOW]

Returns t if blocks marked in WINDOW (or the current one) are treated as
rectangles.
::end:: */
{
    if(!WINDOWP(vw))
	vw = VAL(curr_vw);
    if(VWIN(vw)->vw_Flags & VWFF_RECTBLOCKS)
	return(sym_t);
    return(sym_nil);
}

_PR VALUE cmd_set_rect_blocks(VALUE vw, VALUE stat);
DEFUN("set-rect-blocks", cmd_set_rect_blocks, subr_set_rect_blocks, (VALUE vw, VALUE stat), V_Subr2, DOC_set_rect_blocks) /*
::doc:set_rect_blocks::
set-rect-blocks WINDOW STATUS

Controls whether or not blocks are taken as contiguous regions of text or as
rectangles in WINDOW. When STATUS is t rectangles are used.
::end:: */
{
    int oflags;
    if(!WINDOWP(vw))
	vw = VAL(curr_vw);
    oflags = VWIN(vw)->vw_Flags;
    if(NILP(stat))
	VWIN(vw)->vw_Flags &= ~VWFF_RECTBLOCKS;
    else
	VWIN(vw)->vw_Flags |= VWFF_RECTBLOCKS;
    if((VWIN(vw)->vw_BlockStatus == 0) && (VWIN(vw)->vw_Flags != oflags))
	set_block_refresh(VWIN(vw));
    return(stat);
}

_PR VALUE cmd_window_asleep_p(void);
DEFUN("window-asleep-p", cmd_window_asleep_p, subr_window_asleep_p, (void), V_Subr0, DOC_window_asleep_p) /*
::doc:window_asleep_p::
window-asleep-p

Returns t if window is currently iconified.
::end:: */
{
    if(curr_vw->vw_Flags & VWFF_SLEEPING)
	return(sym_t);
    return(sym_nil);
}

_PR VALUE cmd_window_count(void);
DEFUN("window-count", cmd_window_count, subr_window_count, (void), V_Subr0, DOC_window_count) /*
::doc:window_count::
window-count

Number of opened windows.
::end:: */
{
    return(make_number(window_count));
}

_PR VALUE cmd_position_window(VALUE left, VALUE top, VALUE width, VALUE height);
DEFUN("position-window", cmd_position_window, subr_position_window, (VALUE left, VALUE top, VALUE width, VALUE height), V_Subr4, DOC_position_window) /*
::doc:position_window::
position-window LEFT TOP WIDTH HEIGHT

Sets the position and dimensions of the current window. These are all
*pixel* measurememnts.
::end:: */
{
    VW *vw = curr_vw;
    DECLARE1(left, NUMBERP);
    DECLARE2(top, NUMBERP);
    DECLARE3(width, NUMBERP);
    DECLARE4(height, NUMBERP);
    sys_set_vw_pos(vw, VNUM(left), VNUM(top), VNUM(width), VNUM(height));
    return(sym_t);
}

_PR VALUE cmd_current_window(void);
DEFUN("current-window", cmd_current_window, subr_current_window, (void), V_Subr0,  DOC_current_window) /*
::doc:current_window::
current-window

Returns the currently active window. Note that this is the editor's notion
of `current' -- it doesn't necessarily mean that this is the window to which
your window system will send input events to.
::end:: */
{
    return(VAL(curr_vw));
}

_PR VALUE cmd_with_window(VALUE args);
DEFUN("with-window", cmd_with_window, subr_with_window, (VALUE args), V_SF, DOC_with_window) /*
::doc:with_window::
with-window WINDOW FORMS...

Set the editor's current window to WINDOW and evaluate FORMS, then
reinstall the original window as the current one.
::end:: */
{
    if(CONSP(args))
    {
	GCVAL gcv_args;
	VALUE res;
	PUSHGC(gcv_args, args);
	if((res = cmd_eval(VCAR(args))) && WINDOWP(res))
	{
	    VALUE oldvw = VAL(curr_vw);
	    GCVAL gcv_oldvw;
	    curr_vw = VWIN(res);
	    PUSHGC(gcv_oldvw, oldvw);
	    res = cmd_progn(VCDR(args));
	    POPGC;
	    curr_vw = VWIN(oldvw);
	}
	POPGC;
	return(res);
    }
    return(NULL);
}

_PR VALUE cmd_set_current_window(VALUE vw, VALUE activ);
DEFUN("set-current-window", cmd_set_current_window, subr_set_current_window, (VALUE vw, VALUE activ), V_Subr2,  DOC_set_current_window) /*
::doc:set_current_window::
set-current-window WINDOW [ACTIVATE-P]

Sets the window which jade reguards as current.
If ACTIVATE-P is non-nil the window will be activated with respect to the
window-system (under X11 this means warping the pointer to the top left corner
of the window as well).
::end:: */
{
    DECLARE1(vw, WINDOWP);
    curr_vw = VWIN(vw);
    if(!NILP(activ))
	sys_activate_win(VWIN(vw));
    return(VAL(curr_vw));
}

_PR VALUE cmd_window_id(VALUE vw);
DEFUN("window-id", cmd_window_id, subr_window_id, (VALUE vw), V_Subr1, DOC_window_id) /*
::doc:window_id::
window-id [WINDOW]

Returns the identifier of the physical window that the Lisp window WINDOW
points to. This is window-system dependant, under X11 it will be some integer,
under Intuition a pointer (integer) to the window structure.
::end:: */
{
    if(!WINDOWP(vw))
	vw = VAL(curr_vw);
    return(make_number((long)VWIN(vw)->vw_Window));
}

_PR VALUE cmd_font_x_size(VALUE vw);
DEFUN("font-x-size", cmd_font_x_size, subr_font_x_size, (VALUE vw), V_Subr1, DOC_font_x_size) /*
::doc:font_x_size::
font-x-size [WINDOW]

Returns the width of the window's font (in pixels).
::end:: */
{
    if(!WINDOWP(vw))
	vw = VAL(curr_vw);
    return(make_number((long)VWIN(vw)->vw_FontX));
}

_PR VALUE cmd_font_y_size(VALUE vw);
DEFUN("font-y-size", cmd_font_y_size, subr_font_y_size, (VALUE vw), V_Subr1, DOC_font_x_size) /*
::doc:font_y_size::
font-y-size [WINDOW]

Returns the height of the window's font (in pixels).
::end:: */
{
    if(!WINDOWP(vw))
	vw = VAL(curr_vw);
    return(make_number((long)VWIN(vw)->vw_FontY));
}

_PR VALUE var_status_line_cursor(VALUE val);
DEFUN("status-line-cursor", var_status_line_cursor, subr_status_line_cursor, (VALUE val), V_Var, DOC_status_line_cursor) /*
::doc:status_line_cursor::
When this window-local variable is non-nil the window's cursor is drawn at
the end of the message in the status line, not in the main display.
::end:: */
{
    if(val)
    {
	if(NILP(val))
	    curr_vw->vw_Flags &= ~VWFF_STATUS_CURS;
	else
	    curr_vw->vw_Flags |= VWFF_STATUS_CURS;
	return(val);
    }
    return(curr_vw->vw_Flags & VWFF_STATUS_CURS ? sym_t : sym_nil);
}

_PR VALUE var_buffer_list(VALUE val);
DEFUN("buffer-list", var_buffer_list, subr_buffer_list, (VALUE val), V_Var, DOC_buffer_list) /*
::doc:buffer_list::
List of buffers in most-recently-used order. Each window has it's own.
::end:: */
{
    if(val)
	curr_vw->vw_BufferList = val;
    return(curr_vw->vw_BufferList);
}

void
windows_init(void)
{
    ADD_SUBR(subr_make_window);
    ADD_SUBR(subr_destroy_window);
    ADD_SUBR(subr_sleep_window);
    ADD_SUBR(subr_unsleep_window);
    ADD_SUBR(subr_next_window);
    ADD_SUBR(subr_message);
    ADD_SUBR(subr_font_name);
    ADD_SUBR(subr_max_scroll);
    ADD_SUBR(subr_y_scroll_step_ratio);
    ADD_SUBR(subr_x_scroll_step_ratio);
    ADD_SUBR(subr_rect_blocks_p);
    ADD_SUBR(subr_set_rect_blocks);
    ADD_SUBR(subr_window_asleep_p);
    ADD_SUBR(subr_window_count);
    ADD_SUBR(subr_position_window);
    ADD_SUBR(subr_current_window);
    ADD_SUBR(subr_set_current_window);
    ADD_SUBR(subr_with_window);
    ADD_SUBR(subr_window_id);
    ADD_SUBR(subr_font_x_size);
    ADD_SUBR(subr_font_y_size);
    ADD_SUBR(subr_status_line_cursor);
    ADD_SUBR(subr_buffer_list);
    INTERN(sym_make_window_hook, "make-window-hook");
    INTERN(sym_destroy_window_hook, "destroy-window-hook");
}

void
windows_kill(void)
{
    VW *vw, *nxt;
    while(curr_vw)
	cmd_destroy_window(VAL(curr_vw));
    vw = view_chain;
    while(vw)
    {
	nxt = vw->vw_Next;
	myfree(vw);
	vw = nxt;
    }
    view_chain = NULL;
}

void
window_sweep(void)
{
    VW *vw = view_chain;
    view_chain = NULL;
    while(vw)
    {
	VW *nxt = vw->vw_Next;
	if(GC_MARKEDP(VAL(vw)))
	{
	    GC_CLR(VAL(vw));
	    vw->vw_Next = view_chain;
	    view_chain = vw;
	}
	else
	    myfree(vw);
	vw = nxt;
    }
}

void
window_prin(VALUE strm, VALUE vw)
{
    u_char buf[40];
    if(VWIN(vw)->vw_Window)
    {
#ifdef HAVE_X11
	sprintf(buf, "#<window %ld ", VWIN(vw)->vw_Window);
#else
	sprintf(buf, "#<window 0x%x ", VWIN(vw)->vw_Window);
#endif
	stream_puts(strm, buf, -1, FALSE);
	stream_puts(strm, VSTR(VWIN(vw)->vw_Tx->tx_BufferName), -1, TRUE);
	stream_putc(strm, '>');
    }
    else
	stream_puts(strm, "#<dead-window>", -1, FALSE);
}
