/*
 *	X11 support, Dave Lemke, 11/91
 *	X Toolkit support, Kevin Buettner, 2/94
 *
 * $Header: /usr/build/vile/vile/RCS/x11.c,v 1.252 2000/10/10 23:18:32 tom Exp $
 *
 */

/*
 * Widget set selection.
 *
 * You must have exactly one of the following defined
 *
 *    NO_WIDGETS	-- Use only Xlib and X toolkit (Xt)
 *    ATHENA_WIDGETS	-- Use Xlib, Xt, and Xaw widget set
 *    MOTIF_WIDGETS	-- Use Xlib, Xt, and Motif widget set
 *    OL_WIDGETS	-- Use Xlib, Xt, and Openlook widget set
 *
 * We derive/set from the configure script some flags that allow intermediate
 * configurations between NO_WIDGETS and ATHENA_WIDGETS
 *
 *    KEV_WIDGETS
 *    OPT_KEV_DRAGGING
 *    OPT_KEV_SCROLLBARS
 */

#define	termdef	1			/* don't define "term" external */

#define NEED_X_INCLUDES 1
#include	"estruct.h"
#include	"edef.h"
#include	"nefunc.h"
#include	"pscreen.h"

#include	<X11/cursorfont.h>

#if defined(lint) && HAVE_X11_INTRINSICI_H
#include	<X11/IntrinsicI.h>
#endif

#if XtSpecificationRelease < 4
#define XtPointer caddr_t
#endif

#if DISP_X11 && XTOOLKIT

/* sanity-check, so we know it's safe to not nest ifdef's */

#if NO_WIDGETS
# if ATHENA_WIDGETS || MOTIF_WIDGETS || OL_WIDGETS
   >make an error<
# endif
#else
# if ATHENA_WIDGETS
#  if MOTIF_WIDGETS || OL_WIDGETS
    >make an error<
#  endif
# else
#  if MOTIF_WIDGETS
#   if OL_WIDGETS
     >make an error<
#   endif
#  else
#   if !OL_WIDGETS
     >make an error<
#   endif
#  endif
# endif
#endif

#ifndef OPT_KEV_SCROLLBARS
# if NO_WIDGETS
#  define OPT_KEV_SCROLLBARS 1
# endif
#endif

#ifndef OPT_KEV_DRAGGING
# if NO_WIDGETS
#  define OPT_KEV_DRAGGING 1
# endif
#endif

#ifndef KEV_WIDGETS
# if NO_WIDGETS || OPT_KEV_SCROLLBARS
#  define KEV_WIDGETS 1
# endif
#endif

#ifndef OPT_XAW_SCROLLBARS
# if ATHENA_WIDGETS && !OPT_KEV_SCROLLBARS
#  define OPT_XAW_SCROLLBARS 1
# endif
#endif

#define MY_CLASS	"XVile"

#if SYS_VMS
#undef SYS_UNIX
#define coreWidgetClass widgetClass /* patch for VMS 5.4-3 (dickey) */
#endif

/* redefined in X11/Xos.h */
#undef strchr
#undef strrchr

#if ATHENA_WIDGETS
#if HAVE_LIB_XAW
#include	<X11/Xaw/Form.h>
#include	<X11/Xaw/Grip.h>
#include	<X11/Xaw/Scrollbar.h>
#endif
#if HAVE_LIB_XAW3D
#include	<X11/Xaw3d/Form.h>
#include	<X11/Xaw3d/Grip.h>
#include	<X11/Xaw3d/Scrollbar.h>
#endif
#if HAVE_LIB_NEXTAW
#include	<X11/neXtaw/Form.h>
#include	<X11/neXtaw/Grip.h>
#include	<X11/neXtaw/Scrollbar.h>
#endif
#endif /* OPT_XAW_SCROLLBARS */

#if OL_WIDGETS
#undef BANG
#include	<Xol/OpenLook.h>
#include	<Xol/Form.h>
#include	<Xol/BulletinBo.h>
#include	<Xol/Slider.h>
#include	<Xol/Scrollbar.h>
#endif /* OL_WIDGETS */

#include	<X11/Shell.h>
#include	<X11/keysym.h>
#include	<X11/Xatom.h>

#if MOTIF_WIDGETS
#include	<Xm/Form.h>
#include	<Xm/PanedW.h>
#include	<Xm/ScrollBar.h>
#include	<Xm/SashP.h>
#if defined(lint) && HAVE_XM_XMP_H
#include	<Xm/XmP.h>
#endif
#endif /* MOTIF_WIDGETS */

#if OPT_X11_ICON
#if HAVE_LIBXPM
#include <X11/xpm.h>
#endif
#endif

#define	XCalloc(type)	typecalloc(type)

#define	absol(x)	((x) > 0 ? (x) : -(x))
#define	CEIL(a,b)	((a + b - 1) / (b))

#define onMsgRow(tw)	(ttrow == (int)(tw->rows - 1))

/* XXX -- use xcutsel instead */
#undef	SABER_HACK		/* hack to support Saber since it doesn't do
				 * selections right */

#undef btop		/* defined in SunOS includes */

#define PANE_WIDTH_MAX 200

#if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
# define PANE_WIDTH_DEFAULT 15
# define PANE_WIDTH_MIN 7
#else
# if MOTIF_WIDGETS || OL_WIDGETS
#  define PANE_WIDTH_DEFAULT 20
#  define PANE_WIDTH_MIN 10
# endif
#endif

#ifndef XtNmenuHeight
#define XtNmenuHeight "menuHeight"
#endif

#ifndef XtCMenuHeight
#define XtCMenuHeight "MenuHeight"
#endif

#if OPT_MENUS_COLORED
#define XtNmenuBackground "menuBackground"
#define XtNmenuForeground "menuForeground"
#define XtCMenuBackground "MenuBackground"
#define XtCMenuForeground "MenuForeground"
#endif

#define MENU_HEIGHT_DEFAULT 20

#define MINCOLS	30
#define MINROWS MINWLNS
#define MAXSBS	((MAXROWS) / 2)

/* Blinking cursor toggle...defined this way to leave room for future
 * cursor flags.
 */
#define	BLINK_TOGGLE	0x1

/*
 * Fonts searched flags
 */

#define FSRCH_BOLD	0x1
#define FSRCH_ITAL	0x2
#define FSRCH_BOLDITAL	0x4

/* Keyboard queue size */
#define KQSIZE 64

static Display *dpy;

#if OPT_MENUS
#if ATHENA_WIDGETS
#include <X11/Xaw/SimpleMenu.h>
#include <X11/Xaw/Box.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Paned.h>
#endif
#if MOTIF_WIDGETS
#include <Xm/RowColumn.h>
#endif
    Widget	menub;
#endif

#if OPT_XAW_SCROLLBARS
typedef struct _scroll_info {
    int		totlen;		/* total length of scrollbar */
} ScrollInfo;
#else
#if OPT_KEV_SCROLLBARS
typedef struct _scroll_info {
    int		top;		/* top of "thumb" */
    int		bot;		/* bottom of "thumb" */
    int		totlen;		/* total length of scrollbar */
    Boolean	exposed;	/* has scrollbar received expose event? */
} ScrollInfo;
#endif
#endif

typedef struct _text_gc {
    GC		gc;
    Boolean	reset;
} ColorGC;

typedef struct _text_win {
    /* X stuff */
    Window	win;		/* window corresponding to screen */
    XtAppContext
		app_context;	/* application context */
    Widget	top_widget;	/* top level widget */
    Widget	screen;		/* screen widget */
    unsigned	screen_depth;
#if ATHENA_WIDGETS && OPT_MENUS
    Widget	pane_widget;	/* pane widget, actually a form */
    Widget	menu_widget;	/* menu-bar widget, actually a box */
#endif
    Widget	form_widget;	/* form enclosing text-display + scrollbars */
    Widget	pane;		/* panes in which scrollbars live */

    int		maxscrollbars;	/* how many scrollbars, sliders, etc. */
    Widget	*scrollbars;	/* the scrollbars */
    int		nscrollbars;	/* number of currently active scroll bars */
#if OL_WIDGETS
    Widget	*sliders;
#endif

#if OPT_MENUS_COLORED
    Pixel	menubar_fg;	/* color of the menubar */
    Pixel	menubar_bg;
#endif
#if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
    Pixel	scrollbar_fg;
    Pixel	scrollbar_bg;
    Bool	slider_is_solid;
    Bool	slider_is_3D;
    GC		scrollbargc;	/* graphics context for scrollbar "thumb" */
    Pixmap	trough_pixmap;
    Pixmap	slider_pixmap;
    ScrollInfo	*scrollinfo;
    Widget	*grips;		/* grips for resizing scrollbars */
    XtIntervalId scroll_repeat_id;
    ULONG	scroll_repeat_timeout;
#endif	/* OPT_KEV_SCROLLBARS */
#if OPT_XAW_SCROLLBARS
    Pixmap	thumb_bm;	/* bitmap for scrollbar thumb */
#endif
    ULONG	scroll_repeat_interval;
    XtIntervalId blink_id;
    int		blink_status;
    int		blink_interval;
    Bool	exposed;	/* Have we received any expose events? */
    int		visibility;	/* How visible is the window? */

    int		base_width;	/* width with screen widgets' width zero */
    int		base_height;
    UINT	pane_width;	/* full width of scrollbar pane */
    Dimension	menu_height;	/* height of menu-bar */
    Dimension	top_width;	/* width of top widget as of last resize */
    Dimension	top_height;	/* height of top widget as of last resize */

    int		fsrch_flags;	/* flags which indicate which fonts have
				 * been searched for
				 */
    XFontStruct *pfont;		/* Normal font */
    XFontStruct *pfont_bold;
    XFontStruct *pfont_ital;
    XFontStruct *pfont_boldital;
    GC		textgc;
    GC		reversegc;
    GC		selgc;
    GC		revselgc;
    int		is_color_cursor;
    GC		cursgc;
    GC		revcursgc;
    GC		modeline_focus_gc;	/* GC for modeline w/ focus */
    GC		modeline_gc;		/* GC for other modelines  */
    ColorGC	fore_color[NCOLORS];
    ColorGC	back_color[NCOLORS];
    Boolean	bg_follows_fg;
    Pixel	fg;
    Pixel	bg;
    Pixel	default_fg;
    Pixel	default_bg;
    Pixel	colors_fg[NCOLORS];
    Pixel	colors_bg[NCOLORS];
    Pixel	modeline_fg;
    Pixel	modeline_bg;
    Pixel	modeline_focus_fg;
    Pixel	modeline_focus_bg;
    Pixel	selection_fg;
    Pixel	selection_bg;
    int		char_width,
		char_ascent,
		char_descent,
		char_height;
    Bool	left_ink,	/* font has "ink" past bounding box on left */
		right_ink;	/* font has "ink" past bounding box on right */
    int		wheel_scroll_amount;
    char *	geometry;
    char *	starting_fontname;	/* name of font at startup */
    char *	fontname;		/* name of current font */
    Bool	focus_follows_mouse;
    Bool	fork_on_startup;
    Bool	scrollbar_on_left;
    Bool	update_window_name;
    Bool	update_icon_name;
    Bool	persistent_selections;
    Bool	selection_sets_DOT;

    /* text stuff */
    Bool	reverse;
    unsigned	rows,
		cols;
    Bool	show_cursor;

    /* cursor stuff */
    Pixel	cursor_fg;
    Pixel	cursor_bg;

    /* pointer stuff */
    Pixel	pointer_fg;
    Pixel	pointer_bg;
    Cursor	normal_pointer;
#if OPT_WORKING
    Cursor	watch_pointer;
    Bool	want_to_work;
#endif

    /* selection stuff */
    String	multi_click_char_class;	/* ?? */
    Time	lasttime;	/* for multi-click */
    Time	click_timeout;
    int		numclicks;
    Bool	have_selection;
    Bool	wipe_permitted;
    Bool	was_on_msgline;
    Bool	did_select;
    Bool	pasting;
    MARK	prevDOT;	/* DOT prior to selection */
    XtIntervalId sel_scroll_id;

    /* key press queue */
    int		kqhead;
    int		kqtail;
    int		kq[KQSIZE];

    /* Special translations */
    XtTranslations my_scrollbars_trans;
#if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
    XtTranslations my_resizeGrip_trans;
#endif
}	    TextWindowRec, *TextWindow;


static	TextWindowRec cur_win_rec;
static	TextWindow cur_win = &cur_win_rec;
static	TBUFF	*PasteBuf;

static	Atom	atom_WM_PROTOCOLS;
static	Atom	atom_WM_DELETE_WINDOW;
static	Atom	atom_FONT;
static	Atom	atom_FOUNDRY;
static	Atom	atom_WEIGHT_NAME;
static	Atom	atom_SLANT;
static	Atom	atom_SETWIDTH_NAME;
static	Atom	atom_PIXEL_SIZE;
static	Atom	atom_RESOLUTION_X;
static	Atom	atom_RESOLUTION_Y;
static	Atom	atom_SPACING;
static	Atom	atom_AVERAGE_WIDTH;
static	Atom	atom_CHARSET_REGISTRY;
static	Atom	atom_CHARSET_ENCODING;
static	Atom	atom_TARGETS;
static	Atom	atom_MULTIPLE;
static	Atom	atom_TIMESTAMP;
static	Atom	atom_TEXT;
static	Atom	atom_CLIPBOARD;

#if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
static	Cursor	curs_sb_v_double_arrow;
static	Cursor	curs_sb_up_arrow;
static	Cursor	curs_sb_down_arrow;
static	Cursor	curs_sb_left_arrow;
static	Cursor	curs_sb_right_arrow;
static	Cursor	curs_double_arrow;
#endif

#if MOTIF_WIDGETS
static Bool lookfor_sb_resize = FALSE;
#endif

struct eventqueue {
    XEvent event;
    struct eventqueue *next;
};

static struct eventqueue *evqhead = NULL;
static struct eventqueue *evqtail = NULL;

static	int	x_getc   (void);

static	void	x_open   (void),
		x_close  (void),
		x_flush  (void),
		x_beep   (void),
		x_rev    ( UINT state );

#if OPT_COLOR
static	void	x_fcol   ( int color );
static	void	x_bcol   ( int color );
static	void	x_ccol   ( int color );
#else
#define	x_fcol	nullterm_setfore
#define	x_bcol	nullterm_setback
#define	x_ccol	nullterm_setccol
#endif

static	void	x_setpal(const char *s);
static	void	x_scroll(int from, int to, int count);

static	int	x_watchfd(int fd, WATCHTYPE type, long *idp);
static	void	x_unwatchfd(int fd, long id);

static	int	set_character_class(char *s);
static	void	x_touch (TextWindow tw, int sc, int sr, UINT ec, UINT er);
static	void	x_paste_selection (Atom selection);
static	void	x_own_selection(Atom selection);
static	Boolean	x_get_selected_text(UCHAR **datp, SIZE_T *lenp);
static	Boolean	x_get_clipboard_text(UCHAR **datp, SIZE_T *lenp);
static	Boolean	x_convert_selection (Widget w, Atom *selection, Atom *target,
			Atom *type, XtPointer *value, unsigned long *length,
			int *format);
static	void	extend_selection (TextWindow tw, int nr, int nc, Bool wipe);
static	void	x_process_event (Widget w, XtPointer unused, XEvent *ev,
			Boolean *continue_to_dispatch);
static	void	x_configure_window (Widget w, XtPointer unused, XEvent *ev,
			Boolean *continue_to_dispatch);
static	void	x_change_focus (Widget w, XtPointer unused, XEvent *ev,
			Boolean *continue_to_dispatch);
static	void	x_typahead_timeout (XtPointer flagp, XtIntervalId *id);
static	void	x_key_press (Widget w, XtPointer unused, XEvent *ev,
			Boolean *continue_to_dispatch);
static	void	x_wm_delwin (Widget w, XtPointer unused, XEvent *ev,
			Boolean *continue_to_dispatch);
static	void	x_start_autocolor_timer (void);
static	void	x_autocolor_timeout (XtPointer flagp, XtIntervalId *id);
static	void	x_stop_autocolor_timer (void);
#if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
static	Boolean	too_light_or_too_dark (Pixel pixel);
#endif
#if OPT_KEV_SCROLLBARS
static	Boolean	alloc_shadows (Pixel pixel, Pixel *light, Pixel *dark);
#endif
static	XFontStruct *query_font (TextWindow tw, const char *fname);
static	void	configure_bar (Widget w, XEvent *event, String *params,
			Cardinal *num_params);
static	int	check_scrollbar_allocs (void);
static	void	kqinit(TextWindow tw);
static	int	kqempty(TextWindow tw);
static	int	kqfull(TextWindow tw);
static	void	kqadd (TextWindow tw, int c);
static	int	kqpop(TextWindow tw);
static	void	display_cursor (XtPointer client_data, XtIntervalId *idp);
#if 0
static	void	check_visuals (void);
#endif
#if MOTIF_WIDGETS
static	void	grip_moved (Widget w, XtPointer unused, XEvent *ev,
			Boolean *continue_to_dispatch);
static	void	pane_button (Widget w, XtPointer unused, XEvent *ev,
			Boolean *continue_to_dispatch);
#endif /* MOTIF_WIDGETS */
#if OPT_KEV_SCROLLBARS
static	void	x_expose_scrollbar (Widget w, XtPointer unused, XEvent *ev,
			Boolean *continue_to_dispatch);
#endif /* OPT_KEV_SCROLLBARS */
#if OPT_KEV_DRAGGING
static	void	repeat_scroll (XtPointer count, XtIntervalId  *id);
#endif
#if OPT_LOCALE
static	void	init_xlocale(void);
#endif
#if OPT_WORKING
static	void	x_set_watch_cursor(int onflag);
static	int	x_has_events(void);
#else
#define x_has_events() (XtAppPending(cur_win->app_context) & XtIMXEvent)
#endif /* OPT_WORKING */
static	int	evqempty (void);
static	void	evqadd(const XEvent *evp);

#define	FONTNAME	"7x13"

TERM	    term = {
    0,				/* these four values are set dynamically at
				 * open time */
    0,
    0,
    0,
    0,
    x_open,
    x_close,
    nullterm_kopen,
    nullterm_kclose,
    x_getc,
    psc_putchar,
    tttypahead,
    psc_flush,
    psc_move,
    psc_eeol,
    psc_eeop,
    x_beep,
    x_rev,
    nullterm_setdescrip,
    x_fcol,
    x_bcol,
    x_setpal,
    x_ccol,
    x_scroll,
    x_flush,
    nullterm_icursor,
#if OPT_TITLE
    x_set_window_name,
#else
    nullterm_settitle,
#endif
    x_watchfd,
    x_unwatchfd,
    nullterm_cursorvis,
};


#define	x_width(tw)		((tw)->cols * (tw)->char_width)
#define	x_height(tw)		((tw)->rows * (tw)->char_height)
#define	x_pos(tw, c)		((c) * (tw)->char_width)
#define	y_pos(tw, r)		((r) * (tw)->char_height)
#define	text_y_pos(tw, r)	(y_pos((tw), (r)) + (tw)->char_ascent)
#define min(a,b)		((a) < (b) ? (a) : (b))
#define max(a,b)		((a) > (b) ? (a) : (b))



#if OPT_LOCALE
static char    *rs_inputMethod = "";	/* XtNinputMethod */
static char    *rs_preeditType = NULL;	/* XtNpreeditType */
static XIC      Input_Context;		/* input context */
#endif

#if KEV_WIDGETS
/* We define our own little bulletin board widget here...if this gets
 * too unwieldly, we should move it to another file.
 */

#include	<X11/IntrinsicP.h>

/* New fields for the Bb widget class record */
typedef struct {int empty;} BbClassPart;

/* Class record declaration */
typedef struct _BbClassRec {
    CoreClassPart	core_class;
    CompositeClassPart  composite_class;
    BbClassPart	bb_class;
} BbClassRec;

extern BbClassRec bbClassRec;

/* Instance declaration */
typedef struct _BbRec {
    CorePart	    core;
    CompositePart   composite;
} BbRec;

static XtGeometryResult bbGeometryManager (Widget w, XtWidgetGeometry *request, XtWidgetGeometry *reply);
static XtGeometryResult bbPreferredSize (Widget widget, XtWidgetGeometry *constraint, XtWidgetGeometry *preferred);

BbClassRec bbClassRec = {
  {
/* core_class fields      */
    /* superclass         */    (WidgetClass) &compositeClassRec,
    /* class_name         */    "Bb",
    /* widget_size        */    sizeof(BbRec),
    /* class_initialize   */    NULL,
    /* class_part_init    */	NULL,
    /* class_inited       */	FALSE,
    /* initialize         */    NULL,
    /* initialize_hook    */	NULL,
    /* realize            */    XtInheritRealize,
    /* actions            */    NULL,
    /* num_actions	  */	0,
    /* resources          */    NULL,
    /* num_resources      */    0,
    /* xrm_class          */    NULLQUARK,
    /* compress_motion	  */	TRUE,
    /* compress_exposure  */	TRUE,
    /* compress_enterleave*/	TRUE,
    /* visible_interest   */    FALSE,
    /* destroy            */    NULL,
    /* resize             */    XtInheritResize,
    /* expose             */    NULL,
    /* set_values         */    NULL,
    /* set_values_hook    */	NULL,
    /* set_values_almost  */    XtInheritSetValuesAlmost,
    /* get_values_hook    */	NULL,
    /* accept_focus       */    NULL,
    /* version            */	XtVersion,
    /* callback_private   */    NULL,
    /* tm_table           */    NULL,
    /* query_geometry     */	bbPreferredSize, /*XtInheritQueryGeometry,*/
    /* display_accelerator*/	XtInheritDisplayAccelerator,
    /* extension          */	NULL
  },{
/* composite_class fields */
    /* geometry_manager   */    bbGeometryManager,
    /* change_managed     */    XtInheritChangeManaged,
    /* insert_child	  */	XtInheritInsertChild,
    /* delete_child	  */	XtInheritDeleteChild,
    /* extension          */	NULL
  },{
/* Bb class fields */
    /* empty		  */	0,
  }
};

WidgetClass bbWidgetClass = (WidgetClass)&bbClassRec;

/*ARGSUSED*/
static XtGeometryResult
bbPreferredSize(
    Widget widget GCC_UNUSED,
    XtWidgetGeometry *constraint GCC_UNUSED,
    XtWidgetGeometry *preferred GCC_UNUSED)
{
    return XtGeometryYes;
}

/*ARGSUSED*/
static XtGeometryResult
bbGeometryManager(
    Widget		w,
    XtWidgetGeometry	*request,
    XtWidgetGeometry	*reply GCC_UNUSED)	/* RETURN */

{
    /* Allow any and all changes to the geometry */
    if (request->request_mode & CWWidth)
	w->core.width = request->width;
    if (request->request_mode & CWHeight)
	w->core.height = request->height;
    if (request->request_mode & CWBorderWidth)
	w->core.border_width = request->border_width;
    if (request->request_mode & CWX)
	w->core.x = request->x;
    if (request->request_mode & CWY)
	w->core.y = request->y;

    return XtGeometryYes;
}

#endif /* KEV_WIDGETS */

static void set_pointer(Window win, Cursor cursor)
{
    XColor colordefs[2];		/* 0 is foreground, 1 is background */

    XDefineCursor(dpy, win, cursor);

    colordefs[0].pixel = cur_win->pointer_fg;
    colordefs[1].pixel = cur_win->pointer_bg;
    XQueryColors (dpy, DefaultColormap (dpy, DefaultScreen (dpy)),
		  colordefs, 2);
    XRecolorCursor (dpy, cursor, colordefs, colordefs+1);
}

#if !OPT_KEV_DRAGGING
static int dont_update_sb = FALSE;

static void
set_scroll_window(long n)
{
    register WINDOW *wp;
    for_each_visible_window(wp) {
	if (n-- == 0)
	    break;
    }
    if (n < 0)
	set_curwp(wp);
}
#endif /* !OPT_KEV_DRAGGING */

#if OL_WIDGETS
static void
JumpProc(
    Widget scrollbar,
    XtPointer closure,
    XtPointer call_data)
{
    long value = (long)closure;
    OlScrollbarVerify *cbs = (OlScrollbarVerify *)call_data;

    if (value >= cur_win->nscrollbars)
	return;
    set_scroll_window(value);
    mvupwind(TRUE,
	     line_no(curwp->w_bufp, curwp->w_line.l) - cbs->new_location);
    dont_update_sb = TRUE;
    (void)update(TRUE);
    dont_update_sb = FALSE;
}

static void
grip_moved(
    Widget slider,
    XtPointer closure,
    XtPointer call_data)
{
    register WINDOW *wp, *saved_curwp;
    int nlines;
    long i = (long) closure;
    OlScrollbarVerify *cbs = (OlScrollbarVerify *)call_data;

    for_each_visible_window(wp) {
	if (i-- == 0)
	    break;
    }
    if (!wp)
	return;
    saved_curwp = curwp;
    nlines = - cbs->new_location;
    if (nlines < 1)
	nlines = 1;
    curwp = wp;
    resize(TRUE, nlines);
    set_curwp(saved_curwp);
    (void) update(TRUE);
}

static void
update_scrollbar_sizes(void)
{
    register WINDOW *wp;
    int i, newsbcnt;
    Dimension new_height;

    i=0;
    for_each_visible_window(wp)
	i++;
    newsbcnt=i;

    /* Create any needed new scrollbars and sliders */
    for (i = cur_win->nscrollbars+1; i <= newsbcnt; i++)
	if (cur_win->scrollbars[i] == NULL) {
	    cur_win->scrollbars[i] = XtVaCreateWidget(
		    "scrollbar",
		    scrollbarWidgetClass,
		    cur_win->pane,
		    XtNtranslations, cur_win->my_scrollbars_trans,
		    NULL);
	    XtAddCallback(cur_win->scrollbars[i],
		    XtNsliderMoved, JumpProc, (XtPointer) i);
	    cur_win->sliders[i] = XtVaCreateWidget(
		    "slider",
		    sliderWidgetClass,
		    cur_win->pane,
		    NULL);
	    XtAddCallback(cur_win->sliders[i],
		    XtNsliderMoved, grip_moved, (XtPointer) i);
	}

    /* Unmanage current set of scrollbars */
    if (cur_win->nscrollbars > 0)
	XtUnmanageChildren(cur_win->scrollbars,
			   (Cardinal) (cur_win->nscrollbars));
    if (cur_win->nscrollbars > 1)
	XtUnmanageChildren(cur_win->sliders,
			   (Cardinal) (cur_win->nscrollbars - 1));

    /* Set sizes and positions on scrollbars and sliders */
    cur_win->nscrollbars = newsbcnt;
    i=0;
    for_each_visible_window(wp) {
	new_height = wp->w_ntrows * cur_win->char_height;
	XtVaSetValues(cur_win->scrollbars[i],
	    XtNy,	wp->w_toprow * cur_win->char_height,
	    XtNheight,	new_height,
	    XtNsliderMin,	1,
	    XtNsliderMax,	200,
	    XtNproportionLength, 2,
	    XtNsliderValue,	3,
	    NULL);
	if (wp->w_wndp) {
	    XtVaSetValues(cur_win->sliders[i],
		XtNy,		wp->w_toprow * cur_win->char_height,
		XtNheight,	(wp->w_ntrows + wp->w_wndp->w_ntrows + 1)
					    * cur_win->char_height,
		XtNsliderMax,	0,
		XtNsliderMin,	-(wp->w_ntrows + wp->w_wndp->w_ntrows),
		XtNsliderValue, -wp->w_ntrows,
		XtNstopPosition, OL_GRANULARITY,
		XtNendBoxes,	FALSE,
		XtNdragCBType,	OL_RELEASE,
		XtNbackground,	cur_win->fg,
		NULL);
	}
	wp->w_flag &= ~WFSBAR;
	gui_update_scrollbar(wp);
	i++;
    }

    /* Manage the sliders */
    if (cur_win->nscrollbars > 1)
	XtManageChildren(cur_win->sliders,
			    (Cardinal) (cur_win->nscrollbars - 1));

    /* Manage the current set of scrollbars */
    XtManageChildren(cur_win->scrollbars,
			   (Cardinal) (cur_win->nscrollbars));


    for (i=0; i<cur_win->nscrollbars; i++)
	XRaiseWindow(dpy, XtWindow(cur_win->scrollbars[i]));
}

