/*  Copyright 1992 John Bovey, University of Kent at Canterbury.
 *
 *  You can do what you like with this source code as long as
 *  you don't try to make money out of it and you include an
 *  unaltered copy of this message (including the copyright).
 */

char xvt_xsetup_c_sccsid[] = "@(#)xsetup.c	1.1 14/7/92 (UKC)";

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xresource.h>
#include <X11/cursorfont.h>
#include "rvt.h"
#include "command.h"
#include "xsetup.h"
#include "screen.h"
#include "sbar.h"

#define XVT_CLASS	"XTerm"
#define SBAR_WIDTH	15	/* width of scroll bar */

#define VT_EVENTS	(	ExposureMask |\
				EnterWindowMask|\
				LeaveWindowMask |\
				ButtonPressMask |\
				ButtonReleaseMask |\
				Button1MotionMask \
			)

#define MW_EVENTS	(	KeyPressMask |\
				FocusChangeMask |\
				StructureNotifyMask \
			)

#define SB_EVENTS	(	ExposureMask |\
				EnterWindowMask|\
				LeaveWindowMask |\
				Button2MotionMask |\
				ButtonReleaseMask |\
				ButtonPressMask \
			)

/*  External global variables that are initialised at startup.
 */
Display *display;
Window vt_win;			/* vt100 window */
Window sb_win;			/* scroll bar window */
Window main_win;		/* parent window */
Colormap colormap;
XFontStruct *mainfont;		/* main font structure */
XFontStruct *boldfont;		/* bold font structure */
GC gc;				/* GC for drawing text */
GC negc;			/* GC for moving areas without graphics
				 * exposure */
GC hlgc;			/* GC used for highlighting text cursor */
GC sbgc;			/* GC used for drawing the scrollbar */
unsigned long foreground;	/* foreground pixel value */
unsigned long background;	/* background pixel value */
int reverse_wrap = 0;		/* enable reverse wrapround */
int debugging = 0;		/* enable debugging output */
int messages = 0;		/* flag to enable messages */
char *username;			/* name to run as */
char *rcmd;				/* remtoe command */
char *nodename;			/* nodename */


static char *xvt_name;		/* the name the program is run under */
static char *res_name;		/* the resource name */
static char *window_name;	/* window name for titles etc. */
static char *icon_name;		/* name to display in the icon */
static int screen;		/* the X screen number */
static Visual *visual;
static XrmDatabase rDB;		/* merged resources database */
static unsigned long border;	/* border pixel value */
static int border_width = 1;
static int save_lines = DEF_SAVED_LINES;	/* number of saved lines */
static XColor foreground_color;
static XColor background_color;
static int iconic = 0;		/* start up iconized */

static int show_scrollbar = 0;	/* scroll-bar displayed if true */

#define OPTABLESIZE	21

static XrmOptionDescRec optable[] = {
	{"-display", ".display", XrmoptionSepArg, (caddr_t) NULL},
	{"-geometry", "*geometry", XrmoptionSepArg, (caddr_t) NULL},
	{"-background", "*background", XrmoptionSepArg, (caddr_t) NULL},
	{"-bg", "*background", XrmoptionSepArg, (caddr_t) NULL},
	{"-foreground", "*foreground", XrmoptionSepArg, (caddr_t) NULL},
	{"-fg", "*foreground", XrmoptionSepArg, (caddr_t) NULL},
	{"-bd", "*borderColor", XrmoptionSepArg, (caddr_t) NULL},
	{"-bw", "*borderWidth", XrmoptionSepArg, (caddr_t) NULL},
	{"-font", "*font", XrmoptionSepArg, (caddr_t) NULL},
	{"-fb", "*boldFont", XrmoptionSepArg, (caddr_t) NULL},
	{"-name", "*name", XrmoptionSepArg, (caddr_t) NULL},
	{"-sl", "*saveLines", XrmoptionSepArg, (caddr_t) NULL},
	{"-cc", "*charClass", XrmoptionSepArg, (caddr_t) NULL},
	{"-sb", "*scrollBar", XrmoptionNoArg, "on"},
	{"-rw", "*reverseWrap", XrmoptionNoArg, "on"},
	{"-msg", "*messages", XrmoptionNoArg, "on"},
	{"-iconic", "*iconic", XrmoptionNoArg, "on"},
	{"-debug", "*debug", XrmoptionNoArg, "on"},
};