#else
#if MOTIF_WIDGETS

static void
JumpProc(
    Widget scrollbar,
    XtPointer closure,
    XtPointer call_data)
{
    long value = (long)closure;
    int lcur;

    lookfor_sb_resize = FALSE;
    if (value >= cur_win->nscrollbars)
	return;
    set_scroll_window(value);
    lcur = line_no(curwp->w_bufp, curwp->w_line.l);
    mvupwind(TRUE, lcur - ((XmScrollBarCallbackStruct *)call_data)->value);
    dont_update_sb = TRUE;
    (void)update(TRUE);
    dont_update_sb = FALSE;
}

static void
update_scrollbar_sizes(void)
{
    register WINDOW *wp;
    int newsbcnt;
    Dimension new_height;
    Widget *children;
    int num_children;
    long i=0;

    for_each_visible_window(wp)
	i++;
    newsbcnt=i;

    /* Remove event handlers on sashes */
    XtVaGetValues(cur_win->pane,
	XmNchildren, &children,
	XmNnumChildren, &num_children,
	NULL);
    while (num_children-- > 0)
	if (XmIsSash(children[num_children]))
	    XtRemoveEventHandler(
		children[num_children],
		ButtonReleaseMask,
		FALSE,
		pane_button,
		NULL);

    /* Create any needed new scrollbars */
    for (i = cur_win->nscrollbars+1; i <= newsbcnt; i++)
	if (cur_win->scrollbars[i] == NULL) {
	    cur_win->scrollbars[i] = XtVaCreateWidget(
		    "scrollbar",
		    xmScrollBarWidgetClass,
		    cur_win->pane,
		    XmNsliderSize,	1,
		    XmNvalue,		1,
		    XmNminimum,		1,
		    XmNmaximum,		2,	/* so we don't get warning */
		    XmNorientation,	XmVERTICAL,
		    XmNtranslations,	cur_win->my_scrollbars_trans,
		    NULL);
	    XtAddCallback(cur_win->scrollbars[i],
		    XmNvalueChangedCallback, JumpProc, (XtPointer) i);
	    XtAddCallback(cur_win->scrollbars[i],
		    XmNdragCallback, JumpProc, (XtPointer) i);
	    XtAddEventHandler(
		    cur_win->scrollbars[i],
		    StructureNotifyMask,
		    FALSE,
		    grip_moved,
		    (XtPointer) i);
	}

    /* Unmanage current set of scrollbars */
    if (cur_win->nscrollbars >= 0)
	XtUnmanageChildren(cur_win->scrollbars,
			   (Cardinal) (cur_win->nscrollbars + 1));

    /* Set sizes on scrollbars */
    cur_win->nscrollbars = newsbcnt;
    i=0;
    for_each_visible_window(wp) {
	new_height = wp->w_ntrows * cur_win->char_height;
	XtVaSetValues(cur_win->scrollbars[i],
	    XmNallowResize,		TRUE,
	    XmNheight,			new_height,
	    XmNpaneMinimum,		1,
	    XmNpaneMaximum,		DisplayHeight(dpy, DefaultScreen(dpy)),
	    XmNshowArrows,		wp->w_ntrows > 3 ? TRUE : FALSE,
	    NULL);
	wp->w_flag &= ~WFSBAR;
	gui_update_scrollbar(wp);
	i++;
    }
    XtVaSetValues(cur_win->scrollbars[i],
	    XmNheight,			cur_win->char_height-1,
	    XmNallowResize,		FALSE,
	    XmNpaneMinimum,		cur_win->char_height-1,
	    XmNpaneMaximum,		cur_win->char_height-1,
	    XmNshowArrows,		FALSE,
	    NULL);

    /* Manage the current set of scrollbars */
    XtManageChildren(cur_win->scrollbars,
			   (Cardinal) (cur_win->nscrollbars + 1));

    /* Add event handlers for sashes */
    XtVaGetValues(cur_win->pane,
	XmNchildren, &children,
	XmNnumChildren, &num_children,
	NULL);
    while (num_children-- > 0)
	if (XmIsSash(children[num_children]))
	    XtAddEventHandler(
		children[num_children],
		ButtonReleaseMask,
		FALSE,
		pane_button,
		NULL);
}

#else
#if OPT_XAW_SCROLLBARS

#if !OPT_KEV_DRAGGING
static void
JumpProc(
    Widget scrollbar GCC_UNUSED,
    XtPointer closure,
    XtPointer call_data)
{
    L_NUM lcur, lmax;
    long value = (long)closure;
    float *percent = (float *)call_data;

    if (value < cur_win->nscrollbars) {
	set_scroll_window(value);
	lcur = line_no(curwp->w_bufp, curwp->w_line.l);
	lmax = vl_line_count(curwp->w_bufp);
	mvupwind(TRUE, (int)(lcur - lmax * (*percent)));
	(void)update(TRUE);
    }
}

static void
ScrollProc(
    Widget scrollbar GCC_UNUSED,
    XtPointer closure,
    XtPointer call_data)
{
    long value = (long)closure;
    long position = (long)call_data;

    if (value < cur_win->nscrollbars) {
	set_scroll_window(value);
	forwline(TRUE, (position / cur_win->char_height));
	(void)update(TRUE);
    }
}
#endif

static void
update_scrollbar_sizes(void)
{
    register WINDOW *wp;
    int i, newsbcnt;
    Dimension new_height;

    i=0;
    for_each_visible_window(wp)
	i++;
    newsbcnt=i;

    /* Create any needed new scrollbars and grips */
    for (i = cur_win->nscrollbars+1; i <= newsbcnt; i++) {
	if (cur_win->scrollbars[i] == NULL) {
	    cur_win->scrollbars[i] = XtVaCreateWidget(
		    "scrollbar",
		    scrollbarWidgetClass,
		    cur_win->pane,
		    XtNforeground,	cur_win->scrollbar_fg,
		    XtNbackground,	cur_win->scrollbar_bg,
		    XtNthumb,		cur_win->thumb_bm,
		    XtNtranslations,	cur_win->my_scrollbars_trans,
		    NULL);
#if !OPT_KEV_DRAGGING
	    XtAddCallback(cur_win->scrollbars[i],
		    XtNjumpProc, JumpProc, (XtPointer) i);
	    XtAddCallback(cur_win->scrollbars[i],
		    XtNscrollProc, ScrollProc, (XtPointer) i);
#endif
	}
    }
    for (i = newsbcnt-2; i >= 0 && cur_win->grips[i] == NULL; i--) {
	cur_win->grips[i] = XtVaCreateWidget(
		    "resizeGrip",
		    gripWidgetClass,
		    cur_win->pane,
		    XtNbackground,	cur_win->modeline_bg,
		    XtNborderWidth,	0,
		    XtNheight,		1,
		    XtNwidth,		1,
		    XtNtranslations,	cur_win->my_resizeGrip_trans,
		    NULL);
    }

    /* Unmanage current set of scrollbars */
    if (cur_win->nscrollbars > 0)
	XtUnmanageChildren(cur_win->scrollbars,
			   (Cardinal) (cur_win->nscrollbars));

    /* Set sizes and positions on scrollbars and grips */
    cur_win->nscrollbars = newsbcnt;
    i=0;
    for_each_visible_window(wp) {
	L_NUM total = vl_line_count(curwp->w_bufp);
	L_NUM thumb = line_no(curwp->w_bufp, curwp->w_line.l);

	new_height = wp->w_ntrows * cur_win->char_height;
	cur_win->scrollinfo[i].totlen = new_height;
	XtVaSetValues(cur_win->scrollbars[i],
	    XtNy,		wp->w_toprow * cur_win->char_height,
	    XtNheight,		new_height,
	    XtNorientation,	XtorientVertical,
	    XtNvertDistance,	wp->w_toprow * cur_win->char_height,
	    XtNhorizDistance,	1,
	    NULL);
	XawScrollbarSetThumb(cur_win->scrollbars[i],
		((float) (thumb - 1)) / max(total, 1),
		((float) wp->w_ntrows) / max(total, wp->w_ntrows));
	wp->w_flag &= ~WFSBAR;
	gui_update_scrollbar(wp);
	if (wp->w_wndp) {
	    XtVaSetValues(cur_win->grips[i],
		XtNx,		1,
		XtNy,		(wp->w_wndp->w_toprow-1) * cur_win->char_height,
		XtNheight,	cur_win->char_height,
		XtNwidth,	cur_win->pane_width,
		XtNvertDistance,(wp->w_wndp->w_toprow-1) * cur_win->char_height,
		XtNhorizDistance,1,
		NULL);
	}
	i++;
    }

    /* Manage the current set of scrollbars */
    XtManageChildren(cur_win->scrollbars,
			   (Cardinal) (cur_win->nscrollbars));

    XtManageChildren(cur_win->grips,
			   (Cardinal) (cur_win->nscrollbars - 1));

    for (i=0; i<cur_win->nscrollbars; i++)
	XRaiseWindow(dpy, XtWindow(cur_win->scrollbars[i]));

    for (i=1; i<cur_win->nscrollbars; i++)
	XRaiseWindow(dpy, XtWindow(cur_win->grips[i-1]));
}

#else
#if OPT_KEV_SCROLLBARS
static void
update_scrollbar_sizes(void)
{
    register WINDOW *wp;
    int i, newsbcnt;
    Dimension new_height;
    Cardinal nchildren;

    i=0;
    for_each_visible_window(wp)
	i++;
    newsbcnt=i;

    /* Create any needed new scrollbars and grips */
    for (i = newsbcnt-1; i >= 0 && cur_win->scrollbars[i] == NULL; i--) {
	if (cur_win->slider_is_3D) {
	    cur_win->scrollbars[i] = XtVaCreateWidget(
			"scrollbar",
			coreWidgetClass,
			cur_win->pane,
			XtNbackgroundPixmap,	cur_win->trough_pixmap,
			XtNborderWidth,		0,
			XtNheight,		1,
			XtNwidth,		1,
			XtNtranslations,	cur_win->my_scrollbars_trans,
			NULL);
	}
	else {
	    cur_win->scrollbars[i] = XtVaCreateWidget(
			"scrollbar",
			coreWidgetClass,
			cur_win->pane,
			XtNbackground,		cur_win->scrollbar_bg,
			XtNborderWidth,		0,
			XtNheight,		1,
			XtNwidth,		1,
			XtNtranslations,	cur_win->my_scrollbars_trans,
			NULL);
	}

	XtAddEventHandler(
		cur_win->scrollbars[i],
		ExposureMask,
		FALSE,
		x_expose_scrollbar,
		(XtPointer)0);
	cur_win->scrollinfo[i].exposed = False;
    }
    for (i = newsbcnt-2; i >= 0 && cur_win->grips[i] == NULL; i--)
	cur_win->grips[i] = XtVaCreateWidget(
		    "resizeGrip",
		    coreWidgetClass,
		    cur_win->pane,
		    XtNbackground,	cur_win->modeline_bg,
		    XtNborderWidth,	0,
		    XtNheight,		1,
		    XtNwidth,		1,
		    XtNtranslations,	cur_win->my_resizeGrip_trans,
		    NULL);

    /* Set sizes and positions on scrollbars and grips */
    i=0;
    for_each_visible_window(wp) {
	new_height = wp->w_ntrows * cur_win->char_height;
	XtVaSetValues(cur_win->scrollbars[i],
	    XtNx,		cur_win->slider_is_3D ? 0 : 1,
	    XtNy,		wp->w_toprow * cur_win->char_height,
	    XtNheight,		new_height,
	    XtNwidth,		cur_win->pane_width
				   + (cur_win->slider_is_3D ? 2 : 0),
	    NULL);
	cur_win->scrollinfo[i].totlen = new_height;
	if (wp->w_wndp) {
	    XtVaSetValues(cur_win->grips[i],
		XtNx,		1,
		XtNy,		(wp->w_wndp->w_toprow-1) * cur_win->char_height,
		XtNheight,	cur_win->char_height,
		XtNwidth,	cur_win->pane_width,
		NULL);
	}
	i++;
    }

    if (cur_win->nscrollbars > newsbcnt) {
	nchildren = cur_win->nscrollbars - newsbcnt;
	XtUnmanageChildren(cur_win->scrollbars+newsbcnt, nchildren);
	XtUnmanageChildren(cur_win->grips+newsbcnt-1,    nchildren);
	for (i = cur_win->nscrollbars; i > newsbcnt; )
	    cur_win->scrollinfo[--i].exposed = False;
    }
    else if (cur_win->nscrollbars < newsbcnt) {
	nchildren = newsbcnt - cur_win->nscrollbars;
	XtManageChildren(cur_win->scrollbars+cur_win->nscrollbars, nchildren);
	if (cur_win->nscrollbars > 0)
	    XtManageChildren(cur_win->grips+cur_win->nscrollbars-1, nchildren);
	else if (cur_win->nscrollbars == 0 && nchildren > 1)
	    XtManageChildren(cur_win->grips, nchildren-1);
    }
    cur_win->nscrollbars = newsbcnt;

    i = 0;
    for_each_visible_window(wp) {
	wp->w_flag &= ~WFSBAR;
	gui_update_scrollbar(wp);
	i++;
    }

    /*
     * Set the cursors... It would be nice if we could do this in the
     * initialization above, but the widget needs to be realized for this
     * to work
     */
    for (i=0; i<cur_win->nscrollbars; i++) {
	if (XtIsRealized(cur_win->scrollbars[i]))
	    set_pointer(
		    XtWindow(cur_win->scrollbars[i]),
		    curs_sb_v_double_arrow);
	if (i < cur_win->nscrollbars-1 && XtIsRealized(cur_win->grips[i]))
	    set_pointer(
		    XtWindow(cur_win->grips[i]),
		    curs_double_arrow);
    }
}


/*
 * The X11R5 Athena scrollbar code was used as a reference for writing
 * draw_thumb and parts of update_thumb.
 */

#define FILL_TOP 0x01
#define FILL_BOT 0x02

#define SP_HT 16				/* slider pixmap height */

static void
draw_thumb(
    Widget w,
    int top,
    int bot,
    int dofill)
{
    UINT length = bot - top;
    if (bot < 0 || top < 0 || bot <= top)
	return;

    if (!dofill)
	XClearArea(XtDisplay(w), XtWindow(w), cur_win->slider_is_3D ? 2 : 1,
		   top, cur_win->pane_width-2, length, FALSE);
    else if (!cur_win->slider_is_3D)
	XFillRectangle(XtDisplay(w), XtWindow(w), cur_win->scrollbargc,
		       1, top, cur_win->pane_width-2, length);
    else {
	if (dofill & FILL_TOP) {
	    int tbot;
	    tbot = bot - ((dofill & FILL_BOT) ? 2 : 0);
	    if (tbot <= top)
		tbot = top + 1;
	    if (top + (SP_HT-2) < tbot)
		tbot = top + (SP_HT-2);
	    XCopyArea(dpy, cur_win->slider_pixmap, XtWindow(w),
		      cur_win->scrollbargc,
		      0, 0, cur_win->pane_width-2, (unsigned int) (tbot - top),
		      2, top);

	    top = tbot;
	}
	if (dofill & FILL_BOT) {
	    int btop = max(top, bot-(SP_HT-2));
	    XCopyArea(dpy, cur_win->slider_pixmap, XtWindow(w),
		      cur_win->scrollbargc,
		      0, SP_HT - (bot-btop),
		      cur_win->pane_width-2, (unsigned int) (bot - btop),
		      2, btop);
	    bot = btop;

	}
	if (top < bot) {
	    XFillRectangle(XtDisplay(w), XtWindow(w), cur_win->scrollbargc,
			   2, top,
			   cur_win->pane_width-2, (unsigned int) (bot-top));
	}
    }
}


#define MIN_THUMB_SIZE 8

static void
update_thumb(
    int barnum,
    int newtop,
    int newlen)
{
    int oldtop, oldbot, newbot, totlen;
    int f = cur_win->slider_is_3D ? 2 : 0;
    Widget w = cur_win->scrollbars[barnum];

    oldtop = cur_win->scrollinfo[barnum].top;
    oldbot = cur_win->scrollinfo[barnum].bot;
    totlen = cur_win->scrollinfo[barnum].totlen;
    newtop = min(newtop, totlen-3);
    newbot = newtop + max(newlen, MIN_THUMB_SIZE);
    newbot = min(newbot, totlen);
    cur_win->scrollinfo[barnum].top = newtop;
    cur_win->scrollinfo[barnum].bot = newbot;

    if (!cur_win->scrollinfo[barnum].exposed)
	return;

    if (XtIsRealized(w)) {
	if (newtop < oldtop) {
	    int tbot = min(newbot, oldtop+f);
	    draw_thumb(w, newtop, tbot,
		       FILL_TOP | ((tbot == newbot) ? FILL_BOT : 0));
	}
	if (newtop > oldtop) {
	    draw_thumb(w, oldtop, min(newtop, oldbot), 0);
	    if (cur_win->slider_is_3D && newtop < oldbot)
		draw_thumb(w, newtop, min(newtop+f, oldbot), FILL_TOP);
	}
	if (newbot < oldbot) {
	    draw_thumb(w, max(newbot, oldtop), oldbot, 0);
	    if (cur_win->slider_is_3D && oldtop < newbot)
		draw_thumb(w, max(newbot-f, oldtop), newbot, FILL_BOT);
	}
	if (newbot > oldbot) {
	    int btop = max(newtop, oldbot-f);
	    draw_thumb(w, btop, newbot,
		       FILL_BOT | ((btop == newtop) ? FILL_TOP : 0));
	}
    }
}

/*ARGSUSED*/
static void
x_expose_scrollbar(
    Widget	w,
    XtPointer	unused GCC_UNUSED,
    XEvent     *ev,
    Boolean    *continue_to_dispatch GCC_UNUSED)
{
    int i;

    if (ev->type != Expose)
	return;

    for (i=0; i < cur_win->nscrollbars; i++) {
	if (cur_win->scrollbars[i] == w) {
	    int top, bot;
	    top = cur_win->scrollinfo[i].top;
	    bot = cur_win->scrollinfo[i].bot;
	    cur_win->scrollinfo[i].top = -1;
	    cur_win->scrollinfo[i].bot = -1;
	    cur_win->scrollinfo[i].exposed = True;
	    update_thumb(i, top, bot-top);
	}
    }
}
#endif /* OPT_KEV_SCROLLBARS  */
#endif /* OPT_XAW_SCROLLBARS  */
#endif /* MOTIF_WIDGETS */
#endif /* OL_WIDGETS */

#if OPT_KEV_DRAGGING
static void
do_scroll(
    Widget w,
    XEvent *event,
    String *params,
    Cardinal *num_params)
{
    static enum { none, forward, backward, drag } scrollmode = none;
    int pos;
    register WINDOW *wp;
    register int i;
    XEvent nev;
    int count;

    /*
     * Return immediately if behind in processing motion events.  Note:
     * If this is taken out, scrolling is actually smoother, but sometimes
     * takes a while to catch up.  I should think that performance would
     * be horrible on a slow server.
     */

    if (scrollmode == drag
     && event->type == MotionNotify
     && x_has_events()
     && XtAppPeekEvent(cur_win->app_context, &nev)
     && (nev.type == MotionNotify || nev.type == ButtonRelease))
	return;

    if (*num_params != 1)
	return;

    /* Determine vertical position */
    switch (event->type) {
	case MotionNotify :
	    pos = event->xmotion.y;
	    break;
	case ButtonPress :
	case ButtonRelease :
	    pos = event->xbutton.y;
	    break;
	default :
	    return;
    }

    /* Determine scrollbar number and corresponding vile window */
    i = 0;
    for_each_visible_window (wp) {
	if (cur_win->scrollbars[i] == w)
	    break;
	i++;
    }

    if (!wp)
	return;

    if (pos < 0)
	pos = 0;
    if (pos > cur_win->scrollinfo[i].totlen)
	pos = cur_win->scrollinfo[i].totlen;

    switch (**params) {
	case 'E' :	/* End */
	    if (cur_win->scroll_repeat_id != (XtIntervalId) 0) {
		XtRemoveTimeOut(cur_win->scroll_repeat_id);
		cur_win->scroll_repeat_id = (XtIntervalId) 0;
	    }
	    set_pointer(XtWindow(w), curs_sb_v_double_arrow);
	    scrollmode = none;
	    break;
	case 'F' :	/* Forward */
	    if (scrollmode != none)
		break;
	    count = (pos / cur_win->char_height) + 1;
	    scrollmode = forward;
	    set_pointer(XtWindow(w), curs_sb_up_arrow);
	    goto do_scroll_common;
	case 'B' :	/* Backward */
	    if (scrollmode != none)
		break;
	    count = -((pos / cur_win->char_height) + 1);
	    scrollmode = backward;
	    set_pointer(XtWindow(w), curs_sb_down_arrow);
do_scroll_common:
	    set_curwp(wp);
	    mvdnwind(TRUE, count);
	    cur_win->scroll_repeat_id = XtAppAddTimeOut(
		    cur_win->app_context,
		    cur_win->scroll_repeat_timeout,
		    repeat_scroll,
		    (XtPointer) count);
	    (void)update(TRUE);
	    break;
	case 'S' :	/* StartDrag */
	    if (scrollmode == none) {
		set_curwp(wp);
		scrollmode = drag;
		set_pointer(XtWindow(w), curs_sb_right_arrow);
	    }
	    /* FALLTHRU */
	case 'D' :	/* Drag */
	    if (scrollmode == drag) {
		int lcur = line_no(curwp->w_bufp, curwp->w_line.l);
		int ltarg = (vl_line_count(curwp->w_bufp) * pos
				/ cur_win->scrollinfo[i].totlen) + 1;
		mvupwind(TRUE, lcur-ltarg);
		(void)update(TRUE);
	    }
	    break;
    }
}

/*ARGSUSED*/
static void
repeat_scroll(
    XtPointer count,
    XtIntervalId  *id GCC_UNUSED)
{
    cur_win->scroll_repeat_id = XtAppAddTimeOut(
	    cur_win->app_context,
	    cur_win->scroll_repeat_interval,
	    repeat_scroll,
	    (XtPointer) count);
    mvdnwind(TRUE, (int)count);
    (void)update(TRUE);
    XSync(dpy, False);
}
#endif /* OPT_KEV_DRAGGING */

#if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
static void
resize_bar(
    Widget w,
    XEvent *event,
    String *params,
    Cardinal *num_params)
{
    static int motion_permitted = False;
    static int root_y;
    int pos = 0;			/* stifle -Wall */
    register WINDOW *wp;
    register int i;
    XEvent nev;

    /* Return immediately if behind in processing motion events */
    if (motion_permitted
     && event->type == MotionNotify
     && x_has_events()
     && XtAppPeekEvent(cur_win->app_context, &nev)
     && (nev.type == MotionNotify || nev.type == ButtonRelease))
	return;

    if (*num_params != 1)
	return;

    switch (**params) {
	case 'S' :	/* Start */
	    motion_permitted = True;
	    root_y = event->xbutton.y_root - event->xbutton.y;
	    return;
	case 'D' :	/* Drag */
	    if (!motion_permitted)
		return;
	    /*
	     * We use kind of convoluted mechanism to determine the vertical
	     * position with respect to the widget which we are moving. The
	     * reason is that the x,y position from the event structure is
	     * unreliable since the widget may have moved in between the
	     * time when the event was first generated (at the display
	     * server) and the time at which the event is received (in
	     * this code here).  So we keep track of the vertical position
	     * of the widget with respect to the root window and use the
	     * corresponding field in the event structure to determine
	     * the vertical position of the pointer with respect to the
	     * widget of interest.  In the past, XQueryPointer() was
	     * used to determine the position of the pointer, but this is
	     * not really what we want since the position returned by
	     * XQueryPointer is the position of the pointer at a time
	     * after the event was both generated and received (and thus
	     * inaccurate).  This is why the resize grip would sometimes
	     * "follow" the mouse pointer after the button was released.
	     */
	    pos = event->xmotion.y_root - root_y;
	    break;
	case 'E' :	/* End */
	    if (!motion_permitted)
		return;
	    pos = event->xbutton.y_root - root_y;
	    motion_permitted = False;
	    break;
    }

    /* Determine grip number and corresponding vile window (above grip) */
    i = 0;
    for_each_visible_window (wp) {
	if (cur_win->grips[i] == w)
	    break;
	i++;
    }

    if (!wp)
	return;

    if (pos < 0)
	pos -= cur_win->char_height;
    pos = pos / cur_win->char_height;

    if (pos) {
	int nlines;
	if (pos >= wp->w_wndp->w_ntrows)
	    pos = wp->w_wndp->w_ntrows - 1;

	nlines = wp->w_ntrows + pos;
	if (nlines < 1)
	    nlines = 1;
	root_y += (nlines - wp->w_ntrows) * cur_win->char_height;
	set_curwp(wp);
	resize(TRUE, nlines);
	(void)update(TRUE);
    }
}
#endif /* OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS */

void
gui_update_scrollbar(
    WINDOW *uwp)
{
    WINDOW *wp;
    int i;
    int lnum, lcnt;

#if !OPT_KEV_SCROLLBARS && !OPT_XAW_SCROLLBARS
    if (dont_update_sb)
	return;
#endif /* !OPT_KEV_SCROLLBARS */

    i = 0;
    for_each_visible_window(wp) {
	if (wp == uwp)
	    break;
	i++;
    }
    if (i >= cur_win->nscrollbars || (wp->w_flag & WFSBAR)) {
	/*
	 * update_scrollbar_sizes will recursively invoke gui_update_scrollbar,
	 * but with WFSBAR disabled.
	 */
	update_scrollbar_sizes();
	return;
    }

    lnum = line_no(wp->w_bufp, wp->w_line.l);
    lnum = (lnum > 0) ? lnum : 1;
    lcnt = vl_line_count(wp->w_bufp);
#if MOTIF_WIDGETS
    lcnt += 1;
    XtVaSetValues(cur_win->scrollbars[i],
	    XmNmaximum,		lcnt + wp->w_ntrows,
	    XmNsliderSize,	wp->w_ntrows,
	    XmNvalue,		lnum,
	    XmNpageIncrement,	wp->w_ntrows > 1 ? wp->w_ntrows-1 : 1,
	    NULL);
#else
#if OL_WIDGETS
    lcnt += 1;
    XtVaSetValues(cur_win->scrollbars[i],
	    XtNsliderMin,	1,
	    XtNsliderMax,	lcnt + wp->w_ntrows,
	    XtNproportionLength, wp->w_ntrows,
	    XtNsliderValue,	lnum,
	    NULL);
#else
#if OPT_XAW_SCROLLBARS
    XawScrollbarSetThumb(cur_win->scrollbars[i],
	    ((float) (lnum - 1)) / max(lcnt, wp->w_ntrows),
	    ((float) wp->w_ntrows) / max(lcnt, wp->w_ntrows));
#else
#if OPT_KEV_SCROLLBARS
    {
	int top, len;
	lcnt  = max(lcnt, 1);
	len   = (min(lcnt, wp->w_ntrows) * cur_win->scrollinfo[i].totlen
				/ lcnt) + 1;
	top   = ((lnum-1) * cur_win->scrollinfo[i].totlen)
		    / lcnt;
	update_thumb(i, top, len);
    }
#endif /* OPT_KEV_SCROLLBARS */
#endif /* OPT_XAW_SCROLLBARS */
#endif /* OL_WIDGETS */
#endif /* MOTIF_WIDGETS */
}

#define OLD_RESOURCES 1		/* New stuff not ready for prime time */

#define XtNnormalShape		"normalShape"
#define XtCNormalShape		"NormalShape"
#define XtNwatchShape		"watchShape"
#define XtCWatchShape		"WatchShape"

#define XtNforkOnStartup	"forkOnStartup"
#define XtCForkOnStartup	"ForkOnStartup"

#define XtNscrollbarWidth	"scrollbarWidth"
#define XtCScrollbarWidth	"ScrollbarWidth"
#define XtNfocusFollowsMouse	"focusFollowsMouse"
#define XtCFocusFollowsMouse	"FocusFollowsMouse"
#define XtNscrollbarOnLeft	"scrollbarOnLeft"
#define XtCScrollbarOnLeft	"ScrollbarOnLeft"
#define XtNmultiClickTime	"multiClickTime"
#define XtCMultiClickTime	"MultiClickTime"
#define XtNcharClass		"charClass"
#define XtNwheelScrollAmount    "wheelScrollAmount"
#define XtCWheelScrollAmount    "WheelScrollAmount"
#define XtCCharClass		"CharClass"
#if OPT_KEV_DRAGGING
#define	XtNscrollRepeatTimeout	"scrollRepeatTimeout"
#define	XtCScrollRepeatTimeout	"ScrollRepeatTimeout"
#endif
#define	XtNscrollRepeatInterval	"scrollRepeatInterval"
#define	XtCScrollRepeatInterval	"ScrollRepeatInterval"
#define XtNpersistentSelections	"persistentSelections"
#define XtCPersistentSelections	"PersistentSelections"
#define XtNselectionSetsDOT	"selectionSetsDOT"
#define XtCSelectionSetsDOT	"SelectionSetsDOT"
#define XtNblinkInterval	"blinkInterval"
#define XtCBlinkInterval	"BlinkInterval"
#define XtNsliderIsSolid	"sliderIsSolid"
#define XtCSliderIsSolid	"SliderIsSolid"
#define	XtNfocusForeground	"focusForeground"
#define	XtNfocusBackground	"focusBackground"

#define XtNfcolor0		"fcolor0"
#define XtCFcolor0		"Fcolor0"
#define XtNbcolor0		"bcolor0"
#define XtCBcolor0		"Bcolor0"

#define XtNfcolor1		"fcolor1"
#define XtCFcolor1		"Fcolor1"
#define XtNbcolor1		"bcolor1"
#define XtCBcolor1		"Bcolor1"

#define XtNfcolor2		"fcolor2"
#define XtCFcolor2		"Fcolor2"
#define XtNbcolor2		"bcolor2"
#define XtCBcolor2		"Bcolor2"

#define XtNfcolor3		"fcolor3"
#define XtCFcolor3		"Fcolor3"
#define XtNbcolor3		"bcolor3"
#define XtCBcolor3		"Bcolor3"

#define XtNfcolor4		"fcolor4"
#define XtCFcolor4		"Fcolor4"
#define XtNbcolor4		"bcolor4"
#define XtCBcolor4		"Bcolor4"

#define XtNfcolor5		"fcolor5"
#define XtCFcolor5		"Fcolor5"
#define XtNbcolor5		"bcolor5"
#define XtCBcolor5		"Bcolor5"

#define XtNfcolor6		"fcolor6"
#define XtCFcolor6		"Fcolor6"
#define XtNbcolor6		"bcolor6"
#define XtCBcolor6		"Bcolor6"

#define XtNfcolor7		"fcolor7"
#define XtCFcolor7		"Fcolor7"
#define XtNbcolor7		"bcolor7"
#define XtCBcolor7		"Bcolor7"

#define XtNfcolor8		"fcolor8"
#define XtCFcolor8		"Fcolor8"
#define XtNbcolor8		"bcolor8"
#define XtCBcolor8		"Bcolor8"

#define XtNfcolor9		"fcolor9"
#define XtCFcolor9		"Fcolor9"
#define XtNbcolor9		"bcolor9"
#define XtCBcolor9		"Bcolor9"

#define XtNfcolorA		"fcolor10"
#define XtCFcolorA		"Fcolor10"
#define XtNbcolorA		"bcolor10"
#define XtCBcolorA		"Bcolor10"

#define XtNfcolorB		"fcolor11"
#define XtCFcolorB		"Fcolor11"
#define XtNbcolorB		"bcolor11"
#define XtCBcolorB		"Bcolor11"

#define XtNfcolorC		"fcolor12"
#define XtCFcolorC		"Fcolor12"
#define XtNbcolorC		"bcolor12"
#define XtCBcolorC		"Bcolor12"

#define XtNfcolorD		"fcolor13"
#define XtCFcolorD		"Fcolor13"
#define XtNbcolorD		"bcolor13"
#define XtCBcolorD		"Bcolor13"

#define XtNfcolorE		"fcolor14"
#define XtCFcolorE		"Fcolor14"
#define XtNbcolorE		"bcolor14"
#define XtCBcolorE		"Bcolor14"

#define XtNfcolorF		"fcolor15"
#define XtCFcolorF		"Fcolor15"
#define XtNbcolorF		"bcolor15"
#define XtCBcolorF		"Bcolor15"

static XtResource resources[] = {
#if OPT_KEV_DRAGGING
    {
	XtNscrollRepeatTimeout,
	XtCScrollRepeatTimeout,
	XtRInt,
	sizeof(int),
	XtOffset(TextWindow, scroll_repeat_timeout),
	XtRImmediate,
	(XtPointer) 500			/* 1/2 second */
    },
#endif	/* OPT_KEV_DRAGGING */
    {
	XtNscrollRepeatInterval,
	XtCScrollRepeatInterval,
	XtRInt,
	sizeof(int),
	XtOffset(TextWindow, scroll_repeat_interval),
	XtRImmediate,
	(XtPointer) 60			/* 60 milliseconds */
    },
    {
	XtNgeometry,
	XtCGeometry,
	XtRString,
	sizeof(String *),
	XtOffset(TextWindow, geometry),
	XtRImmediate,
	"80x36"
    },
    {
	XtNfont,
	XtCFont,
	XtRString,
	sizeof(String *),
	XtOffset(TextWindow, starting_fontname),
	XtRImmediate,
	XtDefaultFont	/* used to be FONTNAME */
    },
    {
	XtNforeground,
	XtCForeground,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, fg),
	XtRString,
#if OLD_RESOURCES
	XtDefaultForeground
#else
	"#c71bc30bc71b"
#endif /* OLD_RESOURCES */
    },
    {
	XtNbackground,
	XtCBackground,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, bg),
	XtRString,
#if OLD_RESOURCES
	XtDefaultBackground
#else
	"#c71bc30bc71b"
#endif
    },
    {
	XtNforkOnStartup,
	XtCForkOnStartup,
	XtRBool,
	sizeof(Bool),
	XtOffset(TextWindow, fork_on_startup),
	XtRImmediate,
	(XtPointer) False
    },
    {
	XtNfocusFollowsMouse,
	XtCFocusFollowsMouse,
	XtRBool,
	sizeof(Bool),
	XtOffset(TextWindow, focus_follows_mouse),
	XtRImmediate,
	(XtPointer) False
    },
    {
	XtNmultiClickTime,
	XtCMultiClickTime,
	XtRInt,
	sizeof(Time),
	XtOffset(TextWindow, click_timeout),
	XtRImmediate,
	(XtPointer) 600
    },
    {
	XtNcharClass,
	XtCCharClass,
	XtRString,
	sizeof(String *),
	XtOffset(TextWindow, multi_click_char_class),
	XtRImmediate,
	NULL
    },
    {
	XtNscrollbarOnLeft,
	XtCScrollbarOnLeft,
	XtRBool,
	sizeof(Bool),
	XtOffset(TextWindow, scrollbar_on_left),
	XtRImmediate,
	(XtPointer) False
    },
    {
	XtNscrollbarWidth,
	XtCScrollbarWidth,
	XtRInt,
	sizeof(int),
	XtOffset(TextWindow, pane_width),
	XtRImmediate,
	(XtPointer) PANE_WIDTH_DEFAULT
    },
    {
	XtNwheelScrollAmount,
	XtCWheelScrollAmount,
	XtRInt,
	sizeof(int),
	XtOffset(TextWindow, wheel_scroll_amount),
	XtRImmediate,
	(XtPointer) 3
    },
#if OPT_MENUS
    {
	XtNmenuHeight,
	XtCMenuHeight,
	XtRInt,
	sizeof(int),
	XtOffset(TextWindow, menu_height),
	XtRImmediate,
	(XtPointer) MENU_HEIGHT_DEFAULT
    },
#endif
#if OPT_MENUS_COLORED
    {
	XtNmenuForeground,
	XtCMenuForeground,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, menubar_fg),
	XtRString,
	XtDefaultForeground
    },
    {
	XtNmenuBackground,
	XtCMenuBackground,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, menubar_bg),
	XtRString,
	XtDefaultBackground
    },
#endif
    {
	XtNpersistentSelections,
	XtCPersistentSelections,
	XtRBool,
	sizeof(Bool),
	XtOffset(TextWindow, persistent_selections),
	XtRImmediate,
	(XtPointer) True
    },
    {
	XtNselectionSetsDOT,
	XtCSelectionSetsDOT,
	XtRBool,
	sizeof(Bool),
	XtOffset(TextWindow, selection_sets_DOT),
	XtRImmediate,
	(XtPointer) False
    },
    {
	XtNblinkInterval,
	XtCBlinkInterval,
	XtRInt,
	sizeof(int),
	XtOffset(TextWindow, blink_interval),
	XtRImmediate,
	(XtPointer) -666		/* 2/3 second; only when highlighted */
    },
};

static XtResource color_resources[] = {
    {
	XtNfcolor0,
	XtCFcolor0,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_fg[0]),
	XtRPixel,
	(XtPointer) &cur_win_rec.fg
    },
    {
	XtNbcolor0,
	XtCBcolor0,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_bg[0]),
	XtRPixel,
	(XtPointer) &cur_win_rec.bg
    },
    {
	XtNfcolor1,
	XtCFcolor1,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_fg[1]),
	XtRString,
	(XtPointer) "rgb:ff/0/0"
    },
    {
	XtNbcolor1,
	XtCBcolor1,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_bg[1]),
	XtRPixel,
	(XtPointer) &cur_win_rec.bg
    },
    {
	XtNfcolor2,
	XtCFcolor2,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_fg[2]),
	XtRString,
	(XtPointer) "rgb:0/ff/0"
    },
    {
	XtNbcolor2,
	XtCBcolor2,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_bg[2]),
	XtRPixel,
	(XtPointer) &cur_win_rec.bg
    },
    {
	XtNfcolor3,
	XtCFcolor3,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_fg[3]),
	XtRString,
	(XtPointer) "rgb:a5/2a/2a"
    },
    {
	XtNbcolor3,
	XtCBcolor3,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_bg[3]),
	XtRPixel,
	(XtPointer) &cur_win_rec.bg
    },
    {
	XtNfcolor4,
	XtCFcolor4,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_fg[4]),
	XtRString,
	(XtPointer) "rgb:0/0/ff"
    },
    {
	XtNbcolor4,
	XtCBcolor4,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_bg[4]),
	XtRPixel,
	(XtPointer) &cur_win_rec.bg
    },
    {
	XtNfcolor5,
	XtCFcolor5,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_fg[5]),
	XtRString,
	(XtPointer) "rgb:ff/0/ff"
    },
    {
	XtNbcolor5,
	XtCBcolor5,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_bg[5]),
	XtRPixel,
	(XtPointer) &cur_win_rec.bg
    },
    {
	XtNfcolor6,
	XtCFcolor6,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_fg[6]),
	XtRString,
	(XtPointer) "rgb:0/ff/ff"
    },
    {
	XtNbcolor6,
	XtCBcolor6,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_bg[6]),
	XtRPixel,
	(XtPointer) &cur_win_rec.bg
    },
    {
	XtNfcolor7,
	XtCFcolor7,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_fg[7]),
	XtRString,
	(XtPointer) "rgb:e6/e6/e6"
    },
    {
	XtNbcolor7,
	XtCBcolor7,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_bg[7]),
	XtRPixel,
	(XtPointer) &cur_win_rec.bg
    },
    {
	XtNfcolor8,
	XtCFcolor8,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_fg[8]),
	XtRString,
	(XtPointer) "rgb:be/be/be"
    },
    {
	XtNbcolor8,
	XtCBcolor8,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_bg[8]),
	XtRPixel,
	(XtPointer) &cur_win_rec.bg
    },
    {
	XtNfcolor9,
	XtCFcolor9,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_fg[9]),
	XtRString,
	(XtPointer) "rgb:ff/7f/7f"
    },
    {
	XtNbcolor9,
	XtCBcolor9,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_bg[9]),
	XtRPixel,
	(XtPointer) &cur_win_rec.bg
    },
    {
	XtNfcolorA,
	XtCFcolorA,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_fg[10]),
	XtRString,
	(XtPointer) "rgb:7f/ff/7f"
    },
    {
	XtNbcolorA,
	XtCBcolorA,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_bg[10]),
	XtRPixel,
	(XtPointer) &cur_win_rec.bg
    },
    {
	XtNfcolorB,
	XtCFcolorB,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_fg[11]),
	XtRString,
	(XtPointer) "rgb:ff/ff/0"
    },
    {
	XtNbcolorB,
	XtCBcolorB,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_bg[11]),
	XtRPixel,
	(XtPointer) &cur_win_rec.bg
    },
    {
	XtNfcolorC,
	XtCFcolorC,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_fg[12]),
	XtRString,
	(XtPointer) "rgb:7f/7f/ff"
    },
    {
	XtNbcolorC,
	XtCBcolorC,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_bg[12]),
	XtRPixel,
	(XtPointer) &cur_win_rec.bg
    },
    {
	XtNfcolorD,
	XtCFcolorD,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_fg[13]),
	XtRString,
	(XtPointer) "rgb:ff/7f/ff"
    },
    {
	XtNbcolorD,
	XtCBcolorD,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_bg[13]),
	XtRPixel,
	(XtPointer) &cur_win_rec.bg
    },
    {
	XtNfcolorE,
	XtCFcolorE,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_fg[14]),
	XtRString,
	(XtPointer) "rgb:7f/ff/ff"
    },
    {
	XtNbcolorE,
	XtCBcolorE,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_bg[14]),
	XtRPixel,
	(XtPointer) &cur_win_rec.bg
    },
    {
	XtNfcolorF,
	XtCFcolorF,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_fg[15]),
	XtRString,
	(XtPointer) "rgb:ff/ff/ff"
    },
    {
	XtNbcolorF,
	XtCBcolorF,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, colors_bg[15]),
	XtRPixel,
	(XtPointer) &cur_win_rec.bg
    },
};

#if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
static XtResource scrollbar_resources[] = {
    {
	XtNforeground,
	XtCForeground,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, scrollbar_fg),
#if OLD_RESOURCES
	XtRPixel,
	(XtPointer) &cur_win_rec.fg
#else
	XtRString,
	"#b6dab2cab6da"
#endif
    },
    {
	XtNbackground,
	XtCBackground,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, scrollbar_bg),
#if OLD_RESOURCES
	XtRPixel,
	(XtPointer) &cur_win_rec.bg
#else
	XtRString,
	"#9e7996589e79"
#endif /* OLD_RESOURCES */
    },
    {
	XtNsliderIsSolid,
	XtCSliderIsSolid,
	XtRBool,
	sizeof(Bool),
	XtOffset(TextWindow, slider_is_solid),
#if OLD_RESOURCES
	XtRImmediate,
	(XtPointer) False
#else
	XtRBool,
	(XtPointer) &cur_win_rec.slider_is_solid
#endif /* OLD_RESOURCES */
    },
};
#endif

static XtResource modeline_resources[] = {
    {
	XtNforeground,
	XtCForeground,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, modeline_fg),
#if OLD_RESOURCES
	XtRPixel,
	(XtPointer) &cur_win_rec.bg
#else
	XtRString,
	"#ffffffffffff"
#endif /* OLD_RESOURCES */
    },
    {
	XtNbackground,
	XtCBackground,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, modeline_bg),
#if OLD_RESOURCES
	XtRPixel,
	(XtPointer) &cur_win_rec.fg
#else
	XtRString,
	"#70006ca37000"
#endif /* OLD_RESOURCES */
    },
    {
	XtNfocusForeground,
	XtCForeground,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, modeline_focus_fg),
#if OLD_RESOURCES
	XtRPixel,
	(XtPointer) &cur_win_rec.bg
#else
	XtRString,
	"#ffffffffffff"
#endif /* OLD_RESOURCES */
    },
    {
	XtNfocusBackground,
	XtCBackground,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, modeline_focus_bg),
#if OLD_RESOURCES
	XtRPixel,
	(XtPointer) &cur_win_rec.fg
#else
	XtRString,
	"#70006ca37000"
#endif /* OLD_RESOURCES */
    },
};

static XtResource selection_resources[] = {
    {
	XtNforeground,
	XtCBackground,				/* weird, huh? */
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, selection_fg),
	XtRPixel,
	(XtPointer) &cur_win_rec.bg
    },
    {
	XtNbackground,
	XtCForeground,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, selection_bg),
	XtRPixel,
	(XtPointer) &cur_win_rec.fg
    },
};

/*
 * We resort to a bit of trickery for the cursor resources.  Note that the
 * default foreground and background for the cursor is the same as that for
 * the rest of the window.  This would render the cursor invisible!  This
 * condition actually indicates that usual technique of inverting the
 * foreground and background colors should be used, the rationale being
 * that no (sane) user would want to set the cursor foreground and
 * background to be the same as the rest of the window.
 */

static XtResource cursor_resources[] = {
    {
	XtNforeground,
	XtCForeground,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, cursor_fg),
	XtRPixel,
	(XtPointer) &cur_win_rec.fg
    },
    {
	XtNbackground,
	XtCBackground,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, cursor_bg),
	XtRPixel,
	(XtPointer) &cur_win_rec.bg
    },
};

static XtResource pointer_resources[] = {
    {
	XtNforeground,
	XtCForeground,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, pointer_fg),
	XtRPixel,
	(XtPointer) &cur_win_rec.fg
    },
    {
	XtNbackground,
	XtCBackground,
	XtRPixel,
	sizeof(Pixel),
	XtOffset(TextWindow, pointer_bg),
	XtRPixel,
	(XtPointer) &cur_win_rec.bg
    },
    {
	XtNnormalShape,
	XtCNormalShape,
	XtRCursor,
	sizeof(Cursor),
	XtOffset(TextWindow, normal_pointer),
	XtRString,
	(XtPointer) "xterm"
    },
#if OPT_WORKING
    {
	XtNwatchShape,
	XtCWatchShape,
	XtRCursor,
	sizeof(Cursor),
	XtOffset(TextWindow, watch_pointer),
	XtRString,
	(XtPointer) "watch"
    },
#endif
};

#define CHECK_MIN_MAX(v,min,max)	\
	do {				\
	    if ((v) > (max))		\
		(v)=(max);		\
	    else if ((v) < (min))	\
		(v) = (min);		\
	} one_time

static void my_error_handler(String message)
{
    fprintf(stderr, "%s: %s\n", prog_arg, message);
    print_usage();
}

#define MIN_UDIFF  0x1000
#define UDIFF(a,b) ((a)>(b)?(a)-(b):(b)-(a))

/*
 * Returns true if the RGB components are close enough to make distinguishing
 * two colors difficult.
 */
static Boolean
SamePixel(Pixel a, Pixel b)
{
    Boolean result = True;
    XColor a_color;
    XColor b_color;
    Colormap colormap;

    if (a != b) {
	result = False;
	if (cur_win->top_widget != 0) {
	    XtVaGetValues(cur_win->top_widget,
		XtNcolormap,	&colormap,
		NULL);

	    a_color.pixel = a;
	    b_color.pixel = b;

	    if (XQueryColor(dpy, colormap, &a_color)
	     && XQueryColor(dpy, colormap, &b_color)) {
		if (UDIFF(a_color.red,   b_color.red)   < MIN_UDIFF
		 && UDIFF(a_color.green, b_color.green) < MIN_UDIFF
		 && UDIFF(a_color.blue,  b_color.blue)  < MIN_UDIFF)
		    result = True;
	    } else {
		TRACE(("FIXME: SamePixel failed\n"));
	    }
	} else {
	    TRACE(("FIXME: SamePixel cannot compute (too soon)\n"));
	}
    }
    return result;
}

static void
monochrome_cursor(void)
{
    cur_win->is_color_cursor = FALSE;
    cur_win->cursor_fg = cur_win->bg;		/* undo our trickery */
    cur_win->cursor_bg = cur_win->fg;
    cur_win->cursgc = cur_win->reversegc;
    cur_win->revcursgc = cur_win->textgc;
}

static int
color_cursor(void)
{
    ULONG	gcmask;
    XGCValues   gcvals;

    gcmask = GCForeground | GCBackground | GCFont | GCGraphicsExposures;
    gcvals.foreground = cur_win->fg;
    gcvals.background = cur_win->bg;
    gcvals.font = cur_win->pfont->fid;
    gcvals.graphics_exposures = False;

    cur_win->cursgc = XCreateGC(dpy,
		DefaultRootWindow(dpy),
		gcmask, &gcvals);
    cur_win->revcursgc = XCreateGC(dpy,
		DefaultRootWindow(dpy),
		gcmask, &gcvals);

    if (cur_win->cursgc == 0
     || cur_win->revcursgc == 0) {
	if (cur_win->cursgc    != 0) XFreeGC(dpy, cur_win->cursgc);
	if (cur_win->revcursgc != 0) XFreeGC(dpy, cur_win->revcursgc);
	monochrome_cursor();
    }

    cur_win->is_color_cursor = TRUE;
    return TRUE;
}

static GC
get_color_gc(int n, Boolean normal)
{
    ColorGC *data = normal
		? &(cur_win->fore_color[n])
		: &(cur_win->back_color[n]);

    if (cur_win->screen_depth == 1) {
	data->gc = normal
		? cur_win->textgc
		: cur_win->reversegc;
    } else if (data->reset) {
	XGCValues	gcvals;
	ULONG		gcmask;

	gcmask = GCForeground | GCBackground | GCFont | GCGraphicsExposures;
	if (cur_win->bg_follows_fg) {
	    gcvals.foreground = normal ? cur_win->colors_fg[n] : cur_win->colors_bg[n];
	    gcvals.background = normal ? cur_win->colors_bg[n] : cur_win->colors_fg[n];
	} else {
	    if (normal) {
		gcvals.foreground = cur_win->colors_fg[n];
		gcvals.background = cur_win->bg;
	    } else {
		gcvals.foreground = cur_win->bg;
		gcvals.background = cur_win->colors_fg[n];
	    }
	}
	if (gcvals.foreground == gcvals.background) {
	    gcvals.foreground = normal ? cur_win->fg : cur_win->bg;
	    gcvals.background = normal ? cur_win->bg : cur_win->fg;
	}
	gcvals.font = cur_win->pfont->fid;
	gcvals.graphics_exposures = False;

	TRACE(("get_color_gc(%d,%s) %#lx/%#lx\n",
		n,
		normal ? "fg" : "bg",
		(long) gcvals.foreground,
		(long) gcvals.background));

	if (data->gc == 0)
	    data->gc = XCreateGC(dpy, DefaultRootWindow(dpy), gcmask, &gcvals);
	else
	    XChangeGC(dpy, data->gc, gcmask, &gcvals);

	TRACE(("... gc %#lx\n", (long)data->gc));
	data->reset = False;
    }
    return data->gc;
}

static void
reset_color_gcs(void)
{
    int n;

    for (n = 0; n < NCOLORS; n++) {
	cur_win->fore_color[n].reset = True;
	cur_win->back_color[n].reset = True;
    }
}

#if OPT_TRACE
static char *ColorsOf(Pixel pixel)
{
    static char result[80];

    XColor color;
    Colormap colormap;

    XtVaGetValues(cur_win->screen,
	XtNcolormap,	&colormap,
	NULL);

    color.pixel = pixel;
    XQueryColor(dpy, colormap, &color);
    sprintf(result, "%4X/%4X/%4X", color.red, color.green, color.blue);
    return result;
}
#endif