static char *usearray[] = {
	"rvt [-l <username>] [-display <name>] [-geometry <spec>] [-bg <colour>]",
	"    [-fg <colour>] [-bd <colour>] [-bw <count>] [-font <fontname>]",
	"    [-fb <fontname>] [-name <name>] [-sl <count>] [-cc <char-class>]",
	"    [-sb] [-rw] [-msg] [-iconic] <remote machine name>",
	"where:",
	"<remote machine>	name of the remote machine to connect to.",
	"-l <username>		username to connect as",
	"-display <name>	specify the display (server)",
	"-geometry <spec>	the initial window geometry",
	"-background <colour>	background colour",
	"-bg <colour>		same as -background",
	"-foreground <colour>	foreground colour",
	"-fg <colour>		same as -foreground",
	"-bd <colour>		border colour",
	"-bw <count>		border width",
	"-font <fontname>	normal font",
	"-fb <fontname>		font used for bold text",
	"-name <name>		name used for matching X resources",
	"-sl <count>		number of lines saved after scrolling off window",
	"-cc <char-class>	character classes for double click",
	"-sb			provide an initial scrollbar",
	"-rw			enable reverse wrap",
	"-msg			allow messages",
	"-iconic			start up already iconized",
	NULL
};

static XSizeHints sizehints = {
	PMinSize | PResizeInc | PBaseSize,
	0, 0, 80, 24,		/* x, y, width and height */
	1, 1,			/* Min width and height */
	0, 0,			/* Max width and height */
	1, 1,			/* Width and height increments */
	{0, 0}, {0, 0},		/* Aspect ratio - not used */
	2 * MARGIN, 2 * MARGIN,	/* base size */
	0
};

static int error_handler(Display *, XErrorEvent *);
static int io_error_handler(Display *);
static char *get_resource(const char *, const char *);
static void extract_resources(char *);
static void create_window(int, char **);

/*  Error handling function, tidu up and then exit.
 */
static int
error_handler(dpy, evp)
	Display *dpy;
	XErrorEvent *evp;
{
	quit(1);
	return (0);
}

int
io_error_handler(dpy)
	Display *dpy;
{
	return(error_handler(dpy, (XErrorEvent *) NULL));
}

/*  Open the display, initialise the rDB resources database and create the
 *  window.  If title is non null then it is used as the window and icon title.
 *  iargc and iargv are the original argc, argv so the can be written to a
 *  COMMAND resource.
 */
void
init_display(argc, argv, iargc, iargv, title)
	int argc, iargc;
	char **argv, **iargv;
	char *title;
{
	char str1[256], str2[256];
	char *display_name = NULL;
	XrmDatabase commandlineDB, serverDB;
	XrmValue value;
	XGCValues gcv;
	char *str_type;
	char *s;
	int i;

	XrmInitialize();
	xvt_name = iargv[0];

	/* If there is a name argument then we need to extract it by hand so
	 * that it can be used to add other command line options. */
	res_name = NULL;
	for (i = 1; i < argc; i++)
		if (strcmp(argv[i], "-name") == 0) {
			if (argv[++i] != NULL) {
				res_name = scopy(argv[i]);
				title = scopy(argv[i]);
			} else
				error("missing -name argument");
		}
	if (res_name == NULL)
		res_name = scopy(xvt_name);

	commandlineDB = NULL;
	XrmParseCommand(&commandlineDB, optable, OPTABLESIZE, res_name, &argc, argv);

	if (argc > 1)
		usage();

	/* See if there was a display named in the command line */
	sprintf(str1, "%s.display", res_name);
	sprintf(str2, "%s.Display", XVT_CLASS);
	if (XrmGetResource(commandlineDB, str1, str2, &str_type, &value) == True) {
		strncpy(str1, value.addr, (int) value.size);
		display_name = str1;
	}
	if ((display = XOpenDisplay(display_name)) == NULL) {
		error("can't open display %s", XDisplayName(display_name));
		quit(1);
	}
	/* Get the resources from the server if there are any. */
	if ((s = XResourceManagerString(display)) != NULL) {
		serverDB = XrmGetStringDatabase(s);
		XrmMergeDatabases(serverDB, &rDB);
	}
	XrmMergeDatabases(commandlineDB, &rDB);
	screen = DefaultScreen(display);
	visual = DefaultVisual(display, screen);
	colormap = DefaultColormap(display, screen);
	extract_resources(title);

#ifdef DEBUG_X
	XSynchronize(display, True), XSetErrorHandler(abort);
#else
	if (!debugging) {
		XSetErrorHandler(error_handler);
		XSetIOErrorHandler(io_error_handler);
	}
#endif

	create_window(iargc, iargv);

	/* Create the graphics contexts. */
	gcv.foreground = foreground;
	gcv.background = background;
	gcv.font = mainfont->fid;
	gc = XCreateGC(display, main_win, GCForeground | GCBackground | GCFont, &gcv);

	gcv.graphics_exposures = False;
	negc = XCreateGC(display, main_win,
	    GCForeground | GCBackground | GCGraphicsExposures, &gcv);

	gcv.foreground = foreground;
	gcv.background = background;
	sbgc = XCreateGC(display, main_win, GCForeground | GCBackground | GCFont, &gcv);

	gcv.function = GXinvert;
	gcv.plane_mask = foreground ^ background;
	hlgc = XCreateGC(display, main_win, GCFunction | GCPlaneMask, &gcv);

	/* initialise the screen data structures. */
	scr_init(save_lines);
	sbar_init();
}
/*  Extract the named resource from the database and return a pointer to a static
 *  string containing it.
 */