/* ARGSUSED */
int
x_preparse_args(
    int        *pargc,
    char     ***pargv)
{
    XFontStruct *pfont;
    XGCValues   gcvals;
    ULONG	gcmask;
    int		geo_mask, startx, starty;
    int		i, j;
    int		status = TRUE;
    Cardinal	start_cols, start_rows;
    static XrmOptionDescRec options[] = {
	{"-t",		(char *)0,	    XrmoptionSkipArg,	(caddr_t)0 },
	{"-fork",	"*forkOnStartup",   XrmoptionNoArg,	"true" },
	{"+fork",	"*forkOnStartup",   XrmoptionNoArg,	"false" },
	{"-leftbar",	"*scrollbarOnLeft", XrmoptionNoArg,	"true" },
	{"-rightbar",	"*scrollbarOnLeft", XrmoptionNoArg,	"false" },
    };
#if MOTIF_WIDGETS || OL_WIDGETS
    static XtActionsRec new_actions[] = {
	{ "ConfigureBar", configure_bar }
    };
    static String scrollbars_translations =
	"#override \n\
		Ctrl<Btn1Down>:ConfigureBar(Split) \n\
		Ctrl<Btn2Down>:ConfigureBar(Kill) \n\
		Ctrl<Btn3Down>:ConfigureBar(Only)";
    static String fallback_resources[]= {
	"*scrollPane.background:grey80",
	"*scrollbar.background:grey60",
	NULL
    };
#else
#if OPT_KEV_DRAGGING
    static XtActionsRec new_actions[] = {
	{ "ConfigureBar", configure_bar },
	{ "DoScroll", do_scroll },
	{ "ResizeBar", resize_bar }
    };
    static String scrollbars_translations =
	"#override \n\
		Ctrl<Btn1Down>:ConfigureBar(Split) \n\
		Ctrl<Btn2Down>:ConfigureBar(Kill) \n\
		Ctrl<Btn3Down>:ConfigureBar(Only) \n\
		<Btn1Down>:DoScroll(Forward) \n\
		<Btn2Down>:DoScroll(StartDrag) \n\
		<Btn3Down>:DoScroll(Backward) \n\
		<Btn2Motion>:DoScroll(Drag) \n\
		<BtnUp>:DoScroll(End)";
    static String resizeGrip_translations =
	"#override \n\
		<BtnDown>:ResizeBar(Start) \n\
		<BtnMotion>:ResizeBar(Drag) \n\
		<BtnUp>:ResizeBar(End)";
    static String fallback_resources[]= {
	NULL
    };
#else
    static XtActionsRec new_actions[] = {
	{ "ConfigureBar", configure_bar },
	{ "ResizeBar", resize_bar }
    };
    static String scrollbars_translations =
	"#override \n\
		Ctrl<Btn1Down>:ConfigureBar(Split) \n\
		Ctrl<Btn2Down>:ConfigureBar(Kill) \n\
		Ctrl<Btn3Down>:ConfigureBar(Only)";
    static String resizeGrip_translations =
	"#override \n\
		<BtnDown>:ResizeBar(Start) \n\
		<BtnMotion>:ResizeBar(Drag) \n\
		<BtnUp>:ResizeBar(End)";
    static String fallback_resources[]= {
	NULL
    };
#endif /* OPT_KEV_DRAGGING */
#if OPT_XAW_SCROLLBARS
    static char solid_pixmap_bits[] = { '\003', '\003' };
#endif
    static char stippled_pixmap_bits[] = { '\002', '\001' };
#endif /* MOTIF_WIDGETS || OL_WIDGETS */

#if OL_WIDGETS
    /* There is a cryptic statement in the poor documentation that I have
     * on OpenLook that OlToolkitInitialize is now preferred to the older
     * OlInitialize (which is used in the examples and is documented better).
     * The documentation I have says that OlToolkitInitialize returns a
     * widget.  I don't believe it, nor do I understand what kind of widget
     * it would be.  I get the impression that it takes one argument, but
     * I don't know what that argument is supposed to be.
     */
    (void) OlToolkitInitialize( NULL );
#endif /* OL_WIDGETS */

#if OPT_TITLE
    /*
     * If user sets the title explicitly, he probably will not like allowing
     * xvile to set it automatically when visiting a new buffer.
     */
    for (i = 1; i < (*pargc) - 1; i++) {
	char *param = (*pargv)[i];
	unsigned len = strlen(param);
	if (len > 1 && !strncmp(param, "-title", len)) {
	    auto_set_title = FALSE;
	    break;
	}
    }
#endif

    XtSetErrorHandler(my_error_handler);
    cur_win->top_widget = XtVaAppInitialize(
	    &cur_win->app_context,
	    MY_CLASS,
	    options, XtNumber(options),
	    pargc, *pargv,
	    fallback_resources,
	    XtNgeometry,	NULL,
	    XtNinput,		TRUE,
	    NULL);
    XtSetErrorHandler((XtErrorHandler)0);
    dpy = XtDisplay(cur_win->top_widget);

#if 0
    check_visuals();
#endif

    XtVaGetValues(cur_win->top_widget,
		XtNdepth, &cur_win->screen_depth,
		NULL);

#if !OLD_RESOURCES
    cur_win->slider_is_solid = (cur_win->screen_depth >= 6);
#endif

    XtGetApplicationResources(
	    cur_win->top_widget,
	    (XtPointer)cur_win,
	    resources,
	    XtNumber(resources),
	    (ArgList)0,
	    0);

    if (cur_win->fork_on_startup)
	(void) newprocessgroup(TRUE,1);

    if (SamePixel(cur_win->bg, cur_win->fg))
	cur_win->fg = BlackPixel(dpy,DefaultScreen(dpy));
    if (SamePixel(cur_win->bg, cur_win->fg))
	cur_win->bg = WhitePixel(dpy,DefaultScreen(dpy));

    cur_win->default_fg = cur_win->fg;
    cur_win->default_bg = cur_win->bg;

#if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
    XtGetSubresources(
	    cur_win->top_widget,
	    (XtPointer)cur_win,
	    "scrollbar",
	    "Scrollbar",
	    scrollbar_resources,
	    XtNumber(scrollbar_resources),
	    (ArgList)0,
	    0);
#endif	/* OPT_KEV_SCROLLBARS */

    XtGetSubresources(
	    cur_win->top_widget,
	    (XtPointer)cur_win,
	    "modeline",
	    "Modeline",
	    modeline_resources,
	    XtNumber(modeline_resources),
	    (ArgList)0,
	    0);

    XtGetSubresources(
	    cur_win->top_widget,
	    (XtPointer)cur_win,
	    "selection",
	    "Selection",
	    selection_resources,
	    XtNumber(selection_resources),
	    (ArgList)0,
	    0);

    XtGetSubresources(
	    cur_win->top_widget,
	    (XtPointer)cur_win,
	    "cursor",
	    "Cursor",
	    cursor_resources,
	    XtNumber(cursor_resources),
	    (ArgList)0,
	    0);

    XtGetSubresources(
	    cur_win->top_widget,
	    (XtPointer)cur_win,
	    "pointer",
	    "Pointer",
	    pointer_resources,
	    XtNumber(pointer_resources),
	    (ArgList)0,
	    0);

    /*
     * Try to keep the default for fcolor0 looking like something other than
     * white.
     */
    if (SamePixel(cur_win_rec.fg, WhitePixel(dpy,DefaultScreen(dpy)))) {
	static Pixel black;
	TRACE(("force default value of fcolor0 to Black\n"));
	black = BlackPixel(dpy,DefaultScreen(dpy));
	color_resources[0].default_addr = &black;
    }

    XtGetSubresources(
	    cur_win->top_widget,
	    (XtPointer)cur_win,
	    "color",
	    "Color",
	    color_resources,
	    XtNumber(color_resources),
	    (ArgList)0,
	    0);

    /* Initialize atoms needed for getting a fully specified font name */
    atom_FONT		= XInternAtom(dpy, "FONT", False);
    atom_FOUNDRY	= XInternAtom(dpy, "FOUNDRY", False);
    atom_WEIGHT_NAME	= XInternAtom(dpy, "WEIGHT_NAME", False);
    atom_SLANT		= XInternAtom(dpy, "SLANT", False);
    atom_SETWIDTH_NAME	= XInternAtom(dpy, "SETWIDTH_NAME", False);
    atom_PIXEL_SIZE	= XInternAtom(dpy, "PIXEL_SIZE", False);
    atom_RESOLUTION_X	= XInternAtom(dpy, "RESOLUTION_X", False);
    atom_RESOLUTION_Y	= XInternAtom(dpy, "RESOLUTION_Y", False);
    atom_SPACING	= XInternAtom(dpy, "SPACING", False);
    atom_AVERAGE_WIDTH	= XInternAtom(dpy, "AVERAGE_WIDTH", False);
    atom_CHARSET_REGISTRY = XInternAtom(dpy, "CHARSET_REGISTRY", False);
    atom_CHARSET_ENCODING = XInternAtom(dpy, "CHARSET_ENCODING", False);

    pfont = query_font(cur_win, cur_win->starting_fontname);
    if (!pfont) {
	pfont = query_font(cur_win, FONTNAME);
	if (!pfont) {
	    (void)fprintf(stderr, "couldn't get font \"%s\" or \"%s\", exiting\n",
		    cur_win->starting_fontname, FONTNAME);
	    ExitProgram(BADEXIT);
	}
    }
    (void) set_character_class(cur_win->multi_click_char_class);

    /*
     * Look at our copy of the geometry resource to obtain the dimensions of
     * the window in characters.  We've provided a default value of 80x36
     * so there'll always be something to parse.  We still need to check
     * the return mask since the user may specify a position, but no size.
     */
    geo_mask = XParseGeometry(cur_win->geometry,
		&startx, &starty,
		&start_cols, &start_rows);

    cur_win->rows = (geo_mask & HeightValue) ? start_rows : 36;
    cur_win->cols = (geo_mask & WidthValue) ? start_cols : 80;

    /*
     * Fix up the geometry resource of top level shell providing initial
     * position if so requested by user.
     */

    if (geo_mask & (XValue | YValue)) {
	char *gp = cur_win->geometry;
	while (*gp && *gp != '+' && *gp != '-')
	    gp++;			/* skip over width and height */
	if (*gp)
	    XtVaSetValues(cur_win->top_widget,
			XtNgeometry,	gp,
			NULL);
    }

    /* Sanity check values obtained from XtGetApplicationResources */
    CHECK_MIN_MAX(cur_win->pane_width, PANE_WIDTH_MIN, PANE_WIDTH_MAX);


#if MOTIF_WIDGETS
    cur_win->form_widget = XtVaCreateManagedWidget(
	    "form",
	    xmFormWidgetClass,
	    cur_win->top_widget,
	    NULL);
#else
#if OL_WIDGETS
    cur_win->form_widget = XtVaCreateManagedWidget(
	    "form",
	    formWidgetClass,
	    cur_win->top_widget,
	    NULL);
#else
#if ATHENA_WIDGETS && OPT_MENUS
    cur_win->pane_widget = XtVaCreateManagedWidget(
	    "pane",
	    panedWidgetClass,
	    cur_win->top_widget,
	    NULL);
    cur_win->menu_widget = XtVaCreateManagedWidget(
	    "menubar",
	    boxWidgetClass,
	    cur_win->pane_widget,
	    XtNshowGrip,		False,
	    NULL);
    cur_win->form_widget = XtVaCreateManagedWidget(
	    "form",
#if KEV_WIDGETS	/* FIXME */
	    bbWidgetClass,
#else
	    formWidgetClass,
#endif
	    cur_win->pane_widget,
	    XtNwidth,			x_width(cur_win)
						+ cur_win->pane_width + 2,
	    XtNheight,			x_height(cur_win),
	    XtNbackground,		cur_win->bg,
	    XtNbottom,			XtChainBottom,
	    XtNleft,			XtChainLeft,
	    XtNright,			XtChainRight,
	    XtNfromVert,		cur_win->menu_widget,
	    XtNvertDistance,		0,
	    NULL);
#else
#if ATHENA_WIDGETS
    cur_win->form_widget = XtVaCreateManagedWidget(
	    "form",
#if KEV_WIDGETS	/* FIXME */
	    bbWidgetClass,
#else
	    formWidgetClass,
#endif
	    cur_win->top_widget,
	    XtNwidth,			x_width(cur_win)
						+ cur_win->pane_width + 2,
	    XtNheight,			x_height(cur_win),
	    XtNbackground,		cur_win->bg,
	    XtNbottom,			XtChainBottom,
	    XtNleft,			XtChainLeft,
	    XtNright,			XtChainRight,
	    NULL);
#else
#if NO_WIDGETS
    cur_win->form_widget = XtVaCreateManagedWidget(
	    "form",
	    bbWidgetClass,
	    cur_win->top_widget,
	    XtNwidth,			x_width(cur_win)
						+ cur_win->pane_width + 2,
	    XtNheight,			x_height(cur_win),
	    XtNbackground,		cur_win->bg,
	    NULL);
#endif /* NO_WIDGETS */
#endif /* ATHENA_WIDGETS */
#endif /* ATHENA_WIDGETS && OPT_MENUS */
#endif /* OL_WIDGETS */
#endif /* MOTIF_WIDGETS */

#if OPT_MENUS
#if ATHENA_WIDGETS
	status = do_menu ( cur_win->menu_widget );
#endif
#if MOTIF_WIDGETS
	menub = XmCreateMenuBar (cur_win->form_widget, "menub", NULL, 0);

	XtVaSetValues (menub,
			XmNtopAttachment,	XmATTACH_FORM,
			XmNleftAttachment,	XmATTACH_FORM,
			XmNbottomAttachment,	XmATTACH_NONE,
			XmNrightAttachment,	XmATTACH_FORM,
			NULL);
	XtManageChild (menub);
	status = do_menu ( menub );
#endif
#endif

    cur_win->screen = XtVaCreateManagedWidget(
	    "screen",
#if MOTIF_WIDGETS
	    xmPrimitiveWidgetClass,
#else
	    coreWidgetClass,
#endif
	    cur_win->form_widget,
	    XtNwidth,			x_width(cur_win),
	    XtNheight,			x_height(cur_win),
	    XtNborderWidth,		0,
	    XtNbackground,		cur_win->bg,
#if MOTIF_WIDGETS
	    XmNresizable,		TRUE,
	    XmNbottomAttachment,	XmATTACH_FORM,
#if OPT_MENUS
	    XmNtopAttachment,		XmATTACH_WIDGET,
	    XmNtopWidget,		menub,
	    XmNtopOffset,		2,
#else
	    XmNtopAttachment,		XmATTACH_FORM,
#endif
	    XmNleftAttachment,		XmATTACH_FORM,
	    XmNrightAttachment,		XmATTACH_NONE,
#else
#if OL_WIDGETS
	    XtNyAttachBottom,		TRUE,
	    XtNyVaryOffset,		FALSE,
	    XtNxAddWidth,		TRUE,
	    XtNyAddHeight,		TRUE,
#else
#if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
	    XtNx,			cur_win->scrollbar_on_left
					    ? cur_win->pane_width+2
					    : 0,
	    XtNy,			0,
#endif	/* OPT_KEV_SCROLLBARS */
#endif	/* OL_WIDGETS */
#endif	/* MOTIF_WIDGETS */
	    NULL);

#if defined(LESSTIF_VERSION)
    /*
     * Lesstif (0.81) seems to install translations that cause certain
     * keys (like TAB) to manipulate the focus in addition to their
     * functions within xvile.  This leads to frustration when you are
     * trying to insert text, and find the focus shifting to a scroll
     * bar or whatever.  To fix this problem, we remove those nasty
     * translations here.
     *
     * Aside from this little nit, "lesstif" seems to work admirably
     * with xvile.  Just build with screen=motif.  You can find
     * lesstif at:  ftp://ftp.lesstif.org/pub/hungry/lesstif
     */

    XtUninstallTranslations(cur_win->screen);
#endif  /* LESSTIF_VERSION */

    /* Initialize graphics context for display of normal and reverse text */
    gcmask = GCForeground | GCBackground | GCFont | GCGraphicsExposures;
    gcvals.foreground = cur_win->fg;
    gcvals.background = cur_win->bg;
    gcvals.font = cur_win->pfont->fid;
    gcvals.graphics_exposures = False;
    cur_win->textgc = XCreateGC(dpy,
	    DefaultRootWindow(dpy),
	    gcmask, &gcvals);
    cur_win->exposed    = FALSE;
    cur_win->visibility = VisibilityUnobscured;

    gcvals.foreground = cur_win->bg;
    gcvals.background = cur_win->fg;
    gcvals.font = cur_win->pfont->fid;
    cur_win->reversegc = XCreateGC(dpy,
	    DefaultRootWindow(dpy),
	    gcmask, &gcvals);

    cur_win->bg_follows_fg = (gbcolor == ENUM_FCOLOR);
    if ((j = gbcolor) < 0)
	j = 0;

    TRACE(("colors_fg/colors_bg pixel values:\n"));
    for (i = 0; i < NCOLORS; i++) {
	ctrans[i] = i;
	TRACE(("   [%2d]", i));
	TRACE((" %#8lx %s",   cur_win->colors_fg[i], ColorsOf(cur_win->colors_fg[i])));
	TRACE((" %#8lx %s\n", cur_win->colors_bg[i], ColorsOf(cur_win->colors_bg[i])));
    }
    reset_color_gcs();

    /* Initialize graphics context for display of selections */
    if (cur_win->screen_depth == 1
     || SamePixel(cur_win->selection_bg, cur_win->selection_fg)
     ||  (SamePixel(cur_win->fg, cur_win->selection_fg)
       && SamePixel(cur_win->bg, cur_win->selection_bg))
     ||  (SamePixel(cur_win->fg, cur_win->selection_bg)
       && SamePixel(cur_win->bg, cur_win->selection_fg))) {
	cur_win->selgc = cur_win->reversegc;
	cur_win->revselgc = cur_win->textgc;
    }
    else {
	gcvals.foreground = cur_win->selection_fg;
	gcvals.background = cur_win->selection_bg;
	cur_win->selgc = XCreateGC(dpy,
		DefaultRootWindow(dpy),
		gcmask, &gcvals);
	gcvals.foreground = cur_win->selection_bg;
	gcvals.background = cur_win->selection_fg;
	cur_win->revselgc = XCreateGC(dpy,
		DefaultRootWindow(dpy),
		gcmask, &gcvals);
    }

    /*
     * Initialize graphics context for display of normal modelines.
     * Portions of the modeline are never displayed in reverse video (wrt
     * the modeline) so there is no corresponding reverse video gc.
     */
    if (cur_win->screen_depth == 1
     || SamePixel(cur_win->modeline_bg, cur_win->modeline_fg)
     ||  (SamePixel(cur_win->fg, cur_win->modeline_fg)
       && SamePixel(cur_win->bg, cur_win->modeline_bg))
     ||  (SamePixel(cur_win->fg, cur_win->modeline_bg)
       && SamePixel(cur_win->bg, cur_win->modeline_fg))) {
	cur_win->modeline_gc = cur_win->reversegc;
    }
    else {
	gcvals.foreground = cur_win->modeline_fg;
	gcvals.background = cur_win->modeline_bg;
	cur_win->modeline_gc = XCreateGC(dpy,
					 DefaultRootWindow(dpy),
					 gcmask, &gcvals);
    }

    /*
     * Initialize graphics context for display of modelines which indicate
     * that the corresponding window has focus.
     */
    if (cur_win->screen_depth == 1
     || SamePixel(cur_win->modeline_focus_bg, cur_win->modeline_focus_fg)
     ||  (SamePixel(cur_win->fg, cur_win->modeline_focus_fg)
       && SamePixel(cur_win->bg, cur_win->modeline_focus_bg))
     ||  (SamePixel(cur_win->fg, cur_win->modeline_focus_bg)
       && SamePixel(cur_win->bg, cur_win->modeline_focus_fg))) {
	cur_win->modeline_focus_gc = cur_win->reversegc;
    }
    else {
	gcvals.foreground = cur_win->modeline_focus_fg;
	gcvals.background = cur_win->modeline_focus_bg;
	cur_win->modeline_focus_gc = XCreateGC(dpy,
					       DefaultRootWindow(dpy),
					       gcmask, &gcvals);
    }

    /* Initialize cursor graphics context and flag which indicates how to
     * display cursor.
     */
    if (cur_win->screen_depth == 1
     || SamePixel(cur_win->cursor_bg, cur_win->cursor_fg)
     ||  (SamePixel(cur_win->fg, cur_win->cursor_fg)
       && SamePixel(cur_win->bg, cur_win->cursor_bg))
     ||  (SamePixel(cur_win->fg, cur_win->cursor_bg)
       && SamePixel(cur_win->bg, cur_win->cursor_fg))) {
	monochrome_cursor();
    } else if (color_cursor()) {
	x_ccol(-1);
    }

#if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
    if (SamePixel(cur_win->scrollbar_bg, cur_win->scrollbar_fg)) {
	cur_win->scrollbar_bg = cur_win->bg;
	cur_win->scrollbar_fg = cur_win->fg;
    }
    if (cur_win->screen_depth == 1 || too_light_or_too_dark(cur_win->scrollbar_fg))
	cur_win->slider_is_solid = False;
#endif /* OPT_KEV_SCROLLBARS */

#if OPT_XAW_SCROLLBARS
    cur_win->thumb_bm =
	XCreateBitmapFromData(dpy, DefaultRootWindow(dpy),
	    cur_win->slider_is_solid
		? solid_pixmap_bits
		: stippled_pixmap_bits,
	    2, 2);
#endif /* OPT_XAW_SCROLLBARS */

#if OPT_KEV_SCROLLBARS
    gcvals.background = cur_win->scrollbar_bg;
    if (!cur_win->slider_is_solid) {
	gcmask = GCFillStyle | GCStipple | GCForeground | GCBackground;
	gcvals.foreground = cur_win->scrollbar_fg;
	gcvals.fill_style = FillOpaqueStippled;
	gcvals.stipple = XCreatePixmapFromBitmapData(dpy,
		DefaultRootWindow(dpy),
		stippled_pixmap_bits,
		2, 2,
		1L, 0L,
		1);
    }
    else {
	gcmask = GCForeground | GCBackground;
	gcvals.foreground = cur_win->scrollbar_fg;
    }
    gcmask |= GCGraphicsExposures;
    gcvals.graphics_exposures = False;
    cur_win->scrollbargc = XCreateGC(dpy,
	    DefaultRootWindow(dpy),
	    gcmask, &gcvals);

    if (cur_win->screen_depth >= 6 && cur_win->slider_is_solid) {
	Pixel fg_light, fg_dark, bg_light, bg_dark;
	if ( alloc_shadows(cur_win->scrollbar_fg, &fg_light, &fg_dark)
	  && alloc_shadows(cur_win->scrollbar_bg, &bg_light, &bg_dark)) {
	    GC gc;
	    Pixmap slider_pixmap;
	    cur_win->slider_is_3D = True;

	    cur_win->trough_pixmap = XCreatePixmap(dpy, DefaultRootWindow(dpy),
		    cur_win->pane_width+2, 16, cur_win->screen_depth);

#define TROUGH_HT 16
	    gcvals.foreground = cur_win->scrollbar_bg;
	    gc = XCreateGC(dpy, DefaultRootWindow(dpy), gcmask, &gcvals);
	    XFillRectangle(dpy, cur_win->trough_pixmap, gc, 0,0,
		    cur_win->pane_width+2, TROUGH_HT);
	    XSetForeground(dpy, gc, bg_dark);
	    XFillRectangle(dpy, cur_win->trough_pixmap, gc, 0,0, 2, TROUGH_HT);
	    XSetForeground(dpy, gc, bg_light);
	    XFillRectangle(dpy, cur_win->trough_pixmap, gc,
		    (int) cur_win->pane_width, 0, 2, TROUGH_HT);

	    slider_pixmap = XCreatePixmap(dpy, DefaultRootWindow(dpy),
		    cur_win->pane_width-2, SP_HT, cur_win->screen_depth);

	    XSetForeground(dpy, gc, cur_win->scrollbar_fg);
	    XFillRectangle(dpy, slider_pixmap, gc, 0,0,
		    cur_win->pane_width-2, SP_HT);
	    XSetForeground(dpy, gc, fg_light);
	    XFillRectangle(dpy, slider_pixmap, gc, 0,0, 2, SP_HT);
	    XSetForeground(dpy, gc, fg_dark);
	    XFillRectangle(dpy, slider_pixmap, gc,
		    (int) cur_win->pane_width-4, 0, 2, SP_HT);

	    XSetTile(dpy, cur_win->scrollbargc, slider_pixmap);
	    XSetFillStyle(dpy, cur_win->scrollbargc, FillTiled);
	    XSetTSOrigin(dpy, cur_win->scrollbargc, 2, 0);

	    cur_win->slider_pixmap = XCreatePixmap(dpy, DefaultRootWindow(dpy),
		    cur_win->pane_width-2, SP_HT, cur_win->screen_depth);
	    XCopyArea(dpy, slider_pixmap, cur_win->slider_pixmap, gc,
		      0, 0, cur_win->pane_width-2, SP_HT, 0, 0);

	    /* Draw top bevel */
	    XSetForeground(dpy, gc, fg_light);
	    XDrawLine(dpy, cur_win->slider_pixmap, gc,
		      0, 0, (int)cur_win->pane_width-3, 0);
	    XDrawLine(dpy, cur_win->slider_pixmap, gc,
		      0, 1, (int)cur_win->pane_width-4, 1);

	    /* Draw bottom bevel */
	    XSetForeground(dpy, gc, fg_dark);
	    XDrawLine(dpy, cur_win->slider_pixmap, gc,
		      2, SP_HT-2, (int)cur_win->pane_width-3, SP_HT-2);
	    XDrawLine(dpy, cur_win->slider_pixmap, gc,
		      1, SP_HT-1, (int)cur_win->pane_width-3, SP_HT-1);

	    XFreeGC(dpy, gc);
	}
    }
#endif /* OPT_KEV_SCROLLBARS */

    XtAppAddActions(cur_win->app_context, new_actions, XtNumber(new_actions));
    cur_win->my_scrollbars_trans = XtParseTranslationTable(scrollbars_translations);

#if MOTIF_WIDGETS
    cur_win->pane = XtVaCreateManagedWidget(
	    "scrollPane",
	    xmPanedWindowWidgetClass,
	    cur_win->form_widget,
	    XtNwidth,			cur_win->pane_width,
	    XmNbottomAttachment,	XmATTACH_FORM,
	    XmNtraversalOn,		False, /* Added by gdr */
#if OPT_MENUS
	    XmNtopAttachment,		XmATTACH_WIDGET,
	    XmNtopWidget,		menub,
#else
	    XmNtopAttachment,		XmATTACH_FORM,
#endif
	    XmNleftAttachment,		XmATTACH_WIDGET,
	    XmNleftWidget,		cur_win->screen,
	    XmNrightAttachment,		XmATTACH_FORM,
	    XmNspacing,			cur_win->char_height,
	    XmNsashIndent,		2,
	    XmNsashWidth,		cur_win->pane_width - 4,
	    XmNmarginHeight,		0,
	    XmNmarginWidth,		0,
	    XmNseparatorOn,		FALSE,
	    NULL);
#else
#if OL_WIDGETS
    cur_win->pane = XtVaCreateManagedWidget(
	    "scrollPane",
	    bulletinBoardWidgetClass,
	    cur_win->form_widget,
	    XtNwidth,			cur_win->pane_width,
	    XtNheight,			x_height(cur_win),
	    XtNxRefWidget,		cur_win->screen,
	    XtNyAttachBottom,		TRUE,
	    XtNyVaryOffset,		FALSE,
	    XtNxAddWidth,		TRUE,
	    XtNyAddHeight,		TRUE,
	    XtNlayout,			OL_IGNORE,
	    NULL);
#else
#if OPT_XAW_SCROLLBARS
    cur_win->my_resizeGrip_trans = XtParseTranslationTable(resizeGrip_translations);
    cur_win->pane = XtVaCreateManagedWidget(
	    "scrollPane",
	    formWidgetClass,
	    cur_win->form_widget,
	    XtNwidth,			cur_win->pane_width + 2,
	    XtNheight,			x_height(cur_win)
						- cur_win->char_height,
	    XtNx,			cur_win->scrollbar_on_left
					    ? 0
					    : x_width(cur_win),
	    XtNy,			0,
	    XtNborderWidth,		0,
	    XtNbackground,		cur_win->modeline_bg,
	    XtNfromHoriz,		cur_win->scrollbar_on_left
					    ? NULL
					    : cur_win->screen,
	    XtNhorizDistance,		0,
	    NULL);
    if (cur_win->scrollbar_on_left)
	XtVaSetValues(cur_win->screen,
	    XtNfromHoriz,		cur_win->pane,
	    NULL);
#else
#if OPT_KEV_SCROLLBARS
    cur_win->my_resizeGrip_trans = XtParseTranslationTable(resizeGrip_translations);
    cur_win->pane = XtVaCreateManagedWidget(
	    "scrollPane",
	    bbWidgetClass,
	    cur_win->form_widget,
	    XtNwidth,			cur_win->pane_width + 2,
	    XtNheight,			x_height(cur_win)
						- cur_win->char_height,
	    XtNx,			cur_win->scrollbar_on_left
					    ? 0
					    : x_width(cur_win),
	    XtNy,			0,
	    XtNborderWidth,		0,
	    XtNbackground,		cur_win->modeline_bg,
	    NULL);
#endif	/* OPT_KEV_SCROLLBARS */
#endif	/* OPT_XAW_SCROLLBARS */
#endif	/* OL_WIDGETS */
#endif	/* MOTIF_WIDGETS */

#if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
    curs_sb_v_double_arrow = XCreateFontCursor(dpy, XC_sb_v_double_arrow);
    curs_sb_up_arrow       = XCreateFontCursor(dpy, XC_sb_up_arrow);
    curs_sb_down_arrow     = XCreateFontCursor(dpy, XC_sb_down_arrow);
    curs_sb_left_arrow     = XCreateFontCursor(dpy, XC_sb_left_arrow);
    curs_sb_right_arrow    = XCreateFontCursor(dpy, XC_sb_right_arrow);
    curs_double_arrow      = XCreateFontCursor(dpy, XC_double_arrow);
#endif

#if OPT_KEV_SCROLLBARS
    cur_win->nscrollbars = 0;
#else
    cur_win->nscrollbars = -1;
#endif

    /*
     * Move scrollbar to the left if requested via the resources.
     * Note that this is handled elsewhere for NO_WIDGETS.
     */
    if (cur_win->scrollbar_on_left) {
#if MOTIF_WIDGETS
	XtVaSetValues(cur_win->pane,
	    XmNleftAttachment,	XmATTACH_FORM,
	    XmNrightAttachment, XmATTACH_WIDGET,
	    XmNrightWidget,	cur_win->screen,
	    NULL);
	XtVaSetValues(cur_win->screen,
	    XmNleftAttachment,	XmATTACH_NONE,
	    XmNrightAttachment,	XmATTACH_FORM,
	    NULL);
#else	/* !MOTIF_WIDGETS */
# if OL_WIDGETS
	XtVaSetValues(cur_win->pane,
	    XtNxRefWidget,	cur_win->form_widget,
	    NULL);
	XtVaSetValues(cur_win->screen,
	    XtNxRefWidget,	cur_win->pane,
	    NULL);
# else
	/* EMPTY */;
# endif	/* OL_WIDGETS */
#endif	/* !MOTIF_WIDGETS */
    }

    XtAddEventHandler(
	    cur_win->screen,
	    KeyPressMask,
	    FALSE,
	    x_key_press,
	    (XtPointer)0);

    XtAddEventHandler(
	    cur_win->screen,
	    (EventMask) (ButtonPressMask | ButtonReleaseMask
		       | (cur_win->focus_follows_mouse ? PointerMotionMask
						       : (Button1MotionMask | Button3MotionMask))
		       | ExposureMask | VisibilityChangeMask),
	    TRUE,
	    x_process_event,
	    (XtPointer)0);

    XtAddEventHandler(
	    cur_win->top_widget,
	    StructureNotifyMask,
	    FALSE,
	    x_configure_window,
	    (XtPointer)0);

    XtAddEventHandler(
	    cur_win->top_widget,
	    EnterWindowMask | LeaveWindowMask | FocusChangeMask,
	    FALSE,
	    x_change_focus,
	    (XtPointer)0);

    /* Realize the widget, but don't do the mapping (yet...) */
    XtSetMappedWhenManaged(cur_win->top_widget, False);
    XtRealizeWidget(cur_win->top_widget);

    /* Now that the widget hierarchy is realized, we can fetch
       some crucial dimensions and set the window manager hints
       dealing with resize appropriately. */
    {
	Dimension new_width, new_height;
	XtVaGetValues(cur_win->top_widget,
		XtNheight,	&cur_win->top_height,
		XtNwidth,	&cur_win->top_width,
		NULL);
#if ATHENA_WIDGETS && OPT_MENUS
	XtVaGetValues(cur_win->menu_widget,
		XtNheight,	&cur_win->menu_height,
		XtNwidth,	&new_width,
		NULL);
#endif
	XtVaGetValues(cur_win->screen,
		XtNheight,	&new_height,
		XtNwidth,	&new_width,
		NULL);

	cur_win->base_width = cur_win->top_width - new_width;
	cur_win->base_height = cur_win->top_height - new_height;

	/* Ugly hack:  If the window manager chooses not to respect
	   the min_height hint, it may instead choose to use base_height
	   as min_height instead.  I believe that the reason for this
	   is because O'Reilly's Xlib Programming Manual, Vol 1
	   has lead some window manager implementors astray.  It says:

		In R4, the base_width and base_height fields have been
		added to the XSizeHints structure.  They are used with
		the width_inc and height_inc fields to indicate to the
		window manager that it should resize the window in
		steps -- in units of a certain number of pixels
		instead of single pixels.  The window manager resizes
		the window to any multiple of width_inc in width and
		height_inc in height, but no smaller than min_width
		and min_height and no bigger than max_width and
		max_height.  If you think about it, min_width and
		min_height and base_width and base_height have
		basically the same purpose.  Therefore, base_width and
		base_height take priority over min_width and
		min_height, so only one of these pairs should be set.

	   We are indeed lucky that most window managers have chosen to
	   ignore the last two sentences in the above paragraph.  These
	   two pairs of values *do not* serve the same purpose.  I can
	   see where they're coming from...  if you have a minimum
	   height that you want the application to be, you could simply
	   set base_height to be the this minimum height which would be
	   the real_base_height + N*unit_height where N is a
	   non-negative integer.  But what they're forgetting in all
	   this is that the window manager reports the size in units
	   to the user and the size it reports will likely be off by N.

	   Unfortunately, enlightenment 0.16.3 (and probably other
	   versions too) do seem to only use base_height and
	   base_width to determine the smallest window size and this
	   is causing some problems for xvile with no menu bars.
	   Specifically, I've seen BadValue errors from the guts of Xt
	   when cur_win->screen gets resized down to zero.  So we make
	   sure that base_height is non-zero and hope the user doesn't
	   notice that extra pixel of height.  */
	if (cur_win->base_height == 0)
	    cur_win->base_height = 1;

	XtVaSetValues(cur_win->top_widget,
#if XtSpecificationRelease >= 4
		XtNbaseHeight,	cur_win->base_height,
		XtNbaseWidth,	cur_win->base_width,
#endif
		XtNminHeight,	cur_win->base_height
				    + MINROWS*cur_win->char_height,
		XtNminWidth,	cur_win->base_width
				    + MINCOLS*cur_win->char_width,
		XtNheightInc,	cur_win->char_height,
		XtNwidthInc,	cur_win->char_width,
		NULL);
    }
    /* According to the docs, this should map the widget too... */
    XtSetMappedWhenManaged(cur_win->top_widget, True);
    /* ... but an explicit map calls seems to be necessary anyway */
    XtMapWidget(cur_win->top_widget);

    cur_win->win = XtWindow(cur_win->screen);

#if OPT_LOCALE
    init_xlocale();
#endif

#if OPT_X11_ICON
    {
#ifdef VILE_ICON
# include VILE_ICON
#else
# if HAVE_LIBXPM
#  include <icons/vile.xpm>
# else
#  ifndef VMS
#   include <icons/vile.xbm>
#  else
#   include "icons/vile.xbm"
#  endif
# endif
#endif
	XWMHints *hints = XGetWMHints(dpy, XtWindow(cur_win->top_widget));
	if (!hints) hints = XAllocWMHints();

	if (hints)
	{
	    hints->flags = IconPixmapHint;
#if HAVE_LIBXPM
	    XpmCreatePixmapFromData(
		dpy, DefaultRootWindow(dpy),
		vile, &hints->icon_pixmap, 0, 0
	    );
#else
	    hints->icon_pixmap =
		XCreateBitmapFromData(
		    dpy, DefaultRootWindow(dpy),
		    (char *)vile_bits, vile_width, vile_height
		);
#endif

	    XSetWMHints(dpy, XtWindow(cur_win->top_widget), hints);
	    XFree(hints);
	}
    }
#endif /* OPT_X11_ICON */

    /* We wish to participate in the "delete window" protocol */
    atom_WM_PROTOCOLS = XInternAtom(dpy, "WM_PROTOCOLS", False);
    atom_WM_DELETE_WINDOW = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
    {
	Atom atoms[2];
	i = 0;
	atoms[i++] = atom_WM_DELETE_WINDOW;
	XSetWMProtocols(dpy,
		XtWindow(cur_win->top_widget),
		atoms,
		i);
    }
    XtAddEventHandler(
	    cur_win->top_widget,
	    NoEventMask,
	    TRUE,
	    x_wm_delwin,
	    (XtPointer)0);

    /* Atoms needed for selections */
    atom_TARGETS	= XInternAtom(dpy, "TARGETS",   False);
    atom_MULTIPLE	= XInternAtom(dpy, "MULTIPLE",  False);
    atom_TIMESTAMP	= XInternAtom(dpy, "TIMESTAMP", False);
    atom_TEXT		= XInternAtom(dpy, "TEXT",      False);
    atom_CLIPBOARD	= XInternAtom(dpy, "CLIPBOARD", False);

    set_pointer(XtWindow(cur_win->screen), cur_win->normal_pointer);

    return status;
}

#if 0
static void
check_visuals(void)
{
    static char *classes[] = { "StaticGray", "GrayScale", "StaticColor", "PseudoColor", "TrueColor", "DirectColor" };
    XVisualInfo *visuals, visual_template;
    int nvisuals;
    visuals = XGetVisualInfo(dpy, VisualNoMask, &visual_template, &nvisuals);
    if (visuals != NULL) {
	int i;
	for (i=0; i<nvisuals; i++) {
	    printf("Class: %s, Depth: %d\n",
		   classes[visuals[i].class], visuals[i].depth);
	}
	XFree(visuals);
    }
}
#endif


#if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
static Boolean
too_light_or_too_dark(
    Pixel pixel)
{
    XColor color;
    Colormap colormap;

    XtVaGetValues(cur_win->screen,
	XtNcolormap,	&colormap,
	NULL);

    color.pixel = pixel;
    XQueryColor(dpy, colormap, &color);

    return (color.red > 0xfff0 && color.green > 0xfff0 && color.blue > 0xfff0)
	|| (color.red < 0x0020 && color.green < 0x0020 && color.blue < 0x0020);
}
#endif

#if OPT_KEV_SCROLLBARS
static Boolean
alloc_shadows(
    Pixel pixel,
    Pixel *light,
    Pixel *dark)
{
    XColor color;
    Colormap colormap;
    unsigned long lred, lgreen, lblue, dred, dgreen, dblue;

    XtVaGetValues(cur_win->screen,
	XtNcolormap,	&colormap,
	NULL);

    color.pixel = pixel;
    XQueryColor(dpy, colormap, &color);

    if ( (color.red > 0xfff0 && color.green > 0xfff0 && color.blue > 0xfff0)
      || (color.red < 0x0020 && color.green < 0x0020 && color.blue < 0x0020))
	return False;			/* It'll look awful! */

#define MAXINTENS ((unsigned long)65535L)
#define PlusFortyPercent(v) ((7 * (long) (v)) / 5)
    lred   = PlusFortyPercent(color.red);
    lred   = min(lred, MAXINTENS);
    lred   = max(lred, (color.red + MAXINTENS)/2);

    lgreen = PlusFortyPercent(color.green);
    lgreen = min(lgreen, MAXINTENS);
    lgreen = max(lgreen, (color.green + MAXINTENS)/2);

    lblue  = PlusFortyPercent(color.blue);
    lblue  = min(lblue, MAXINTENS);
    lblue  = max(lblue, (color.blue + MAXINTENS)/2);

#define MinusFortyPercent(v) ((3 * (long) (v)) / 5)

    dred   = MinusFortyPercent(color.red);
    dgreen = MinusFortyPercent(color.green);
    dblue  = MinusFortyPercent(color.blue);

    color.red = (unsigned short) lred;
    color.green = (unsigned short) lgreen;
    color.blue = (unsigned short) lblue;

    if (!XAllocColor(dpy, colormap, &color))
	return False;

    *light = color.pixel;

    color.red = (unsigned short) dred;
    color.green = (unsigned short) dgreen;
    color.blue = (unsigned short) dblue;

    if (!XAllocColor(dpy, colormap, &color))
	return False;

    *dark = color.pixel;

    return True;
}
#endif

char *
x_current_fontname(void)
{
    return cur_win->fontname;
}

static char *
x_get_font_atom_property(
    XFontStruct *pf,
    Atom atom)
{
    XFontProp *pp;
    int i;
    char *retval = NULL;

    for (i=0, pp = pf->properties; i < pf->n_properties; i++, pp++)
	if (pp->name == atom) {
	    retval = XGetAtomName(dpy, pp->card32);
	    break;
	}
    return retval;
}

static XFontStruct *
query_font(
	TextWindow tw,
	const char *fname)
{
    XFontStruct *pf;

    TRACE(("x11:query_font(%s)\n", fname));
    if ((pf = XLoadQueryFont(dpy, fname)) != 0) {
	char *fullname = NULL;

	if (pf->max_bounds.width != pf->min_bounds.width) {
	    (void)fprintf(stderr,
			  "proportional font, things will be miserable\n");
	}

	/*
	 * Free resources assoicated with any presently loaded fonts.
	 */
	if (tw->pfont)
	    XFreeFont(dpy, tw->pfont);
	if (tw->pfont_bold) {
	    XFreeFont(dpy, tw->pfont_bold);
	    tw->pfont_bold = NULL;
	}
	if (tw->pfont_ital) {
	    XFreeFont(dpy, tw->pfont_ital);
	    tw->pfont_ital = NULL;
	}
	if (tw->pfont_boldital) {
	    XFreeFont(dpy, tw->pfont_boldital);
	    tw->pfont_boldital = NULL;
	}
	tw->fsrch_flags = 0;

	tw->pfont = pf;
	tw->char_width  = pf->max_bounds.width;
	tw->char_height = pf->ascent + pf->descent;
	tw->char_ascent = pf->ascent;
	tw->char_descent = pf->descent;
	tw->left_ink	= (pf->min_bounds.lbearing < 0);
	tw->right_ink	= (pf->max_bounds.rbearing > tw->char_width);

	TRACE(("...success left:%d, right:%d\n", tw->left_ink, tw->right_ink));

	FreeIfNeeded(cur_win->fontname);
	if ((fullname = x_get_font_atom_property(pf, atom_FONT)) != NULL
	 && fullname[0] == '-') {
	    /*
	     * Good. Not much work to do; the name was available via the FONT
	     * property.
	     */
	    tw->fontname = strmalloc(fullname);
	    XFree(fullname);
	    TRACE(("...resulting FONT property font %s\n", tw->fontname));
	}
	else {
	    /*
	     * Woops, fully qualified name not available from the FONT property.
	     * Attempt to get the full name piece by piece.  Ugh!
	     */
	    char str[1024], *s;
	    if (fullname != NULL)
		XFree(fullname);

	    s = str;
	    *s++ = '-';

#define GET_ATOM_OR_STAR(atom)					\
    do {							\
	char *as;						\
	if ((as = x_get_font_atom_property(pf, (atom))) != NULL) { \
	    char *asp = as;					\
	    while ((*s++ = *asp++))				\
		;						\
	    *(s-1) = '-';					\
	    XFree(as);						\
	}							\
	else {							\
	    *s++ = '*';						\
	    *s++ = '-';						\
	}							\
    } one_time

#define GET_ATOM_OR_GIVEUP(atom)				\
    do {							\
	char *as;						\
	if ((as = x_get_font_atom_property(pf, (atom))) != NULL) { \
	    char *asp = as;					\
	    while ((*s++ = *asp++))				\
		;						\
	    *(s-1) = '-';					\
	    XFree(as);						\
	}							\
	else							\
	    goto piecemeal_done;				\
    } one_time

#define GET_LONG_OR_GIVEUP(atom)				\
    do {							\
	unsigned long val;					\
	if (XGetFontProperty(pf, (atom), &val)) {		\
	    sprintf(s,"%ld",val);				\
	    while (*s++ != '\0')				\
		;						\
	    *(s-1) = '-';					\
	}							\
	else							\
	    goto piecemeal_done;				\
    } one_time

	    GET_ATOM_OR_STAR(atom_FOUNDRY);
	    GET_ATOM_OR_GIVEUP(XA_FAMILY_NAME);
	    GET_ATOM_OR_GIVEUP(atom_WEIGHT_NAME);
	    GET_ATOM_OR_GIVEUP(atom_SLANT);
	    GET_ATOM_OR_GIVEUP(atom_SETWIDTH_NAME);
	    *s++ = '*';				/* ADD_STYLE_NAME */
	    *s++ = '-';
	    GET_LONG_OR_GIVEUP(atom_PIXEL_SIZE);
	    GET_LONG_OR_GIVEUP(XA_POINT_SIZE);
	    GET_LONG_OR_GIVEUP(atom_RESOLUTION_X);
	    GET_LONG_OR_GIVEUP(atom_RESOLUTION_Y);
	    GET_ATOM_OR_GIVEUP(atom_SPACING);
	    GET_LONG_OR_GIVEUP(atom_AVERAGE_WIDTH);
	    GET_ATOM_OR_STAR(atom_CHARSET_REGISTRY);
	    GET_ATOM_OR_STAR(atom_CHARSET_ENCODING);
	    *(s-1) = '\0';

#undef GET_ATOM_OR_STAR
#undef GET_ATOM_OR_GIVEUP
#undef GET_LONG_OR_GIVEUP

	    fname = str;
piecemeal_done:
	    /*
	     * We will either use the name which was built up piecemeal or
	     * the name which was originally passed to us to assign to
	     * the fontname field.  We prefer the fully qualified name
	     * so that we can later search for bold and italic fonts.
	     */
	    tw->fontname = strmalloc(fname);
	    TRACE(("...resulting piecemeal font %s\n", tw->fontname));
	}
    }
    return pf;
}

static XFontStruct *
alternate_font(
    char *weight,
    char *slant)
{
    char *newname, *np, *op;
    int cnt;
    XFontStruct *fsp = NULL;
    if (cur_win->fontname == NULL
     || cur_win->fontname[0] != '-'
     || (newname = castalloc(char, (SIZE_T)strlen(cur_win->fontname)+32)) == NULL)
	return NULL;

    /* copy initial two fields */
    for (cnt=3, np=newname, op=cur_win->fontname; *op && cnt > 0; ) {
	if (*op == '-')
	    cnt--;
	*np++ = *op++;
    }
    if (!*op)
	goto done;

    /* substitute new weight and slant as appropriate */
#define SUBST_FIELD(field)				\
    do {						\
	if ((field) != NULL) {				\
	    char *fp = (field);				\
	    if (nocase_eq(*fp, *op))			\
		goto done;				\
	    while ((*np++ = *fp++))			\
		;					\
	    *(np-1) = '-';				\
	    while (*op && *op++ != '-')			\
		;					\
	}						\
	else {						\
	    while (*op && (*np++ = *op++) != '-')	\
		;					\
	}						\
	if (!*op)					\
	    goto done;					\
    } one_time

    SUBST_FIELD(weight);
    SUBST_FIELD(slant);
#undef SUBST_FIELD

    /* copy rest of name */
    while ((*np++ = *op++))
	;

    TRACE(("x11:alternate_font(weight=%s, slant=%s)\n -> %s\n",
	weight ? weight : "",
	slant  ? slant  : "", newname));

    if ((fsp = XLoadQueryFont(dpy, newname)) != NULL) {
	cur_win->left_ink = cur_win->left_ink || (fsp->min_bounds.lbearing < 0);
	cur_win->right_ink = cur_win->right_ink
		    || (fsp->max_bounds.rbearing > cur_win->char_width);
	TRACE(("...found left:%d, right:%d\n",
		cur_win->left_ink,
		cur_win->right_ink));
    }

done:
    free(newname);
    return fsp;

}

#if OPT_MENUS
int
x_menu_height(void)
{
    return cur_win->menu_height;
}
#endif /* OPT_MENUS */

#if OPT_MENUS_COLORED
int
x_menu_foreground(void)
{
    return cur_win->menubar_fg;
}
int
x_menu_background(void)
{
    return cur_win->menubar_bg;
}
#endif /* OPT_MENUS_COLORED */

int
x_setfont(
    const char  *fname)
{
    XFontStruct *pfont;
    Dimension   oldw;
    Dimension   oldh;

    if (cur_win) {
	oldw = x_width(cur_win);
	oldh = x_height(cur_win);
	if ((pfont = query_font(cur_win, fname)) != 0) {
	    XSetFont(dpy, cur_win->textgc, pfont->fid);
	    XSetFont(dpy, cur_win->reversegc, pfont->fid);
	    XSetFont(dpy, cur_win->selgc, pfont->fid);
	    XSetFont(dpy, cur_win->revselgc, pfont->fid);
	    XSetFont(dpy, cur_win->cursgc, pfont->fid);
	    XSetFont(dpy, cur_win->revcursgc, pfont->fid);
	    XSetFont(dpy, cur_win->modeline_focus_gc, pfont->fid);
	    XSetFont(dpy, cur_win->modeline_gc, pfont->fid);
	    if (cur_win->textgc != cur_win->revselgc) {
		XSetFont(dpy, cur_win->selgc, pfont->fid);
		XSetFont(dpy, cur_win->revselgc, pfont->fid);
	    }
	    reset_color_gcs();

	    /* if size changed, resize it, otherwise refresh */
	    if (oldw != x_width(cur_win) || oldh != x_height(cur_win)) {
		XtVaSetValues(cur_win->top_widget,
			XtNminHeight,	cur_win->base_height
					    + MINROWS*cur_win->char_height,
			XtNminWidth,	cur_win->base_width
					    + MINCOLS*cur_win->char_width,
			XtNheightInc,	cur_win->char_height,
			XtNwidthInc,	cur_win->char_width,
			NULL);
		update_scrollbar_sizes();
		XClearWindow(dpy, cur_win->win);
		x_touch(cur_win, 0, 0, cur_win->cols, cur_win->rows);
		XResizeWindow(dpy, XtWindow(cur_win->top_widget),
			      x_width(cur_win) + cur_win->base_width,
			      x_height(cur_win) + cur_win->base_height);

	    } else {
		XClearWindow(dpy, cur_win->win);
		x_touch(cur_win, 0, 0, cur_win->cols, cur_win->rows);
		x_flush();
	    }

	    return 1;
	}
	return 0;
    }
    return 1;
}

static void
x_open(void)
{
#if OPT_COLOR
    static const char *initpalettestr = "0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15";

    set_colors(NCOLORS);
    set_ctrans(initpalettestr);	/* should be set_palette() */
#endif

    kqinit(cur_win);
    cur_win->scrollbars = NULL;
    cur_win->maxscrollbars = 0;
#if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
    cur_win->scrollinfo = NULL;
    cur_win->grips = NULL;
#endif
#if OL_WIDGETS
    cur_win->sliders = NULL;
#endif

    /* main code assumes that it can access a cell at nrow x ncol */
    term.maxcols = term.cols = cur_win->cols;
    term.maxrows = term.rows = cur_win->rows;

    if (check_scrollbar_allocs() != TRUE)
	ExitProgram(BADEXIT);
}

static void
x_close(void)
{
    /* FIXME: Free pixmaps and GCs !!! */

    if(cur_win->top_widget) {
	XtDestroyWidget(cur_win->top_widget);
	cur_win->top_widget = 0;
	XtCloseDisplay(dpy);  /* need this if $xshell left subprocesses */
    }
}

static void
x_touch(
    TextWindow tw,
    int	 sc,
    int	 sr,
    UINT ec,
    UINT er)
{
    register UINT r;
    register UINT c;

    if (er > tw->rows)
	er = tw->rows;
    if (ec > tw->cols)
	ec = tw->cols;

    for (r = sr; r < er; r++) {
	MARK_LINE_DIRTY(r);
	for (c = sc; c < ec; c++)
	    if (CELL_TEXT(r,c) != ' ' || CELL_ATTR(r,c))
		MARK_CELL_DIRTY(r,c);
    }
}


static void
wait_for_scroll(
    TextWindow  tw)
{
    XEvent      ev;
    int		sc,
		sr;
    unsigned    ec,
		er;
    XGraphicsExposeEvent *gev;

    for_ever {		/* loop looking for a gfx expose or no expose */
	if (XCheckTypedEvent(dpy, NoExpose, &ev))
	    return;
	if (XCheckTypedEvent(dpy, GraphicsExpose, &ev)) {
	    gev = (XGraphicsExposeEvent *) & ev;
	    sc = gev->x / tw->char_width;
	    sr = gev->y / tw->char_height;
	    ec = CEIL(gev->x + gev->width,  tw->char_width);
	    er = CEIL(gev->y + gev->height, tw->char_height);
	    x_touch(tw, sc, sr, ec, er);
	    if (gev->count == 0)
		return;
	}
	XSync(dpy, False);
    }
}

static void
x_setpal(const char *thePalette)
{
	TRACE(("x_setpal(%s)\n", thePalette));
	set_ctrans(thePalette);
	x_touch(cur_win, 0, 0, cur_win->cols, cur_win->rows);
	x_flush();
}

static void
x_scroll(
    int from,
    int to,
    int count)
{
    if (cur_win->visibility == VisibilityFullyObscured)
	return;			/* Why bother? */

    if (from == to)
	return;			/* shouldn't happen */

    XCopyArea(dpy, cur_win->win, cur_win->win, cur_win->textgc,
	      x_pos(cur_win, 0), y_pos(cur_win, from),
	      x_width(cur_win), (unsigned)(count * cur_win->char_height),
	      x_pos(cur_win, 0), y_pos(cur_win, to));
    if (from < to)
	XClearArea(dpy, cur_win->win,
		x_pos(cur_win, 0), y_pos(cur_win,from),
		x_width(cur_win), (unsigned)((to-from) * cur_win->char_height),
		FALSE);
    else
	XClearArea(dpy, cur_win->win,
		x_pos(cur_win, 0), y_pos(cur_win,to+count),
		x_width(cur_win), (unsigned)((from-to) * cur_win->char_height),
		FALSE);
    if (cur_win->visibility == VisibilityPartiallyObscured) {
	XFlush(dpy);
	wait_for_scroll(cur_win);
    }
}