static char *
get_resource(name, class)
	const char *name, *class;
{
	static char resource[256];
	char str1[256], str2[256];
	XrmValue value;
	char *str_type;

	sprintf(str1, "%s.%s", res_name, name);
	sprintf(str2, "%s.%s", XVT_CLASS, class);
	if (XrmGetResource(rDB, str1, str2, &str_type, &value) == True) {
		strncpy(resource, value.addr, (int) value.size);
		return (resource);
	}
	/* The following is added for compatibility with xterm. */
	sprintf(str1, "%s.vt100.%s", res_name, name);
	sprintf(str2, "%s.VT100.%s", XVT_CLASS, class);
	if (XrmGetResource(rDB, str1, str2, &str_type, &value) == True) {
		strncpy(resource, value.addr, (int) value.size);
		return (resource);
	}
	return (NULL);
}
/*  Extract the resource fields that are needed to open the window.
 *  if title is non-NULL it is used as a window and icon title.
 */
static void
extract_resources(title)
	char *title;
{
	char *s;
	int x, y, width, height;
	int flags;
	XColor color;

	/* First get the font since we need it to set the size. */
	if ((s = get_resource("font", "Font")) == NULL)
		s = DEF_FONT;
	if ((mainfont = XLoadQueryFont(display, s)) == NULL) {
		error("can't access font %s\n", s);
		quit(1);
	}
	sizehints.width_inc = XTextWidth(mainfont, "M", 1);
	sizehints.height_inc = mainfont->ascent + mainfont->descent;

	/* Determine whether debugging is enabled. */
	if ((s = get_resource("debug", "Debug")) != NULL)
		debugging = strcmp(s, "on") == 0;

	/* Determine whether to allow messages. */
	if ((s = get_resource("messages", "Messages")) != NULL)
		messages = strcmp(s, "on") == 0;

	/* Determine whether to start up iconized. */
	if ((s = get_resource("iconic", "Iconic")) != NULL)
		iconic = strcmp(s, "on") == 0;

	/* Determine whether to display the scrollbar. */
	if ((s = get_resource("scrollBar", "ScrollBar")) != NULL)
		show_scrollbar = strcmp(s, "on") == 0;
	if (show_scrollbar)
		sizehints.base_width += SBAR_WIDTH;

	if ((s = get_resource("borderWidth", "BorderWidth")) != NULL)
		border_width = atoi(s);
	flags = 0;
	if ((s = get_resource("geometry", "Geometry")) != NULL)
		flags = XParseGeometry(s, &x, &y, &width, &height);

	if (flags & WidthValue) {
		sizehints.width = width;
		sizehints.flags |= USSize;
	}
	if (flags & HeightValue) {
		sizehints.height = height;
		sizehints.flags |= USSize;
	}
	sizehints.width = sizehints.width * sizehints.width_inc + sizehints.base_width;
	sizehints.height = sizehints.height * sizehints.height_inc + sizehints.base_height;
	sizehints.min_width = sizehints.width_inc + sizehints.base_width;
	sizehints.min_height = sizehints.height_inc + sizehints.base_height;
	if (flags & XValue) {
		if (flags & XNegative)
			x = DisplayWidth(display, screen) + x - sizehints.width - 2 * border_width;
		sizehints.x = x;
		sizehints.flags |= USPosition;
	}
	if (flags & YValue) {
		if (flags & YNegative)
			y = DisplayHeight(display, screen) + y - sizehints.height - 2 * border_width;
		sizehints.y = y;
		sizehints.flags |= USPosition;
	}
	if (flags & XNegative)
		sizehints.win_gravity = flags & YNegative
		    ? SouthEastGravity
		    : NorthEastGravity;
	else
		sizehints.win_gravity = flags & YNegative
		    ? SouthWestGravity
		    : NorthWestGravity;
	sizehints.flags |= PWinGravity;

	/* Do the foreground, background and border colours. */
	foreground = BlackPixel(display, screen);
	XParseColor(display, colormap, "black", &foreground_color);
	if ((s = get_resource("foreground", "Foreground")) != NULL) {
		if (XParseColor(display, colormap, s, &foreground_color) == 0)
			error("invalid foreground color %s", s);
		else if (XAllocColor(display, colormap, &foreground_color) == 0)
			error("can't allocate color %s", s);
		else
			foreground = foreground_color.pixel;
	}
	background = WhitePixel(display, screen);
	XParseColor(display, colormap, "white", &background_color);
	if ((s = get_resource("background", "Background")) != NULL) {
		if (XParseColor(display, colormap, s, &background_color) == 0)
			error("invalid background color %s", s);
		else if (XAllocColor(display, colormap, &background_color) == 0)
			error("can't allocate color %s", s);
		else
			background = background_color.pixel;
	}
	border = foreground;
	if ((s = get_resource("borderColor", "BorderColor")) != NULL) {
		if (XParseColor(display, colormap, s, &color) == 0)
			error("invalid border color %s", s);
		else if (XAllocColor(display, colormap, &color) == 0)
			error("can't allocate color %s", s);
		else
			border = color.pixel;
	}
	/* Get the window and icon names */
	icon_name = title;
	window_name = title;
	if ((s = get_resource("iconName", "IconName")) != NULL) {
		icon_name = scopy(s);
		window_name = scopy(s);
	}
	if ((s = get_resource("title", "Title")) != NULL)
		window_name = scopy(s);

	if (window_name == NULL)
		window_name = scopy(res_name);
	if (icon_name == NULL)
		icon_name = scopy(res_name);

	/* Extract the bold font if there is one. */
	if ((s = get_resource("boldFont", "BoldFont")) != NULL)
		if ((boldfont = XLoadQueryFont(display, s)) == NULL)
			error("can't access font %s\n", s);

	/* Get the character class. */
	if ((s = get_resource("charClass", "CharClass")) != NULL)
		scr_char_class(s);

	/* Get the reverse wrapround flag. */
	if ((s = get_resource("reverseWrap", "ReverseWrap")) != NULL)
		reverse_wrap = strcmp(s, "on") == 0;

	/* extract xvt specific arguments. */
	if ((s = get_resource("saveLines", "SaveLines")) != NULL)
		save_lines = atoi(s);
}
/*  Open and map the window.
 */