/*
 * The X protocol request for clearing a rectangle (PolyFillRectangle) takes
 * 20 bytes.  It will therefore be more expensive to switch from drawing text
 * to filling a rectangle unless the area to be cleared is bigger than 20
 * spaces.  Actually it is worse than this if we are going to switch
 * immediately to drawing text again since we incur a certain overhead
 * (16 bytes) for each string to be displayed.  This is how the value of
 * CLEAR_THRESH was computed (36 = 20+16).
 *
 * Kev's opinion:  If XDrawImageString is to be called, it is hardly ever
 * worth it to call XFillRectangle.  The only time where it will be a big
 * win is when the entire area to update is all spaces (in which case
 * XDrawImageString will not be called).  The following code would be much
 * cleaner, simpler, and easier to maintain if we were to just call
 * XDrawImageString where there are non-spaces to be written and
 * XFillRectangle when the entire region is to be cleared.
 */
#define	CLEAR_THRESH	36

static void
flush_line(
    char  *text,
    int	   len,
    unsigned int attr,
    int	   sr,
    int	   sc)
{
    GC	fore_gc;
    GC	back_gc;
    int	fore_yy = text_y_pos(cur_win, sr);
    int	back_yy = y_pos(cur_win, sr);
    char *p;
    int   cc, tlen, i, startcol;
    int   fontchanged = FALSE;

    if (attr == 0) {	/* This is the most common case, so we list it first */
	fore_gc = cur_win->textgc;
	back_gc = cur_win->reversegc;
    }
    else if ((attr & VACURS) && cur_win->is_color_cursor) {
	fore_gc = cur_win->cursgc;
	back_gc = cur_win->revcursgc;
	attr &= ~VACURS;
    }
    else if (attr & VASEL) {
	fore_gc = cur_win->selgc;
	back_gc = cur_win->revselgc;
    }
    else if (attr & VAMLFOC)
	fore_gc = back_gc = cur_win->modeline_focus_gc;
    else if (attr & VAML)
	fore_gc = back_gc = cur_win->modeline_gc;
    else if (attr & (VACOLOR)) {
	int fg = ctrans[VCOLORNUM(attr)];
	int bg = (gbcolor == ENUM_FCOLOR) ? fg : ctrans[gbcolor];
	fore_gc = get_color_gc(fg, True);
	back_gc = get_color_gc(bg, False);
    }
    else {
	fore_gc = cur_win->textgc;
	back_gc = cur_win->reversegc;
    }

    if (attr & (VAREV | VACURS)) {
	GC tmp_gc = fore_gc;
	fore_gc = back_gc;
	back_gc = tmp_gc;
    }

    if (attr & (VABOLD | VAITAL)) {
	XFontStruct *fsp = NULL;
	if ((attr & (VABOLD | VAITAL)) == (VABOLD | VAITAL)) {
	    if (!(cur_win->fsrch_flags & FSRCH_BOLDITAL)) {
		if ((fsp = alternate_font("bold","i")) != NULL
		 || (fsp = alternate_font("bold","o")) != NULL)
		    cur_win->pfont_boldital = fsp;
		cur_win->fsrch_flags |= FSRCH_BOLDITAL;
	    }
	    if (cur_win->pfont_boldital != NULL) {
		XSetFont(dpy, fore_gc, cur_win->pfont_boldital->fid);
		fontchanged = TRUE;
		attr &= ~(VABOLD | VAITAL);	/* don't use fallback */
	    }
	    else
		goto tryital;
	}
	else if (attr & VAITAL) {
tryital:
	    if (!(cur_win->fsrch_flags & FSRCH_ITAL)) {
		if ((fsp = alternate_font((char *)0,"i")) != NULL
		 || (fsp = alternate_font((char *)0,"o")) != NULL)
		    cur_win->pfont_ital = fsp;
		cur_win->fsrch_flags |= FSRCH_ITAL;
	    }
	    if (cur_win->pfont_ital != NULL) {
		XSetFont(dpy, fore_gc, cur_win->pfont_ital->fid);
		fontchanged = TRUE;
		attr &= ~VAITAL;		/* don't use fallback */
	    }
	    else if (attr & VABOLD)
		goto trybold;
	}
	else if (attr & VABOLD) {
trybold:
	    if (!(cur_win->fsrch_flags & FSRCH_BOLD)) {
		cur_win->pfont_bold = alternate_font("bold",NULL);
		cur_win->fsrch_flags |= FSRCH_BOLD;
	    }
	    if (cur_win->pfont_bold != NULL) {
		XSetFont(dpy, fore_gc, cur_win->pfont_bold->fid);
		fontchanged = TRUE;
		attr &= ~VABOLD;		/* don't use fallback */
	    }
	}
    }

    /* break line into TextStrings and FillRects */
    p = (char *)text;
    cc = 0;
    tlen = 0;
    startcol = sc;
    for (i = 0; i < len; i++) {
	if (text[i] == ' ') {
	    cc++;
	    tlen++;
	} else {
	    if (cc >= CLEAR_THRESH) {
		tlen -= cc;
		XDrawImageString(dpy, cur_win->win, fore_gc,
				 (int)x_pos(cur_win, sc), fore_yy,
				 p, tlen);
		if (attr & VABOLD)
		    XDrawString(dpy, cur_win->win, fore_gc,
				(int)x_pos(cur_win, sc)+1, fore_yy,
				 p, tlen);
		p += tlen + cc;
		sc += tlen;
		XFillRectangle(dpy, cur_win->win, back_gc,
			       x_pos(cur_win, sc), back_yy,
			       (unsigned)(cc * cur_win->char_width),
			       (unsigned)(cur_win->char_height));
		sc += cc;
		tlen = 1;	/* starting new run */
	    } else
		tlen++;
	    cc = 0;
	}
    }
    if (cc >= CLEAR_THRESH) {
	tlen -= cc;
	XDrawImageString(dpy, cur_win->win, fore_gc,
			 x_pos(cur_win, sc), fore_yy,
			 p, tlen);
	if (attr & VABOLD)
	    XDrawString(dpy, cur_win->win, fore_gc,
			(int)x_pos(cur_win, sc)+1, fore_yy,
			 p, tlen);
	sc += tlen;
	XFillRectangle(dpy, cur_win->win, back_gc,
		       x_pos(cur_win, sc), back_yy,
		       (unsigned)(cc * cur_win->char_width),
		       (unsigned)(cur_win->char_height));
    } else if (tlen > 0) {
	XDrawImageString(dpy, cur_win->win, fore_gc,
			 x_pos(cur_win, sc), fore_yy,
			 p, tlen);
	if (attr & VABOLD)
	    XDrawString(dpy, cur_win->win, fore_gc,
			(int)x_pos(cur_win, sc)+1, fore_yy,
			 p, tlen);
    }
    if (attr & (VAUL | VAITAL)) {
	fore_yy += cur_win->char_descent - 1;
	XDrawLine(dpy, cur_win->win, fore_gc,
		  x_pos(cur_win, startcol), fore_yy,
		  x_pos(cur_win, startcol + len) - 1, fore_yy);
    }

    if (fontchanged)
	XSetFont(dpy, fore_gc, cur_win->pfont->fid);
}


/* See above comment regarding CLEAR_THRESH */
#define NONDIRTY_THRESH 16

/* make sure the screen looks like we want it to */
static void
x_flush(void)
{
    int r, c, sc, ec, cleanlen;
    VIDEO_ATTR attr;

    if (cur_win->visibility == VisibilityFullyObscured || !cur_win->exposed)
	return;		/* Why bother? */

    /*
     * Write out cursor _before_ rest of the screen in order to avoid
     * flickering / winking effect noticable on some display servers.  This
     * means that the old cursor position (if different from the current
     * one) will be cleared after the new cursor is displayed.
     */

    if (ttrow >=0 && ttrow < term.rows && ttcol >= 0 && ttcol < term.cols
     && !cur_win->wipe_permitted) {
	CLEAR_CELL_DIRTY(ttrow, ttcol);
	display_cursor((XtPointer) 0, (XtIntervalId *) 0);
    }

    /* sometimes we're the last to know about resizing...*/
    if ((int) cur_win->rows > term.maxrows)
	cur_win->rows = term.maxrows;

    for (r = 0; r < (int) cur_win->rows; r++) {
	if (!IS_DIRTY_LINE(r))
	    continue;
	if (r !=  ttrow)
	    CLEAR_LINE_DIRTY(r);

	/*
	 * The following code will cause monospaced fonts with ink outside
	 * the bounding box to be cleaned up.
	 */
	if (cur_win->left_ink || cur_win->right_ink)
	    for (c=0; c < term.cols; ) {
		while (c < term.cols && !IS_DIRTY(r,c))
		    c++;
		if (c >= term.cols)
		    break;
		if (cur_win->left_ink && c > 0)
		    MARK_CELL_DIRTY(r,c-1);
		while (c < term.cols && IS_DIRTY(r,c))
		    c++;
		if (cur_win->right_ink && c < term.cols) {
		    MARK_CELL_DIRTY(r,c);
		    c++;
		}
	    }

	c = 0;
	while (c < term.cols) {
	    /* Find the beginning of the next dirty sequence */
	    while (c < term.cols && !IS_DIRTY(r,c))
		c++;
	    if (c >= term.cols)
		break;
	    if (r == ttrow && c == ttcol && !cur_win->wipe_permitted) {
		c++;
		continue;
	    }
	    CLEAR_CELL_DIRTY(r,c);
	    sc = ec = c;
	    attr = VATTRIB(CELL_ATTR(r,c));
	    cleanlen = NONDIRTY_THRESH;
	    c++;
	    /*
	     * Scan until we find the end of line, a cell with a different
	     * attribute, a sequence of NONDIRTY_THRESH non-dirty chars, or
	     * the cursor position.
	     */
	    while (c < term.cols) {
		if (attr != VATTRIB(CELL_ATTR(r,c)))
		    break;
		else if (r == ttrow && c == ttcol && !cur_win->wipe_permitted) {
		    c++;
		    break;
		}
		else if (IS_DIRTY(r,c)) {
		    ec = c;
		    cleanlen = NONDIRTY_THRESH;
		    CLEAR_CELL_DIRTY(r,c);
		}
		else if (--cleanlen <= 0)
		    break;
		c++;
	    }
	    /* write out the portion from sc thru ec */
	    flush_line(&CELL_TEXT(r,sc), ec-sc+1,
		       (unsigned int) VATTRIB(CELL_ATTR(r,sc)), r, sc);
	}
    }
    XFlush(dpy);
}


/* selection processing stuff */

/* multi-click code stolen from xterm */
/*
 * double click table for cut and paste in 8 bits
 *
 * This table is divided in four parts :
 *
 *	- control characters	[0,0x1f] U [0x80,0x9f]
 *	- separators		[0x20,0x3f] U [0xa0,0xb9]
 *	- binding characters	[0x40,0x7f] U [0xc0,0xff]
 *	- exceptions
 */
static int  charClass[256] = {
/* NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL */
    32, 1, 1, 1, 1, 1, 1, 1,
/*  BS   HT   NL   VT   NP   CR   SO   SI */
    1, 32, 1, 1, 1, 1, 1, 1,
/* DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB */
    1, 1, 1, 1, 1, 1, 1, 1,
/* CAN   EM  SUB  ESC   FS   GS   RS   US */
    1, 1, 1, 1, 1, 1, 1, 1,
/*  SP    !    "    #    $    %    &    ' */
    32, 33, 34, 35, 36, 37, 38, 39,
/*   (    )    *    +    ,    -    .    / */
    40, 41, 42, 43, 44, 45, 46, 47,
/*   0    1    2    3    4    5    6    7 */
    48, 48, 48, 48, 48, 48, 48, 48,
/*   8    9    :    ;    <    =    >    ? */
    48, 48, 58, 59, 60, 61, 62, 63,
/*   @    A    B    C    D    E    F    G */
    64, 48, 48, 48, 48, 48, 48, 48,
/*   H    I    J    K    L    M    N    O */
    48, 48, 48, 48, 48, 48, 48, 48,
/*   P    Q    R    S    T    U    V    W */
    48, 48, 48, 48, 48, 48, 48, 48,
/*   X    Y    Z    [    \    ]    ^    _ */
    48, 48, 48, 91, 92, 93, 94, 48,
/*   `    a    b    c    d    e    f    g */
    96, 48, 48, 48, 48, 48, 48, 48,
/*   h    i    j    k    l    m    n    o */
    48, 48, 48, 48, 48, 48, 48, 48,
/*   p    q    r    s    t    u    v    w */
    48, 48, 48, 48, 48, 48, 48, 48,
/*   x    y    z    {    |    }    ~  DEL */
    48, 48, 48, 123, 124, 125, 126, 1,
/* x80  x81  x82  x83  IND  NEL  SSA  ESA */
    1, 1, 1, 1, 1, 1, 1, 1,
/* HTS  HTJ  VTS  PLD  PLU   RI  SS2  SS3 */
    1, 1, 1, 1, 1, 1, 1, 1,
/* DCS  PU1  PU2  STS  CCH   MW  SPA  EPA */
    1, 1, 1, 1, 1, 1, 1, 1,
/* x98  x99  x9A  CSI   ST  OSC   PM  APC */
    1, 1, 1, 1, 1, 1, 1, 1,
/*   -    i   c/    L   ox   Y-    |   So */
    160, 161, 162, 163, 164, 165, 166, 167,
/*  ..   c0   ip   <<    _        R0    - */
    168, 169, 170, 171, 172, 173, 174, 175,
/*   o   +-    2    3    '    u   q|    . */
    176, 177, 178, 179, 180, 181, 182, 183,
/*   ,    1    2   >>  1/4  1/2  3/4    ? */
    184, 185, 186, 187, 188, 189, 190, 191,
/*  A`   A'   A^   A~   A:   Ao   AE   C, */
    48, 48, 48, 48, 48, 48, 48, 48,
/*  E`   E'   E^   E:   I`   I'   I^   I: */
    48, 48, 48, 48, 48, 48, 48, 48,
/*  D-   N~   O`   O'   O^   O~   O:    X */
    48, 48, 48, 48, 48, 48, 48, 216,
/*  O/   U`   U'   U^   U:   Y'    P    B */
    48, 48, 48, 48, 48, 48, 48, 48,
/*  a`   a'   a^   a~   a:   ao   ae   c, */
    48, 48, 48, 48, 48, 48, 48, 48,
/*  e`   e'   e^   e:    i`  i'   i^   i: */
    48, 48, 48, 48, 48, 48, 48, 48,
/*   d   n~   o`   o'   o^   o~   o:   -: */
    48, 48, 48, 48, 48, 48, 48, 248,
/*  o/   u`   u'   u^   u:   y'    P   y: */
48, 48, 48, 48, 48, 48, 48, 48};

static int
set_character_class_range(
    register int low,
    register int high,		/* in range of [0..255] */
    register int value)		/* arbitrary */
{

    if (low < 0 || high > 255 || high < low)
	return (-1);

    for (; low <= high; low++)
	charClass[low] = value;

    return (0);
}


/*
 * set_character_class - takes a string of the form
 *
 *                 low[-high]:val[,low[-high]:val[...]]
 *
 * and sets the indicated ranges to the indicated values.
 */

static int
set_character_class(register char *s)
{
    register int i;		/* iterator, index into s */
    int		len;		/* length of s */
    int		acc;		/* accumulator */
    int		low,
		high;		/* bounds of range [0..127] */
    int		base;		/* 8, 10, 16 (octal, decimal, hex) */
    int		numbers;	/* count of numbers per range */
    int		digits;		/* count of digits in a number */
    static char *errfmt = "xvile:  %s in range string \"%s\" (position %d)\n";

    if (!s || !s[0])
	return -1;

    base = 10;			/* in case we ever add octal, hex */
    low = high = -1;		/* out of range */

    for (i = 0, len = strlen(s), acc = 0, numbers = digits = 0;
	    i < len; i++) {
	int	   c = s[i];

	if (isSpace(c)) {
	    continue;
	} else if (isDigit(c)) {
	    acc = acc * base + (c - '0');
	    digits++;
	    continue;
	} else if (c == '-') {
	    low = acc;
	    acc = 0;
	    if (digits == 0) {
		(void)fprintf(stderr, errfmt, "missing number", s, i);
		return (-1);
	    }
	    digits = 0;
	    numbers++;
	    continue;
	} else if (c == ':') {
	    if (numbers == 0)
		low = acc;
	    else if (numbers == 1)
		high = acc;
	    else {
		(void)fprintf(stderr, errfmt, "too many numbers",
			s, i);
		return (-1);
	    }
	    digits = 0;
	    numbers++;
	    acc = 0;
	    continue;
	} else if (c == ',') {
	    /*
	     * now, process it
	     */

	    if (high < 0) {
		high = low;
		numbers++;
	    }
	    if (numbers != 2) {
		(void)fprintf(stderr, errfmt, "bad value number",
			s, i);
	    } else if (set_character_class_range(low, high, acc) != 0) {
		(void)fprintf(stderr, errfmt, "bad range", s, i);
	    }
	    low = high = -1;
	    acc = 0;
	    digits = 0;
	    numbers = 0;
	    continue;
	} else {
	    (void)fprintf(stderr, errfmt, "bad character", s, i);
	    return (-1);
	}			/* end if else if ... else */

    }

    if (low < 0 && high < 0)
	return (0);

    /*
     * now, process it
     */

    if (high < 0)
	high = low;
    if (numbers < 1 || numbers > 2) {
	(void)fprintf(stderr, errfmt, "bad value number", s, i);
    } else if (set_character_class_range(low, high, acc) != 0) {
	(void)fprintf(stderr, errfmt, "bad range", s, i);
    }
    return (0);
}

/*
 * Copy a single character into the paste-buffer, quoting it if necessary
 */
static int
add2paste(
TBUFF	**p,
int	c)
{
	if (c == '\n' || isBlank(c))
		/*EMPTY*/;
	else if (isSpecial(c) || (c == '\r') || !isPrint(c))
		(void)tb_append(p, quotec);
	return (tb_append(p, c) != 0);
}

/*
 * Copy the selection into the PasteBuf buffer.  If we are pasting into a
 * window, check to see if:
 *
 *	+ the window's buffer is modifiable (if not, don't waste time copying
 *	  text!)
 *	+ the buffer uses 'autoindent' mode (if so, do some heuristics
 *	  for placement of the pasted text -- we may put it on lines by
 *	  itself, above or below the current line)
 */
#define OLD_PASTE 0

static int
copy_paste(
TBUFF	**p,
char	*value,
SIZE_T	length)
{
	WINDOW	*wp = row2window(ttrow);
	BUFFER	*bp = (wp != 0) ? wp->w_bufp : 0;
	int	status;

	if (bp != 0 && b_val(bp,MDVIEW))
		return FALSE;

	status = TRUE;

	if (bp != 0 && (b_val(bp,MDCINDENT) || b_val(bp,MDAIND))) {

#if OLD_PASTE
		/*
		 * If the cursor points before the first nonwhite on
		 * the line, convert the insert into an 'O' command.
		 * If it points to the end of the line, convert it into
		 * an 'o' command.  Otherwise (if it is within the
		 * nonwhite portion of the line), assume the user knows
		 * what (s)he is doing.
		 */
#endif
		if (setwmark(ttrow, ttcol)) {	/* MK gets cursor */
#if OLD_PASTE
			LINE	*lp	= MK.l;
			int	first   = firstchar(lp);
			int	last    = lastchar(lp);
			CMDFUNC	*f = 0;

			/* If the line contains only a single nonwhite,
			 * we will insert before it.
			 */
			if (first >= MK.o)
				f = &f_openup_no_aindent;
			else if (last <= MK.o)
				f = &f_opendown_no_aindent;
			if (insertmode) {
				if ((*value != '\n') && MK.o == 0)
					(void)tb_append(p, '\n');
			}
			else if (f) {
				char *pstr;
				/* we're _replacing_ the default
					insertion command, so reinit */
				tb_init(p, esc_c);
				pstr = fnc2pstr(f);
				tb_bappend(p, pstr + 1, (ALLOC_T) *pstr);
			}
#endif
		}
	}

	while (length-- > 0) {
		if (!add2paste(p, *value++)) {
			status = FALSE;
			break;
		}
	}

	return status;
}

/* ARGSUSED */
static void
x_get_selection(
    Widget	   w GCC_UNUSED,
    XtPointer      cldat GCC_UNUSED,
    Atom	  *selection,
    Atom	  *type,
    XtPointer      value,
    unsigned long *length,
    int		  *format)
{
    int	do_ins;

    if (*format != 8 || *type != XA_STRING) {
	x_beep();			/* can't handle incoming data */
	return;
    }

    if (length != 0 && value != NULL) {
	char *s = NULL;			/* stifle warning */
	/* should be impossible to hit this with existing paste */
	/* XXX massive hack -- leave out 'i' if in prompt line */
	do_ins = !insertmode
		&& (!onMsgRow(cur_win) || *selection == atom_CLIPBOARD)
		&& ((s = fnc2pstr(&f_insert_no_aindent)) != NULL);

	if (tb_init(&PasteBuf, esc_c)) {
		if ((do_ins && !tb_bappend(&PasteBuf, s+1, (ALLOC_T)*s))
		 || !copy_paste(&PasteBuf, (char *)value, (SIZE_T) *length)
		 || (do_ins && !tb_append(&PasteBuf, esc_c)))
			tb_free(&PasteBuf);
	}
	XtFree((char *)value);
    }
}

static void
x_paste_selection(Atom selection)
{
    if (cur_win->have_selection && selection == XA_PRIMARY) {
	/* local transfer */
	UCHAR  *data;
	SIZE_T len_st;
	unsigned long len_ul;

	Atom	type      = XA_STRING;
	int	format    = 8;

	if (!x_get_selected_text(&data, &len_st)) {
	    x_beep();
	    return;
	}
	len_ul = (unsigned long) len_st;	/* Ugh. */
	x_get_selection(cur_win->top_widget, NULL, &selection, &type,
			(XtPointer) data, &len_ul, &format);
    }
    else {
	XtGetSelectionValue(
	    cur_win->top_widget,
	    selection,
	    XA_STRING,
	    x_get_selection,
	    (XtPointer)0,		/* client data */
	    XtLastTimestampProcessed(dpy));
    }
}

static Boolean
x_get_selected_text(
    UCHAR **datp,
    SIZE_T *lenp)
{
    UCHAR	*data = 0;
    UCHAR	*dp = 0;
    SIZE_T	length;
    KILL	*kp;		/* pointer into kill register */

    /* FIXME: Can't select message line */

    if (!cur_win->have_selection)
	return False;

    sel_yank(SEL_KREG);
    for (length = 0, kp = kbs[SEL_KREG].kbufh; kp; kp = kp->d_next)
	length += KbSize(SEL_KREG, kp);
    if ( length == 0
      || (dp = data = (UCHAR *) XtMalloc(length * sizeof(UCHAR))) == 0
      || (kp = kbs[SEL_KREG].kbufh) == 0)
	return False;

    while (kp != NULL) {
	SIZE_T len = KbSize(SEL_KREG,kp);
	(void)memcpy((char *)dp, (char *)kp->d_chunk, len);
	kp = kp->d_next;
	dp += len;
    }

    *lenp = length;
    *datp = data;
    return True;
}

static Boolean
x_get_clipboard_text(
    UCHAR **datp,
    SIZE_T *lenp)
{
    UCHAR	*data = 0;
    UCHAR	*dp = 0;
    SIZE_T	length;
    KILL	*kp;		/* pointer into kill register */

    for (length = 0, kp = kbs[CLIP_KREG].kbufh; kp; kp = kp->d_next)
	length += KbSize(CLIP_KREG, kp);
    if ( length == 0
      || (dp = data = (UCHAR *) XtMalloc(length * sizeof(UCHAR))) == 0
      || (kp = kbs[CLIP_KREG].kbufh) == 0)
	return False;

    while (kp != NULL) {
	SIZE_T len = KbSize(CLIP_KREG,kp);
	(void)memcpy((char *)dp, (char *)kp->d_chunk, len);
	kp = kp->d_next;
	dp += len;
    }

    *lenp = length;
    *datp = data;
    return True;
}

/* ARGSUSED */
static Boolean
x_convert_selection(
    Widget	   w GCC_UNUSED,
    Atom	  *selection,
    Atom	  *target,
    Atom	  *type,
    XtPointer     *value,
    unsigned long *length,
    int		  *format)
{
    if (!cur_win->have_selection && *selection == XA_PRIMARY)
	return False;

    /*
     * The ICCCM requires us to handle the following targets: TARGETS,
     * MULTIPLE, and TIMESTAMP.  MULTIPLE and TIMESTAMP are handled by
     * the Xt intrinsics.  Below, we handle TARGETS, STRING, and TEXT.
     * The STRING and TEXT targets are what xvile uses to transfer
     * selected text to another client.  TARGETS is simply a list of
     * the targets we support (including the ones handled by the Xt
     * intrinsics).
     */

    if (*target == atom_TARGETS) {
	Atom *tp;

#define NTARGS 5

	*(Atom **)value = tp = (Atom *)XtMalloc(NTARGS * sizeof(Atom));

	if (tp == NULL)
	    return False;	/* should not happen (even if out of memory) */

	*tp++ = atom_TARGETS;
	*tp++ = atom_MULTIPLE;
	*tp++ = atom_TIMESTAMP;
	*tp++ = XA_STRING;
	*tp++ = atom_TEXT;

	*type   = XA_ATOM;
	*length = tp - *(Atom **)value;
	*format = 32;		/* width of the data being transfered */
	return True;
    }
    else if (*target == XA_STRING || *target == atom_TEXT) {
	*type   = XA_STRING;
	*format = 8;
	if (*selection == XA_PRIMARY)
	    return x_get_selected_text((UCHAR **)value, (SIZE_T *)length);
	else	/* CLIPBOARD */
	    return x_get_clipboard_text((UCHAR **)value, (SIZE_T *)length);
    }

    return False;
}

/* ARGSUSED */
static void
x_lose_selection(
    Widget w GCC_UNUSED,
    Atom  *selection)
{
    if (*selection == XA_PRIMARY) {
	cur_win->have_selection = False;
	cur_win->was_on_msgline = False;
	sel_release();
	(void) update(TRUE);
    }
    else {
	/* Free up the data in the kill buffer (how do we do this?) */
    }
}

void own_selection(void)
{
    x_own_selection(XA_PRIMARY);
}

static void
x_own_selection(Atom selection)
{
    /*
     * Note:  we've been told that the Hummingbird X Server (which runs on a
     * PC) updates the contents of the clipboard only if we remove the next
     * line, causing this program to assert the selection on each call.  We
     * don't do that, however, since it would violate the sense of the ICCCM,
     * which is minimizing network traffic.
     *
     * Kev's note on the above comment (which I assume was written by Tom):
     * I've added some new code for dealing with clipboards in now.  It
     * may well be that the clipboard will work properly now.  Of course,
     * you'll need to run the copy-to-clipboard command from vile.  If
     * you're on a Sun keyboard, you might want to bind this to the Copy
     * key (F16).  I may also think about doing a sort of timer mechanism
     * which asserts ownership of the clipboard if a certain amount of
     * time has gone by with no activity.
     */
    if (!cur_win->have_selection || selection != XA_PRIMARY)
	cur_win->have_selection =
	    XtOwnSelection(
		cur_win->top_widget,
		selection,
		XtLastTimestampProcessed(dpy),
		x_convert_selection,
		x_lose_selection,
		(XtSelectionDoneProc)0);
}

static void
scroll_selection(
    XtPointer rowcol,
    XtIntervalId *idp)
{
    int row, col;
    if (*idp == cur_win->sel_scroll_id)
	XtRemoveTimeOut(cur_win->sel_scroll_id);	/* shouldn't happen */
    cur_win->sel_scroll_id = (XtIntervalId) 0;

    row = (((long) rowcol) >> 16) & 0xffff;
    col = ((long) rowcol) & 0xffff;
    if (row & 0x8000)
	row |= -1 << 16;
    if (col & 0x8000)
	col |= -1 << 16;
    extend_selection(cur_win, row, col, TRUE);
}

static int
line_count_and_interval(
    long scroll_count,
    unsigned long *ip)
{
    scroll_count = scroll_count / 4 - 2;
    if (scroll_count <= 0) {
	*ip = (1 - scroll_count) * cur_win->scroll_repeat_interval;
	return 1;
    }
    else {
	/*
	 * FIXME: figure out a cleaner way to do this or something like it...
	 */
	if (scroll_count > 450)
	    scroll_count *= 1024;
	else if (scroll_count > 350)
	    scroll_count *= 128;
	else if (scroll_count > 275)
	    scroll_count *= 64;
	else if (scroll_count > 200)
	    scroll_count *= 16;
	else if (scroll_count > 150)
	    scroll_count *= 8;
	else if (scroll_count > 100)
	    scroll_count *= 4;
	else if (scroll_count > 75)
	    scroll_count *= 3;
	else if (scroll_count > 50)
	    scroll_count *= 2;
	*ip = cur_win->scroll_repeat_interval;
	return scroll_count;
    }
}

static void
extend_selection(
    TextWindow tw GCC_UNUSED,
    int	nr,
    int	nc,
    Bool wipe)
{
    static long scroll_count = 0;
    long rowcol = 0;
    unsigned long interval = 0;

    if (cur_win->sel_scroll_id != (XtIntervalId) 0) {
	if (nr < curwp->w_toprow || nr >= mode_row(curwp))
	    return;		/* Only let timer extend selection */
	XtRemoveTimeOut(cur_win->sel_scroll_id);
	cur_win->sel_scroll_id = (XtIntervalId) 0;
    }

    if (nr < curwp->w_toprow) {
	if (wipe) {
	    mvupwind(TRUE, line_count_and_interval(scroll_count++, &interval));
	    rowcol = (nr << 16) | (nc & 0xffff);
	}
	else {
	    scroll_count = 0;
	}
	nr = curwp->w_toprow;
    }
    else if (nr >= mode_row(curwp)) {
	if (wipe) {
	    mvdnwind(TRUE, line_count_and_interval(scroll_count++, &interval));
	    rowcol = (nr << 16) | (nc & 0xffff);
	}
	else {
	    scroll_count = 0;
	}
	nr = mode_row(curwp) - 1;
    }
    else {
	scroll_count = 0;
    }
    if (setcursor(nr,nc) && sel_extend(wipe,TRUE)) {
	cur_win->did_select = True;
	(void)update(TRUE);
	if (scroll_count > 0) {
	    x_flush();
	    cur_win->sel_scroll_id = XtAppAddTimeOut(cur_win->app_context,
						     interval,
						     scroll_selection,
						     (XtPointer) rowcol);
	}
    }
    else
	x_beep();
}

static void
multi_click(
	TextWindow  tw,
	int	    nr,
	int	    nc)
{
    UCHAR	*p;
    int	cclass;
    int	sc = nc;
    int oc = nc;
    WINDOW	*wp;

    tw->numclicks++;

    if ((wp = row2window(nr)) != 0 && nr == mode_row(wp)) {
	set_curwp(wp);
	sel_release();
	(void)update(TRUE);
    }
    else {
	switch (tw->numclicks) {
	case 0:
	case 1:			/* shouldn't happen */
		mlforce("BUG: 0 or 1 multiclick value.");
		return;
	case 2:			/* word */
#if OPT_HYPERTEXT
		if (setcursor(nr,nc) && exechypercmd(0,0)) {
		    (void) update(TRUE);
		    return;
		}
#endif
		/* find word start */
		p = (UCHAR *)(&CELL_TEXT(nr,sc));
		cclass = charClass[*p];
		do {
			--sc;
			--p;
		} while (sc >= 0 && charClass[*p] == cclass);
		sc++;
		/* and end */
		p = (UCHAR *)(&CELL_TEXT(nr,nc));
		cclass = charClass[*p];
		do {
			++nc;
			++p;
		} while (nc < (int) tw->cols && charClass[*p] == cclass);
		--nc;

		if (setcursor(nr,sc)) {
			(void)sel_begin();
			extend_selection(tw, nr, nc, FALSE);
			(void) setcursor(nr,oc);
			/* FIXME: Too many updates */
			(void) update(TRUE);
		}
		return;
	case 3:			/* line (doesn't include trailing newline) */
		if (setcursor(nr,sc)) {
			MARK saveDOT;
			saveDOT = DOT;
			(void) gotobol(0, 0);
			(void) sel_begin();
			(void) gotoeol(FALSE, 0);
			(void) sel_extend(FALSE,TRUE);
			DOT = saveDOT;
			cur_win->did_select = True;
			(void) update(TRUE);
		}
		return;
	case 4:			/* document (doesn't include trailing newline) */
		if (setcursor(nr,sc)) {
			MARK saveDOT;
			saveDOT = DOT;
			(void) gotobob(0, 0);
			(void) sel_begin();
			(void) gotoeob(FALSE, 0);
			(void) gotoeol(FALSE, 0);
			(void) sel_extend(FALSE,TRUE);
			DOT = saveDOT;
			cur_win->did_select = True;
			(void) update(TRUE);
		}
		return;
	default:
		/*
		 * This provides a mechanism for getting rid of the
		 * selection.
		 */
		sel_release();
		(void)update(TRUE);
		return;
	}
    }
}

static void
start_selection(
    TextWindow  tw,
    XButtonPressedEvent *ev,
    int	nr,
    int	nc)
{
    tw->wipe_permitted = FALSE;
    if ((tw->lasttime != 0)
     && (absol(ev->time - tw->lasttime) < tw->click_timeout)) {
	/* FIXME: This code used to ignore multiple clicks which
	 *	  spanned rows.  Do we still want this behavior?
	 *	  If so, we'll have to (re)implement it.
	 */
	multi_click(tw, nr, nc);
    }
    else {
	WINDOW *wp;

	beginDisplay();

	tw->lasttime = ev->time;
	tw->numclicks = 1;
	tw->was_on_msgline = onMsgRow(tw);

	if ((wp = row2window(nr)) != 0) {
	    set_curwp(wp);
	}
	tw->prevDOT = DOT;

	/*
	 * If we're on the message line, do nothing.
	 *
	 * If we're on a mode line, make the window whose mode line we're
	 * on the current window.
	 *
	 * Otherwise update the cursor position in whatever window we're
	 * in and set things up so that the current position can be the
	 * possible start of a selection.
	 */
	if (reading_msg_line) {
	    /*EMPTY*/;	/* ignore */
	}
	else if (wp != 0 && nr == mode_row(wp)) {
	    (void)update(TRUE);
	}
	else if (setcursor(nr, nc)) {
	    if (!cur_win->persistent_selections) {
		sel_yank(SEL_KREG);
		sel_release();
	    }
	    (void) sel_begin();
	    (void)update(TRUE);
	    tw->wipe_permitted = TRUE;
	    /* force the editor to notice the changed DOT, if it cares */
	    kqadd(cur_win, KEY_Mouse);
	}
	endofDisplay();
    }
}

/* this doesn't need to do anything.  it's invoked when we do
	shove KEY_Mouse back on the input stream, to force the
	main editor code to notice that DOT has moved. */
/*ARGSUSED*/
int
mouse_motion(int f GCC_UNUSED, int n GCC_UNUSED)
{
	return TRUE;
}

/*ARGSUSED*/
int
copy_to_clipboard(int f GCC_UNUSED, int n GCC_UNUSED)
{
    if (!cur_win->have_selection) {
	x_beep();
	return FALSE;
    }

    sel_yank(CLIP_KREG);
    x_own_selection(atom_CLIPBOARD);

    return TRUE;
}

/*ARGSUSED*/
int
paste_from_clipboard(int f GCC_UNUSED, int n GCC_UNUSED)
{
    x_paste_selection(atom_CLIPBOARD);
    return TRUE;
}

/*ARGSUSED*/
int
paste_from_primary(int f GCC_UNUSED, int n GCC_UNUSED)
{
    x_paste_selection(XA_PRIMARY);
    return TRUE;
}

static XMotionEvent *
compress_motion(
    XMotionEvent *ev)
{
    XEvent      nev;

    while (XPending(ev->display)) {
	XPeekEvent(ev->display, &nev);
	if (nev.type == MotionNotify &&
		nev.xmotion.window == ev->window &&
		nev.xmotion.subwindow == ev->subwindow) {
	    XNextEvent(ev->display, (XEvent *) ev);
	} else
	    break;
    }
    return ev;
}

/*
 * handle non keyboard events associated with vile screen
 */
/*ARGSUSED*/
static void
x_process_event(
    Widget	w GCC_UNUSED,
    XtPointer	unused GCC_UNUSED,
    XEvent     *ev,
    Boolean    *continue_to_dispatch GCC_UNUSED)
{
    int		sc,
		sr;
    unsigned    ec,
		er;

    int		nr,
		nc;
    static int onr = -1, onc = -1;

    XMotionEvent *mev;
    XExposeEvent *gev;
    Bool	do_sel;
    WINDOW	*wp;
    Bool	ignore = vile_is_busy;

    switch (ev->type) {
    case Expose:
	gev = (XExposeEvent *)ev;
	sc = gev->x / cur_win->char_width;
	sr = gev->y / cur_win->char_height;
	ec = CEIL(gev->x + gev->width,  cur_win->char_width);
	er = CEIL(gev->y + gev->height, cur_win->char_height);
	x_touch(cur_win, sc, sr, ec, er);
	cur_win->exposed = TRUE;
	if (ev->xexpose.count == 0)
		x_flush();
	break;

    case VisibilityNotify:
	cur_win->visibility = ev->xvisibility.state;
	XSetGraphicsExposures(dpy, cur_win->textgc,
			      cur_win->visibility != VisibilityUnobscured);
	break;

    case MotionNotify:
	if (ignore)
	    break;
	do_sel = cur_win->wipe_permitted;
	if (!(ev->xmotion.state & (Button1Mask | Button3Mask))) {
	    if (!cur_win->focus_follows_mouse)
		return;
	    else
		do_sel = FALSE;
	}
	mev = compress_motion((XMotionEvent *) ev);
	nc = mev->x / cur_win->char_width;
	nr = mev->y / cur_win->char_height;

	if (nr < 0)
	    nr = -1;	/* want to be out of bounds to force scrolling */
	else if (nr > (int) cur_win->rows)
	    nr = cur_win->rows;

	if (nc < 0)
	    nc = 0;
	else if (nc >= (int) cur_win->cols)
	    nc = cur_win->cols-1;

	/* ignore any spurious motion during a multi-cick */
	if (cur_win->numclicks > 1
	 && cur_win->lasttime != 0
	 && (absol(ev->xmotion.time - cur_win->lasttime) < cur_win->click_timeout))
	    return;
	if (do_sel) {
	    if (ev->xbutton.state & ControlMask) {
		(void)sel_setshape(RECTANGLE);
	    }
	    if (nr != onr || nc != onc)
		extend_selection(cur_win, nr, nc, True);
	    onr = nr;
	    onc = nc;
	}
	else {
	    if (!reading_msg_line && (wp = row2window(nr)) && wp != curwp) {
		(void) set_curwp(wp);
		(void) update(TRUE);
	    }
	}
	break;
    case ButtonPress:
	if (ignore)
	    break;
	nc = ev->xbutton.x / cur_win->char_width;
	nr = ev->xbutton.y / cur_win->char_height;
	TRACE(("ButtonPress #%d (%d,%d)\n", ev->xbutton.button, nr, nc));
	switch (ev->xbutton.button) {
	case Button1:		/* move button and set selection point */
	    start_selection(cur_win, (XButtonPressedEvent *) ev, nr, nc);
	    onr = nr;
	    onc = nc;
	    break;
	case Button2:		/* paste selection */
	    /*
	     * If shifted, paste at mouse.  Otherwise, paste at the last
	     * position marked before beginning a selection.
	     */
	    if (ev->xbutton.state & ShiftMask) {
		if (!setcursor(nr, nc)) {
		    kbd_alarm();	/* don't know how to paste here */
		    break;
		}
	    }
	    x_paste_selection(XA_PRIMARY);
	    break;
	case Button3:		/* end/extend selection */
	    if (((wp = row2window(nr)) != 0) && sel_buffer() == wp->w_bufp)
		(void) set_curwp(wp);
	    if (ev->xbutton.state & ControlMask)
		(void)sel_setshape(RECTANGLE);
	    cur_win->wipe_permitted = True;
	    cur_win->prevDOT = DOT;
	    extend_selection(cur_win, nr, nc, False);
	    break;
	case Button4:
	    mvupwind(TRUE, cur_win->wheel_scroll_amount);
	    (void)update(TRUE);
	    break;
	case Button5:
	    mvdnwind(TRUE, cur_win->wheel_scroll_amount);
	    (void)update(TRUE);
	    break;
	}
	break;
    case ButtonRelease:
	if (ignore)
	    break;
	TRACE(("ButtonRelease #%d (%d,%d)%s\n",
		ev->xbutton.button,
		ev->xbutton.y / cur_win->char_height,
		ev->xbutton.x / cur_win->char_width,
		cur_win->did_select ? ": did_select" : ""));
	switch (ev->xbutton.button) {
	case Button1:
	    if (cur_win->persistent_selections)
		sel_yank(SEL_KREG);

	    /* FALLTHRU */
	case Button3:
	    if (cur_win->sel_scroll_id != ((XtIntervalId) 0)) {
		XtRemoveTimeOut(cur_win->sel_scroll_id);
		cur_win->sel_scroll_id = (XtIntervalId) 0;
	    }
	    if (cur_win->did_select && !cur_win->selection_sets_DOT) {
		restore_dot(cur_win->prevDOT);
		(void)update(TRUE);
	    }
	    cur_win->did_select = False;
	    cur_win->wipe_permitted = False;
	    display_cursor((XtPointer) 0, (XtIntervalId *) 0);
	    break;
	}
	break;
    }
}

/*ARGSUSED*/
static void
x_configure_window(
    Widget	w GCC_UNUSED,
    XtPointer	unused GCC_UNUSED,
    XEvent     *ev,
    Boolean    *continue_to_dispatch GCC_UNUSED)
{
    int nr, nc;
    Dimension new_width, new_height;

    if (ev->type != ConfigureNotify)
	return;

    if (ev->xconfigure.height == cur_win->top_height
     && ev->xconfigure.width == cur_win->top_width)
	return;

    XtVaGetValues(cur_win->top_widget,
	    XtNheight,	&new_height,
	    XtNwidth,	&new_width,
	    NULL);
    new_height = ((new_height - cur_win->base_height) / cur_win->char_height)
			     * cur_win->char_height;
    new_width = ((new_width - cur_win->base_width) /
		    cur_win->char_width) * cur_win->char_width;

    /* Check to make sure the dimensions are sane both here and below
       to avoid BadMatch errors */
    nr = (int)(new_height / cur_win->char_height);
    nc = (int)(new_width  / cur_win->char_width);

    if (nr < MINROWS || nc < MINCOLS) {
	gui_resize(nc, nr);
	/* Calling XResizeWindow will cause another ConfigureNotify
	 * event, so we should return early and let this event occur.
	 */
	return;
    }

#if MOTIF_WIDGETS
    XtVaSetValues(cur_win->form_widget,
	    XmNresizePolicy,	XmRESIZE_NONE,
	    NULL);
    {
	WidgetList children;
	Cardinal nchildren;
	XtVaGetValues(cur_win->form_widget,
		XmNchildren, &children,
		XmNnumChildren, &nchildren,
		NULL);
	XtUnmanageChildren(children, nchildren);
    }
#else
#if NO_WIDGETS || ATHENA_WIDGETS
    XtVaSetValues(cur_win->form_widget,
	    XtNwidth,		new_width + cur_win->pane_width + 2,
	    XtNheight,		new_height,
	    NULL);
#endif /* NO_WIDGETS */
#endif /* MOTIF_WIDGETS */
    XtVaSetValues(cur_win->screen,
	    XtNheight,	new_height,
	    XtNwidth,	new_width,
#if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
	    XtNx,	cur_win->scrollbar_on_left ? cur_win->pane_width+2 : 0,
#endif
	    NULL);
    XtVaSetValues(cur_win->pane,
#if !OPT_KEV_SCROLLBARS && !OPT_XAW_SCROLLBARS
	    XtNwidth,	cur_win->pane_width,
#if OL_WIDGETS
	    XtNheight,	new_height,
#endif /* OL_WIDGETS */
#else	/* OPT_KEV_SCROLLBARS */
	    XtNx,	cur_win->scrollbar_on_left ? 0 : new_width,
	    XtNwidth,	cur_win->pane_width+2,
	    XtNheight,	new_height - cur_win->char_height,
#endif /* OPT_KEV_SCROLLBARS */
	    NULL);
#if MOTIF_WIDGETS
    {
	WidgetList children;
	Cardinal nchildren;
	XtVaGetValues(cur_win->form_widget,
		XmNchildren, &children,
		XmNnumChildren, &nchildren,
		NULL);
	XtManageChildren(children, nchildren);
    }
    XtVaSetValues(cur_win->form_widget,
	    XmNresizePolicy,	XmRESIZE_ANY,
	    NULL);
#endif /* MOTIF_WIDGETS */

    XtVaGetValues(cur_win->top_widget,
	    XtNheight,	&cur_win->top_height,
	    XtNwidth,	&cur_win->top_width,
	    NULL);
    XtVaGetValues(cur_win->screen,
	    XtNheight,	&new_height,
	    XtNwidth,	&new_width,
	    NULL);

    nr = (int)(new_height / cur_win->char_height);
    nc = (int)(new_width  / cur_win->char_width);

    if (nr < MINROWS || nc < MINCOLS) {
	gui_resize(nc, nr);
	/* Calling XResizeWindow will cause another ConfigureNotify
	 * event, so we should return early and let this event occur.
	 */
	return;
    }

    if (nc != (int) cur_win->cols
     || nr != (int) cur_win->rows) {
	newscreensize(nr,nc);
	cur_win->rows = nr;
	cur_win->cols = nc;
	if (check_scrollbar_allocs() == TRUE) /* no allocation failure */
		update_scrollbar_sizes();
    }
#if MOTIF_WIDGETS
    lookfor_sb_resize = FALSE;
#endif
}

void
gui_resize(
    int cols,
    int rows)
{
    if (cols < MINCOLS)
	cols = MINCOLS;
    if (rows < MINROWS)
	rows = MINROWS;

    XResizeWindow(dpy, XtWindow(cur_win->top_widget),
		  (unsigned)cols * cur_win->char_width + cur_win->base_width,
		  (unsigned)rows * cur_win->char_height + cur_win->base_height);
    /* This should cause a ConfigureNotify event */
}

static
int check_scrollbar_allocs(void)
{
	int newmax = cur_win->rows/2;
	int oldmax = cur_win->maxscrollbars;

	if (newmax > oldmax) {

		GROW(cur_win->scrollbars, Widget, oldmax, newmax);
#if OL_WIDGETS
		GROW(cur_win->sliders, Widget, oldmax, newmax);
#endif
#if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
		GROW(cur_win->scrollinfo, ScrollInfo, oldmax, newmax);
		GROW(cur_win->grips, Widget, oldmax, newmax);
#endif

		cur_win->maxscrollbars = newmax;
	}
	return TRUE;
}

#if MOTIF_WIDGETS
static void
grip_moved(
    Widget	w,
    XtPointer	unused,
    XEvent     *ev,
    Boolean    *continue_to_dispatch)
{
    int i;
    register WINDOW *wp, *saved_curwp;
    Dimension height;
    int	lines;

    if (!lookfor_sb_resize)
	return;
    lookfor_sb_resize = FALSE;
    saved_curwp = curwp;

    i = 0;
    for_each_visible_window(wp) {
	XtVaGetValues(cur_win->scrollbars[i],
		XtNheight, &height,
		NULL);
	lines = (height+(cur_win->char_height/2)) / cur_win->char_height;
	if (lines <= 0) {
	    lines = 1;
	}
	curwp = wp;
	resize(TRUE, lines);
	i++;
    }
    set_curwp(saved_curwp);
    (void) update(TRUE);
}
#endif

static void
configure_bar(
    Widget w,
    XEvent *event,
    String *params,
    Cardinal *num_params)
{
    WINDOW *wp;
    int i;

    if (*num_params != 1
     || (event->type != ButtonPress && event->type != ButtonRelease))
	return;

    i = 0;
    for_each_visible_window(wp) {
	if (cur_win->scrollbars[i] == w) {
	    if (strcmp(params[0], "Only") == 0) {
		set_curwp(wp);
		onlywind(TRUE,0);
	    }
	    else if (strcmp(params[0], "Kill") == 0) {
		set_curwp(wp);
		delwind(TRUE,0);
	    }
	    else if (strcmp(params[0], "Split") == 0) {
		if (wp->w_ntrows < 3) {
		    x_beep();
		    break;
		}
		else {
		    int newsize;
		    set_curwp(wp);
		    newsize = CEIL(event->xbutton.y, cur_win->char_height)-1;
		    if (newsize > wp->w_ntrows - 2)
			newsize = wp->w_ntrows - 2;
		    else if (newsize < 1)
			newsize = 1;
		    splitwind(TRUE, 1);
		    resize(TRUE, newsize);
		}
	    }
	    (void) update(TRUE);
	    break;
	}
	i++;
    }
}


#if MOTIF_WIDGETS
static void
pane_button(
    Widget	w,
    XtPointer	unused,
    XEvent     *ev,
    Boolean    *continue_to_dispatch)
{
    lookfor_sb_resize = TRUE;
}
#endif /* MOTIF_WIDGETS */

/*ARGSUSED*/
static void
x_change_focus(
    Widget	w,
    XtPointer	unused GCC_UNUSED,
    XEvent     *ev,
    Boolean    *continue_to_dispatch GCC_UNUSED)
{
    static int got_focus_event = FALSE;

    TRACE(("x11:x_change_focus(type=%d)\n", ev->type));
    switch (ev->type) {
	case EnterNotify:
	    TRACE(("... EnterNotify\n"));
	    if (!ev->xcrossing.focus || got_focus_event)
		return;
	    goto focus_in;
	case FocusIn:
	    TRACE(("... FocusIn\n"));
	    got_focus_event = TRUE;
focus_in:
	    cur_win->show_cursor = True;
#if MOTIF_WIDGETS
	    XmProcessTraversal(cur_win->screen, XmTRAVERSE_CURRENT);
#else /* OL_WIDGETS || NO_WIDGETS */
	    XtSetKeyboardFocus(w, cur_win->screen);
#endif
	    x_flush();
	    break;
	case LeaveNotify:
	    TRACE(("... LeaveNotify\n"));
	    if ( !ev->xcrossing.focus
	      || got_focus_event
	      || ev->xcrossing.detail == NotifyInferior)
		return;
	    goto focus_out;
	case FocusOut:
	    TRACE(("... FocusOut\n"));
	    got_focus_event = TRUE;
focus_out:
	    cur_win->show_cursor = False;
	    x_flush();
	    break;
    }
}

/*ARGSUSED*/
static void
x_wm_delwin(
    Widget	w GCC_UNUSED,
    XtPointer	unused GCC_UNUSED,
    XEvent     *ev,
    Boolean    *continue_to_dispatch GCC_UNUSED)
{
    if ( ev->type == ClientMessage
      && ev->xclient.message_type == atom_WM_PROTOCOLS
      && (Atom) ev->xclient.data.l[0] == atom_WM_DELETE_WINDOW) {
	quit(FALSE, 0);		/* quit might not return */
	(void) update(TRUE);
    }
}

/*
 * Return true if we want to disable reports of the cursor position because the
 * cursor really should be on the message-line.
 */
#if UNUSED
int
x_on_msgline(void)
{
	return reading_msg_line || cur_win->was_on_msgline;
}
#endif

/*
 * Because we poll our input-characters in 'x_getc()', it is possible to have
 * exposure-events pending while doing lengthy processes (e.g., reading from a
 * pipe).  This procedure is invoked from a timer-handler and is designed to
 * handle the exposure-events, and to get keypress-events (i.e., for stopping a
 * lengthy process).
 */
void
x_move_events(void)
{
    XEvent ev;

    while (x_has_events()
	&& !kqfull(cur_win)) {

	/* Get and dispatch next event */
	XtAppNextEvent(cur_win->app_context, &ev);

	/*
	 * Ignore or save certain events which could get us into trouble with
	 * reentrancy.
	 */
	switch (ev.type) {
	    case ButtonPress :
	    case ButtonRelease :
	    case MotionNotify :
		/* Ignore the event */
		continue;

	    case ClientMessage :
	    case SelectionClear :
	    case SelectionNotify :
	    case SelectionRequest :
	    case ConfigureNotify :
	    case ConfigureRequest :
	    case PropertyNotify :
	    case ReparentNotify :
	    case ResizeRequest :
		/* Queue for later processing.  */
		evqadd(&ev);
		continue;

	    default :
		/* do nothing here...we'll dispatch the event below */
		break;
	}

	XtDispatchEvent(&ev);

	/*
	 * If the event was a keypress, check it to see if it was an
	 * interrupt character.  We check here to make sure that the
	 * queue was non-empty, because not all keypresses put
	 * characters into the queue.  We assume that intrc will not
	 * appear in any multi-character sequence generated by a key
	 * press, or that if it does, it will be the last character in
	 * the sequence.  If this is a bad assumption, we will have to
	 * keep track of what the state of the queue was prior to the
	 * keypress and scan the characters added to the queue as a
	 * result of the keypress.
	 */

	if (!kqempty(cur_win) && ev.type == KeyPress) {
	    int c = kqpop(cur_win);
	    if (c == intrc) {
		kqadd(cur_win, esc_c);
#if SYS_VMS
		kbd_alarm(); /* signals? */
#else
		(void)signal_pg(SIGINT);
#endif
	    }
	    else
		kqadd(cur_win, c);
	}
    }
}

#if OPT_WORKING
void
x_working(void)
{
    cur_win->want_to_work = TRUE;
}

static int
x_has_events(void)
{
    if (cur_win->want_to_work == TRUE) {
	x_set_watch_cursor(TRUE);
	cur_win->want_to_work = FALSE;
    }
    return (XtAppPending(cur_win->app_context) & XtIMXEvent);
}