static void
create_window(argc, argv)
	int argc;
	char **argv;
{
	XTextProperty wname, iname;
	XClassHint class;
	XWMHints wmhints;
	Cursor cursor;

	main_win = XCreateSimpleWindow(display, DefaultRootWindow(display),
	    sizehints.x, sizehints.y, sizehints.width, sizehints.height,
	    border_width, foreground, background);

	printf("%ld\n", main_win);
	fflush(NULL);

	if (XStringListToTextProperty(&window_name, 1, &wname) == 0) {
		error("cannot allocate window name");
		quit(1);
	}
	if (XStringListToTextProperty(&icon_name, 1, &iname) == 0) {
		error("cannot allocate icon name");
		quit(1);
	}
	class.res_name = res_name;
	class.res_class = XVT_CLASS;
	wmhints.input = True;
	wmhints.initial_state = iconic ? IconicState : NormalState;
	wmhints.flags = InputHint | StateHint;
	XSetWMProperties(display, main_win, &wname, &iname, argv, argc,
	    &sizehints, &wmhints, &class);
	XSelectInput(display, main_win, MW_EVENTS);

	sb_win = XCreateSimpleWindow(display, main_win, -1, -1, SBAR_WIDTH - 1,
	    sizehints.height, 1, border, background);
	cursor = XCreateFontCursor(display, XC_sb_v_double_arrow);
	XRecolorCursor(display, cursor, &foreground_color, &background_color);
	XDefineCursor(display, sb_win, cursor);
	XSelectInput(display, sb_win, SB_EVENTS);

	vt_win = XCreateSimpleWindow(display, main_win, 0, 0, sizehints.width,
	    sizehints.height, 0, border, background);
	cursor = XCreateFontCursor(display, XC_xterm);
	XRecolorCursor(display, cursor, &foreground_color, &background_color);
	XDefineCursor(display, vt_win, cursor);
	XSelectInput(display, vt_win, VT_EVENTS);

	XMapWindow(display, vt_win);

	if (show_scrollbar) {
		XMoveWindow(display, vt_win, SBAR_WIDTH, 0);
		XResizeWindow(display, vt_win, sizehints.width - SBAR_WIDTH,
			sizehints.height);
		XMapWindow(display, sb_win);
	}
	XMapWindow(display, main_win);
}
/*  Redraw the whole window after an exposure or size change.
 */
void
resize_window()
{
	Window root;
	int x, y;
	unsigned int width, height, depth;

	XGetGeometry(display, main_win, &root, &x, &y, &width, &height,
		&border_width, &depth);
	if (show_scrollbar) {
		XResizeWindow(display, sb_win, SBAR_WIDTH - 1, height);
		XResizeWindow(display, vt_win, width - SBAR_WIDTH, height);
	} else
		XResizeWindow(display, vt_win, width, height);
}
/*  Toggle scrollbar.
 */
void
switch_scrollbar()
{
	Window root;
	int x, y;
	unsigned int width, height, depth;

	XGetGeometry(display, main_win, &root, &x, &y, &width, &height,
		&border_width, &depth);
	if (show_scrollbar) {
		XUnmapWindow(display, sb_win);
		XMoveWindow(display, vt_win, 0, 0);
		width -= SBAR_WIDTH;
		sizehints.base_width -= SBAR_WIDTH;
		sizehints.width = width;
		sizehints.height = height;
		sizehints.flags = USSize | PMinSize | PResizeInc | PBaseSize;
		XSetWMNormalHints(display, main_win, &sizehints);
		XResizeWindow(display, main_win, width, height);
		show_scrollbar = 0;
	} else {
		XMapWindow(display, sb_win);
		XMoveWindow(display, vt_win, SBAR_WIDTH, 0);
		width += SBAR_WIDTH;
		sizehints.base_width += SBAR_WIDTH;
		sizehints.width = width;
		sizehints.height = height;
		sizehints.flags = USSize | PMinSize | PResizeInc | PBaseSize;
		XSetWMNormalHints(display, main_win, &sizehints);
		XResizeWindow(display, main_win, width, height);
		show_scrollbar = 1;
	}
}
/*  Change the window name displayed in the title bar.
 */
void
change_window_name(str)
	char *str;
{
	XTextProperty name;

	if (XStringListToTextProperty(&str, 1, &name) == 0) {
		error("cannot allocate window name");
		return;
	}
	XSetWMName(display, main_win, &name);
	XFree(name.value);
}
/*  Change the icon name.
 */
void
change_icon_name(str)
	char *str;
{
	XTextProperty name;

	if (XStringListToTextProperty(&str, 1, &name) == 0) {
		error("cannot allocate icon name");
		return;
	}
	XSetWMIconName(display, main_win, &name);
	XFree(name.value);
}
/*  Print an error message.
 */
/*VARARGS1*/
void
error(const char *fmt,...)
{
	va_list args;

	va_start(args, fmt);
	fprintf(stderr, "%s: ", xvt_name);
	vfprintf(stderr, fmt, args);
	va_end(args);
	fprintf(stderr, "\n");
}
/*  Print out a usage message and exit.
 */
void
usage(void)
{
	int i;

/*	fprintf(stderr, "xvt: permitted arguments are:\n"); */
	for (i = 0; usearray[i] != NULL; i++)
		fprintf(stderr, "%s\n", usearray[i]);
	exit(1);
};


syntax highlighted by Code2HTML, v. 0.9.1