static void
x_set_watch_cursor(int onflag)
{
    static int watch_is_on = FALSE;
#if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
    int i;
#endif

    if (onflag == watch_is_on)
	return;

    watch_is_on = onflag;

    if (onflag) {
	set_pointer(XtWindow(cur_win->screen), cur_win->watch_pointer);
#if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
	for (i=0; i<cur_win->nscrollbars; i++) {
	    set_pointer(
		    XtWindow(cur_win->scrollbars[i]), cur_win->watch_pointer);
	    if (i < cur_win->nscrollbars-1)
		set_pointer(
			XtWindow(cur_win->grips[i]), cur_win->watch_pointer);
	}
#endif /* OPT_KEV_SCROLLBARS */
    }
    else {
	set_pointer(XtWindow(cur_win->screen), cur_win->normal_pointer);
#if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
	for (i=0; i<cur_win->nscrollbars; i++) {
	    set_pointer(
		    XtWindow(cur_win->scrollbars[i]),
		    curs_sb_v_double_arrow);
	    if (i < cur_win->nscrollbars-1)
		set_pointer(
			XtWindow(cur_win->grips[i]),
			curs_double_arrow);
	}
#endif /* OPT_KEV_SCROLLBARS */
    }
}
#endif /* OPT_WORKING */

static int
evqempty(void)
{
    return evqhead == NULL;
}

static void
evqadd(const XEvent *evp)
{
    struct eventqueue * newentry;
    newentry = typealloc(struct eventqueue);
    if (newentry == NULL)
	return;			/* FIXME: Need method for indicating error */
    newentry->next = NULL;
    newentry->event = *evp;
    if (evqhead == NULL)
	evqhead = evqtail = newentry;
    else {
	evqtail->next = newentry;
	evqtail = newentry;
    }
}

static void
evqdel(XEvent *evp)
{
    struct eventqueue *delentry = evqhead;
    if (delentry == NULL)
	return;			/* should not happen */
    *evp = delentry->event;
    evqhead = delentry->next;
    if (evqhead == NULL)
	evqtail = NULL;
    free((char *)delentry);
}

static void
kqinit(TextWindow tw)
{
    tw->kqhead = 0;
    tw->kqtail = 0;
}

static int
kqempty(TextWindow tw)
{
    return tw->kqhead == tw->kqtail;
}

static int
kqfull(TextWindow tw)
{
    return tw->kqhead == (tw->kqtail + 1) % KQSIZE;
}

static int
kqdel(TextWindow tw)
{
    int c;
    c = tw->kq[tw->kqhead];
    tw->kqhead = (tw->kqhead + 1) % KQSIZE;
    return c;
}

static void
kqadd(
    TextWindow tw,
    int c)
{
    tw->kq[tw->kqtail] = c;
    tw->kqtail = (tw->kqtail + 1) % KQSIZE;
}

static int
kqpop(TextWindow tw)
{
    if (--(tw->kqtail) < 0)
	tw->kqtail = KQSIZE-1;
    return (tw->kq[tw->kqtail]);
}

/*ARGSUSED*/
static void
display_cursor(
    XtPointer client_data GCC_UNUSED,
    XtIntervalId *idp)
{
    static Bool am_blinking = FALSE;
    int the_col = (ttcol >= term.cols) ? term.cols - 1 : ttcol;

    /*
     * Return immediately if we are either in the process of making a
     * selection (by wiping with the mouse) or if the cursor is already
     * displayed and display_cursor() is being called explicitly from the
     * event loop in x_getc.
     */
    if (cur_win->wipe_permitted) {
	am_blinking = FALSE;
	if (cur_win->blink_id != (XtIntervalId) 0) {
	    XtRemoveTimeOut(cur_win->blink_id);
	    cur_win->blink_id = (XtIntervalId) 0;
	}
	return;
    }

    if (IS_DIRTY(ttrow,the_col) && idp == (XtIntervalId *) 0)
	return;

    if (cur_win->show_cursor) {
	if ( cur_win->blink_interval > 0
	  || ( cur_win->blink_interval < 0 && IS_REVERSED(ttrow, the_col) )) {
	    if (idp != (XtIntervalId *) 0 || !am_blinking) {
		/* Set timer to get blinking */
		cur_win->blink_id = XtAppAddTimeOut(
			cur_win->app_context,
			(unsigned long) max(cur_win->blink_interval,
					    -cur_win->blink_interval),
			display_cursor,
			(XtPointer) 0);
		cur_win->blink_status ^= BLINK_TOGGLE;
		am_blinking = TRUE;
	    }
	    else
		cur_win->blink_status &= ~BLINK_TOGGLE;
	}
	else {
	    am_blinking = FALSE;
	    cur_win->blink_status &= ~BLINK_TOGGLE;
	    if (cur_win->blink_id != (XtIntervalId) 0) {
		XtRemoveTimeOut(cur_win->blink_id);
		cur_win->blink_id = (XtIntervalId) 0;
	    }
	}

	MARK_CELL_DIRTY(ttrow,the_col);
	MARK_LINE_DIRTY(ttrow);
	flush_line(&CELL_TEXT(ttrow,the_col), 1,
		   (unsigned int) (VATTRIB(CELL_ATTR(ttrow,the_col))
				^ ((cur_win->blink_status & BLINK_TOGGLE)
				  ? 0 : VACURS)),
		   ttrow, the_col);
    }
    else {
	/* This code will get called when the window no longer has the focus. */
	if (cur_win->blink_id != (XtIntervalId) 0) {
	    XtRemoveTimeOut(cur_win->blink_id);
	    cur_win->blink_id = (XtIntervalId) 0;
	}
	am_blinking = FALSE;
	MARK_CELL_DIRTY(ttrow,the_col);
	MARK_LINE_DIRTY(ttrow);
	flush_line(&CELL_TEXT(ttrow,the_col), 1,
	    (unsigned int) VATTRIB(CELL_ATTR(ttrow,the_col)), ttrow, the_col);
	XDrawRectangle(dpy, cur_win->win,
		       IS_REVERSED(ttrow,the_col) ? cur_win->cursgc
						: cur_win->revcursgc,
		       x_pos(cur_win, ttcol), y_pos(cur_win, ttrow),
		       (unsigned)(cur_win->char_width - 1),
		       (unsigned)(cur_win->char_height - 1));
    }
}


/*
 * main event loop.  this means we'll be stuck if an event that needs
 * instant processing comes in while its off doing other work, but
 * there's no (easy) way around that.
 */
static int
x_getc(void)
{
    int c;

    while (!evqempty()) {
	XEvent ev;
	evqdel(&ev);
	XtDispatchEvent(&ev);
    }
#if OPT_WORKING
    x_set_watch_cursor(FALSE);
#endif
    x_start_autocolor_timer();
    for_ever {

	if (tb_more(PasteBuf)) {	/* handle any queued pasted text */
	    c = tb_next(PasteBuf);
	    c |= NOREMAP;	/* pasted chars are not subject to mapping */
	    cur_win->pasting = True;
	    break;
	} else if (cur_win->pasting) {
	    /*
	     * Set the default position for new pasting to just past the newly
	     * inserted text.
	     */
	    if (DOT.o < llength(DOT.l) && !insertmode)
		DOT.o++;		/* Advance DOT so that consecutive
					   pastes come out right */
	    cur_win->pasting = False;
	    update(TRUE);	/* make sure ttrow & ttcol are valid */
	}

	if (!kqempty(cur_win)) {
	    c = kqdel(cur_win);
	    break;
	}

	/*
	 * Get and dispatch as many X events as possible.  This permits
	 * the editor to catch up if it gets behind in processing keyboard
	 * events since the keyboard queue will likely have something in it.
	 * update() will check for typeahead and will defer its processing
	 * until there is nothing more in the keyboard queue.
	 */

	do {
	    XEvent ev;
	    XtAppNextEvent(cur_win->app_context, &ev);
	    XtDispatchEvent(&ev);
	} while (x_has_events()
	    && !kqfull(cur_win));
    }

    x_stop_autocolor_timer();

    return c;
}

/*
 * Another event loop used for determining type-ahead.
 */
int
x_typahead(
    int milli)		/* milliseconds to wait for type-ahead */
{
    int status;
    XtIntervalId timeoutid = 0;
    int timedout;
    int olddkr;

    if (!cur_win->exposed)
	return FALSE;

    olddkr = im_waiting(TRUE);

    status = !kqempty(cur_win) || tb_more(PasteBuf);

    if (!status) {

	if (milli) {
	    timedout = 0;
	    timeoutid = XtAppAddTimeOut(
			    cur_win->app_context,
			    (ULONG) milli,
			    x_typahead_timeout,
			    (XtPointer) &timedout);
	}
	else
	    timedout = 1;

	while (kqempty(cur_win) && !evqempty()) {
	    XEvent ev;
	    evqdel(&ev);
	    XtDispatchEvent(&ev);
	}
#if OPT_WORKING
	x_set_watch_cursor(FALSE);
#endif

	/*
	 * Process pending events until we get some keyboard input.
	 * Note that we do not block here.
	 */
	while (kqempty(cur_win) &&
		x_has_events()) {
	    XEvent ev;
	    XtAppNextEvent(cur_win->app_context, &ev);
	    XtDispatchEvent(&ev);
	}

	/* Now wait for timer and process events as necessary. */
	while (!timedout && kqempty(cur_win)) {
	    XtAppProcessEvent(cur_win->app_context, (XtInputMask)XtIMAll);
	}

	if (!timedout)
	    XtRemoveTimeOut(timeoutid);

	status = !kqempty(cur_win);
    }

    (void) im_waiting(olddkr);

    return status;
}

/*ARGSUSED*/
static void
x_typahead_timeout(
    XtPointer flagp,
    XtIntervalId *id GCC_UNUSED)
{
    * (int *) flagp = 1;
}

/*ARGSUSED*/
static void
x_key_press(
    Widget	w GCC_UNUSED,
    XtPointer	unused GCC_UNUSED,
    XEvent     *ev,
    Boolean    *continue_to_dispatch GCC_UNUSED)
{
    char	buffer[128];
    KeySym	keysym;
    int		num;

    register int i;
    register SIZE_T n;

    static const struct {
	KeySym  key;
	int     code;
    } escapes[] = {
	/* Arrow keys */
	{XK_Up,      KEY_Up},
	{XK_Down,    KEY_Down},
	{XK_Right,   KEY_Right},
	{XK_Left,    KEY_Left},
	/* page scroll */
	{XK_Next,    KEY_Next},
	{XK_Prior,   KEY_Prior},
	{XK_Home,    KEY_Home},
	{XK_End,     KEY_End},
	/* editing */
	{XK_Insert,  KEY_Insert},
	{XK_Delete,  KEY_Delete},
	{XK_Find,    KEY_Find},
	{XK_Select,  KEY_Select},
	/* command keys */
	{XK_Menu,    KEY_Menu},
	{XK_Help,    KEY_Help},
	/* function keys */
	{XK_F1,      KEY_F1},
	{XK_F2,      KEY_F2},
	{XK_F3,      KEY_F3},
	{XK_F4,      KEY_F4},
	{XK_F5,      KEY_F5},
	{XK_F6,      KEY_F6},
	{XK_F7,      KEY_F7},
	{XK_F8,      KEY_F8},
	{XK_F9,      KEY_F9},
	{XK_F10,     KEY_F10},
	{XK_F11,     KEY_F11},
	{XK_F12,     KEY_F12},
	{XK_F13,     KEY_F13},
	{XK_F14,     KEY_F14},
	{XK_F15,     KEY_F15},
	{XK_F16,     KEY_F16},
	{XK_F17,     KEY_F17},
	{XK_F18,     KEY_F18},
	{XK_F19,     KEY_F19},
	{XK_F20,     KEY_F20},
#if defined(XK_F21) && defined(KEY_F21)
	{XK_F21,     KEY_F21},
	{XK_F22,     KEY_F22},
	{XK_F23,     KEY_F23},
	{XK_F24,     KEY_F24},
	{XK_F25,     KEY_F25},
	{XK_F26,     KEY_F26},
	{XK_F27,     KEY_F27},
	{XK_F28,     KEY_F28},
	{XK_F29,     KEY_F29},
	{XK_F30,     KEY_F30},
	{XK_F31,     KEY_F31},
	{XK_F32,     KEY_F32},
	{XK_F33,     KEY_F33},
	{XK_F34,     KEY_F34},
	{XK_F35,     KEY_F35},
#endif
	/* keypad function keys */
	{XK_KP_F1,   KEY_KP_F1},
	{XK_KP_F2,   KEY_KP_F2},
	{XK_KP_F3,   KEY_KP_F3},
	{XK_KP_F4,   KEY_KP_F4},
#if defined(XK_KP_Up)
	{XK_KP_Up,      KEY_Up},
	{XK_KP_Down,    KEY_Down},
	{XK_KP_Right,   KEY_Right},
	{XK_KP_Left,    KEY_Left},
	{XK_KP_Next,    KEY_Next},
	{XK_KP_Prior,   KEY_Prior},
	{XK_KP_Home,    KEY_Home},
	{XK_KP_End,     KEY_End},
	{XK_KP_Insert,  KEY_Insert},
	{XK_KP_Delete,  KEY_Delete},
#endif
    };

    if (ev->type != KeyPress)
	return;

    x_start_autocolor_timer();

#if OPT_LOCALE
    if (!XFilterEvent(ev, *(&ev->xkey.window))) {
	if (Input_Context != NULL) {
	    Status	    status_return;

	    num = XmbLookupString(Input_Context, (XKeyPressedEvent *) ev, buffer,
				  sizeof(buffer), &keysym,
				  &status_return);
	} else {
	    num = XLookupString((XKeyPressedEvent *) ev, buffer,
				sizeof(buffer), &keysym,
				(XComposeStatus *) 0);
	}
    } else
	num = 0;
#else
    num = XLookupString((XKeyPressedEvent *) ev, buffer, sizeof(buffer),
			&keysym, (XComposeStatus *) 0);
#endif


    if (num <= 0) {
	for (n = 0; n < TABLESIZE(escapes); n++) {
	    if (keysym == escapes[n].key) {
		kqadd(cur_win, escapes[n].code);
		return;
	    }
	}
    }
    else if (num == 1 && (ev->xkey.state & Mod1Mask))
	buffer[0] |= HIGHBIT;

    /* FIXME: Should do something about queue full conditions */
    if (num > 0) {
	for (i=0; i<num && !kqfull(cur_win); i++)
	    kqadd(cur_win, char2int(buffer[i]));
    }
}

/*
 * change reverse video status
 */
static void
x_rev(UINT state)
{
    cur_win->reverse = state;
}



#if OPT_COLOR
static void
x_fcol(int color)
{
    TRACE(("x_fcol(%d), cur_win->fg was %#lx\n", color, cur_win->fg));
    cur_win->fg = (color >= 0 && color < NCOLORS)
		    ? cur_win->colors_fg[color]
		    : cur_win->default_fg;
    TRACE(("...cur_win->fg = %#lx%s\n", cur_win->fg, cur_win->fg == cur_win->default_fg ? " (default)" : ""));

    XSetForeground(dpy, cur_win->textgc,    cur_win->fg);
    XSetBackground(dpy, cur_win->reversegc, cur_win->fg);

    x_touch(cur_win, 0, 0, cur_win->cols, cur_win->rows);
    x_flush();
}

static void
x_bcol(int color)
{
    TRACE(("x_bcol(%d), cur_win->bg was %#lx\n", color, cur_win->bg));
    cur_win->bg = (color >= 0 && color < NCOLORS)
		    ? (SamePixel(cur_win->colors_bg[color], cur_win->default_bg)
			? cur_win->colors_fg[color]
			: cur_win->colors_bg[color])
		    : cur_win->default_bg;
    TRACE(("...cur_win->bg = %#lx%s\n", cur_win->bg, cur_win->bg == cur_win->default_bg ? " (default)" : ""));

    if (color == ENUM_FCOLOR) {
	XSetBackground(dpy, cur_win->textgc,    cur_win->default_bg);
	XSetForeground(dpy, cur_win->reversegc, cur_win->default_bg);
    } else {
	XSetBackground(dpy, cur_win->textgc,    cur_win->bg);
	XSetForeground(dpy, cur_win->reversegc, cur_win->bg);
    }
    cur_win->bg_follows_fg = (color == ENUM_FCOLOR);
    TRACE(("...cur_win->bg_follows_fg = %#x\n", cur_win->bg_follows_fg));

    reset_color_gcs();

    XtVaSetValues(cur_win->screen,
	    XtNbackground,		cur_win->bg,
	    NULL);

    x_touch(cur_win, 0, 0, cur_win->cols, cur_win->rows);
    x_flush();
}

static void
x_ccol(int color)
{
    XGCValues   gcvals;
    ULONG	gcmask;
    Pixel	fg, bg;

    TRACE(("x_ccol(%d)\n", color));
    if (cur_win->is_color_cursor == FALSE) {
	if (!color_cursor()) {
	    gccolor = -1;
	    return;
	}
    }

    fg = (color >= 0 && color < NCOLORS)
		    ? (SamePixel(cur_win->colors_bg[color], cur_win->default_bg)
		        ? cur_win->colors_bg[color]
		        : cur_win->colors_fg[color])
		    : cur_win->cursor_fg;

    bg = (color >= 0 && color < NCOLORS)
		    ? (SamePixel(cur_win->colors_bg[color], cur_win->default_bg)
			? cur_win->colors_fg[color]
			: cur_win->colors_bg[color])
		    : cur_win->cursor_bg;

    gcmask = GCForeground|GCBackground;
    gcvals.background = bg;
    gcvals.foreground = fg;
    XChangeGC(dpy, cur_win->cursgc, gcmask, &gcvals);

    gcvals.foreground = bg;
    gcvals.background = fg;
    XChangeGC(dpy, cur_win->revcursgc, gcmask, &gcvals);
}

#endif

/* beep */
static void
x_beep(void)
{
#if OPT_FLASH
    if (global_g_val(GMDFLASH)) {
	beginDisplay();
	XGrabServer(dpy);
	XSetFunction(dpy, cur_win->textgc, GXxor);
	XSetBackground(dpy, cur_win->textgc, 0L);
	XSetForeground(dpy, cur_win->textgc, cur_win->fg ^ cur_win->bg);
	XFillRectangle(dpy, cur_win->win, cur_win->textgc,
		       0, 0, x_width(cur_win), x_height(cur_win));
	XFlush(dpy);
	catnap(90, FALSE);
	XFillRectangle(dpy, cur_win->win, cur_win->textgc,
		       0, 0, x_width(cur_win), x_height(cur_win));
	XFlush(dpy);
	XSetFunction(dpy, cur_win->textgc, GXcopy);
	XSetBackground(dpy, cur_win->textgc, cur_win->bg);
	XSetForeground(dpy, cur_win->textgc, cur_win->fg);
	XUngrabServer(dpy);
	endofDisplay();
    }
    else
#endif
	XBell(dpy, 0);
}

#if NO_LEAKS
void
x11_leaks(void)
{
	if (cur_win != 0) {
		FreeIfNeeded(cur_win->fontname);
	}
}
#endif	/* NO_LEAKS */

#ifdef USE_SET_WM_NAME
static char x_window_name[NFILEN];
#endif

static char x_icon_name[NFILEN];

void
x_set_icon_name(const char *name)
{
	XTextProperty Prop;

	(void)strncpy0(x_icon_name, name, NFILEN);

	Prop.value = (unsigned char *)x_icon_name;
	Prop.encoding = XA_STRING;
	Prop.format = 8;
	Prop.nitems = strlen(x_icon_name);

	XSetWMIconName(dpy,XtWindow(cur_win->top_widget),&Prop);
}

char *
x_get_icon_name(void)
{
	return x_icon_name;
}

void
x_set_window_name(const char *name)
{
    if (strcmp(name, x_get_window_name())) {
#ifdef USE_SET_WM_NAME
	XTextProperty Prop;

	(void)strncpy0(x_window_name, name, NFILEN);

	Prop.value = (unsigned char *)x_window_name;
	Prop.encoding = XA_STRING;
	Prop.format = 8;
	Prop.nitems = strlen(x_window_name);

	XSetWMName(dpy,XtWindow(cur_win->top_widget),&Prop);
#else
	XtVaSetValues(cur_win->top_widget, XtNtitle, name, NULL);
#endif
    }
}

char *
x_get_window_name(void)
{
	char *result;
#ifdef USE_SET_WM_NAME
	result = x_window_name;
#else
	result = "";
	if (cur_win->top_widget != 0) {
		XtVaGetValues(cur_win->top_widget, XtNtitle, &result, NULL);
	}
#endif
	return result;
}

char *
x_get_display_name(void)
{
	return XDisplayString(dpy);
}

static void
watched_input_callback(XtPointer fd,
		       int *src GCC_UNUSED,
		       XtInputId *id GCC_UNUSED)
{
    dowatchcallback((int) fd);
}

static int
x_watchfd(int fd, WATCHTYPE type, long *idp)
{
    *idp = (long) XtAppAddInput(
		    cur_win->app_context,
		    fd,
		    (XtPointer) ((type & WATCHREAD)  ? XtInputReadMask :
				 (type & WATCHWRITE) ? XtInputWriteMask
						     : XtInputExceptMask),
		    watched_input_callback,
		    (XtPointer) fd);
    return TRUE;
}

static void
x_unwatchfd(int fd GCC_UNUSED, long id)
{
    XtRemoveInput((XtInputId) id);
}

/* Autocolor functions
 *
 * Note that these are self contained and could be moved to another
 * file if desired.
 */

static XtIntervalId x_autocolor_timeout_id;

static void
x_start_autocolor_timer()
{
#if OPT_COLOR&&!SMALLER
    int millisecs = global_b_val(VAL_AUTOCOLOR);
    x_stop_autocolor_timer();
    if (millisecs > 0)
	x_autocolor_timeout_id = XtAppAddTimeOut(cur_win->app_context,
						 (unsigned long) millisecs,
						 x_autocolor_timeout,
						 (XtPointer) 0);
#endif
}

static void
x_stop_autocolor_timer()
{
    if (x_autocolor_timeout_id != 0)
	XtRemoveTimeOut(x_autocolor_timeout_id);
    x_autocolor_timeout_id = 0;
}

static void
x_autocolor_timeout(XtPointer data GCC_UNUSED, XtIntervalId *id GCC_UNUSED)
{
    if (kqempty(cur_win)) {
	XClientMessageEvent ev;

	autocolor();
	XSync(dpy, False);

	/* Send a null message to ourselves to prevent stalling in
	   the event loop. */
	ev.type = ClientMessage;
	ev.serial = 0;
	ev.send_event = True;
	ev.display = dpy;
	ev.window  = cur_win->win;
	ev.message_type = None;
	ev.format = 8;
	XSendEvent(dpy, cur_win->win, False, (long)0, (XEvent *)&ev);
    }
}

/*
 * Return true if the given character would be printable.  Not all characters are.
 */
int
gui_isprint(int ch)
{
    XFontStruct *pf = cur_win->pfont;
    XCharStruct	*pc = 0;

    if (pf != 0
     && pf->per_char != 0
     && !pf->all_chars_exist) {
	if (ch < (int) pf->min_char_or_byte2
	 || ch > (int) pf->max_char_or_byte2) {
	    TRACE(("MissingChar %c\n", ch));
	    return FALSE;
	}
	if (pf->min_byte1 == 0
	 && pf->max_byte1 == 0) {
	    pc = pf->per_char + (ch - pf->min_char_or_byte2);
	} /* FIXME: this does not handle doublebyte characters */
	if (pc != 0
	 && (pc->lbearing + pc->rbearing) == 0
	 && (pc->ascent   + pc->descent) == 0
	 && pc->width == 0) {
	    return FALSE;
	}
    }
    return TRUE;
}

#if OPT_LOCALE
/*
 * This is more or less stolen straight from XFree86 xterm.
 * This should support all European type languages.
 */
static void
init_xlocale(void)
{
    char	   *p, *s, buf[32], tmp[1024];
    XIM		    xim = NULL;
    XIMStyle	    input_style = 0;
    XIMStyles      *xim_styles = NULL;
    int		    found;

    Input_Context = NULL;

    if (rs_inputMethod == NULL || !*rs_inputMethod) {
	if ((p = XSetLocaleModifiers("@im=none")) != NULL && *p)
	    xim = XOpenIM(dpy, NULL, NULL, NULL);
    } else {
	strcpy(tmp, rs_inputMethod);
	for (s = tmp; *s; s++) {
	    char	   *end, *next_s;

	    for (; *s && isSpace(*s); s++)
		/* */ ;
	    if (!*s)
		break;
	    for (end = s; (*end && (*end != ',')); end++)
		/* */ ;
	    for (next_s = end--; ((end >= s) && isSpace(*end)); end--)
		/* */ ;
	    *++end = '\0';
	    if (*s) {
		strcpy(buf, "@im=");
		strcat(buf, s);
		if ((p = XSetLocaleModifiers(buf)) != NULL && *p &&
		    (xim = XOpenIM(dpy, NULL, NULL, NULL)) != NULL)
		    break;
	    }
	    if (!*(s = next_s))
		break;
	}
    }

    if (xim == NULL && (p = XSetLocaleModifiers("")) != NULL && *p)
	xim = XOpenIM(dpy, NULL, NULL, NULL);

    if (xim == NULL) {
	fprintf(stderr, "Failed to open input method\n");
	return;
    }
    if (XGetIMValues(xim, XNQueryInputStyle, &xim_styles, NULL) || !xim_styles) {
	fprintf(stderr, "input method doesn't support any style\n");
	XCloseIM(xim);
	return;
    }
    strcpy(tmp, (rs_preeditType ? rs_preeditType : "Root"));
    for (found = 0, s = tmp; *s && !found; ) {
	unsigned short  i;
	char	       *end, *next_s;

	for (; *s && isSpace(*s); s++)
	    /* */ ;
	if (!*s)
	    break;
	for (end = s; (*end && (*end != ',')); end++)
	    /* */ ;
	for (next_s = end--; ((end >= s) && isSpace(*end));)
	    *end-- = 0;

	if (!strcmp(s, "OverTheSpot"))
	    input_style = (XIMPreeditPosition | XIMStatusArea);
	else if (!strcmp(s, "OffTheSpot"))
	    input_style = (XIMPreeditArea | XIMStatusArea);
	else if (!strcmp(s, "Root"))
	    input_style = (XIMPreeditNothing | XIMStatusNothing);

	for (i = 0; i < xim_styles->count_styles; i++)
	    if (input_style == xim_styles->supported_styles[i]) {
		found = 1;
		break;
	    }
	s = next_s;
    }
    XFree(xim_styles);

    if (found == 0) {
	fprintf(stderr, "input method doesn't support my preedit type\n");
	XCloseIM(xim);
	return;
    }
    /*
     * This program only understands the Root preedit_style yet
     * Then misc.preedit_type should default to:
     *          "OverTheSpot,OffTheSpot,Root"
     *	/MaF
     */
    if (input_style != (XIMPreeditNothing | XIMStatusNothing)) {
	fprintf(stderr, "This program only supports the \"Root\" preedit type\n");
	XCloseIM(xim);
	return;
    }
    Input_Context = XCreateIC(xim, XNInputStyle, input_style,
			      XNClientWindow, cur_win->win,
			      XNFocusWindow, cur_win->win,
			      NULL);

    if (Input_Context == NULL) {
	fprintf(stderr, "Failed to create input context\n");
	XCloseIM(xim);
    }
}

#endif /* OPT_LOCALE */

#endif	/* DISP_X11 && XTOOLKIT */
