
/*
 *                 WARNING!
 * 
 * this file is `X11_c' and is preprocessed by m4 to produce `X11_m4.c'
 * 
 * It must be a valid m4 file, and must produce a valid C include file.
 * 
 */


#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>

DECL_BEFORE

/*
 * We need to write down only overloaded methods.
 */
 
#define XNUMCOL 16

static Display *dpy;
static Window root;
static int xwfont, xhfont, xupfont;
static int screen;
static int depth;
static Colormap colormap;
static unsigned long xcol[XNUMCOL];
static XFontStruct *xsfont;
static GC           xgc;
static XGCValues    xsgc;
static Atom         xWM_PROTOCOLS, xWM_DELETE_WINDOW, xTARGETS;
static XSizeHints   xhints;

static Tutf_function xUTF_16_to_charset;

#if 0
static ttbyte exitmainloop;
#endif

static ttfont X11_UTF_16_to_UTF_16(ttfont c) {
    if ((c & 0xFE00) == 0xF000)
	/* direct-to-font zone */
	c &= 0x01FF;
    return c;
}



/* this can stay static, X11_FlushHW() is not reentrant */
static ttcol _col;



#ifdef CONF__UNICODE
# define XDRAW(col, buf, buflen) \
    if (xsgc.foreground != xcol[COLFG(col)]) \
	XSetForeground(dpy, xgc, xsgc.foreground = xcol[COLFG(col)]); \
    if (xsgc.background != xcol[COLBG(col)]) \
	XSetBackground(dpy, xgc, xsgc.background = xcol[COLBG(col)]); \
    XDrawImageString16(dpy, w, xgc, xbegin, ybegin + xupfont, buf, buflen)
#else
# define XDRAW(col, buf, buflen) \
    if (xsgc.foreground != xcol[COLFG(col)]) \
	XSetForeground(dpy, xgc, xsgc.foreground = xcol[COLFG(col)]); \
    if (xsgc.background != xcol[COLBG(col)]) \
	XSetBackground(dpy, xgc, xsgc.background = xcol[COLBG(col)]); \
    XDrawImageString(dpy, w, xgc, xbegin, ybegin + xupfont, buf, buflen)
#endif

TT_INLINE void X11_Mogrify(Window w, ttshort x, ttshort y, ttattr *Text, ttattr *OldText, ttuint len, ttbyte ValidOldText) {
    ttattr *V, *oV;
    ttcol col;
    ttushort buflen = 0;
#ifdef CONF__UNICODE
    ttfont f;
    XChar2b buf[TT_SMALLBUFF];
#else
    ttbyte buf[TT_SMALLBUFF];
#endif
    int xbegin = x * xwfont, ybegin = y * xhfont;
    
    V = Text;
    oV = OldText;
    
    for (_col = ~HWCOL(*V); len; x++, V++, oV++, len--) {
	col = HWCOL(*V);
	if (buflen && (col != _col || (ValidOldText && *V == *oV) || buflen == TT_SMALLBUFF)) {
	    XDRAW(_col, buf, buflen);
	    buflen = 0;
	}
	if (!ValidOldText || *V != *oV) {
	    if (!buflen) {
		xbegin = x * xwfont;
		_col = col;
	    }
#ifdef CONF__UNICODE
	    f = xUTF_16_to_charset(HWFONT(*V));
	    buf[buflen  ].byte1 = f >> 8;
	    buf[buflen++].byte2 = f & 0xFF;
#else
	    buf[buflen++] = HWFONT(*V);
#endif
	}
    }
    if (buflen) {
	XDRAW(_col, buf, buflen);
	buflen = 0;
    }
}




/* ttobj */


/* ttobject */
static BREAK(ttobject) {
    if (o) {
	if (o->native) {
	    XDestroyWindow(dpy, (Window)o->native);
	    o->native = (opaque)0;
	}
    }
}

/* ttnative */
static ttnative DEF(GetRoot_ttnative)(void) {
    return Create_ttnative(root);
}
static BREAK(ttnative) {
    o->native = TT_NOID;
    FNDEFAULT(ttnative)->Break(o);
}


/* ttwidget */
static BUILD(ttwidget) {
    if ((o->native = (opaque)XCreateSimpleWindow
	 (dpy, root, o->x * xwfont, o->y * xhfont,
	  o->w * xwfont, o->h * xhfont, 0, 0, xcol[o->col]))) {
	
	return o;
    }
    return NULL;
}
static void DEF(AddTo_ttwidget)(ttwidget o, ttvisible parent, ttany constraints) {
    if (parent && !o->parent) {
	FNDEFAULT(ttwidget)->AddTo(o, parent, constraints);

	XReparentWindow(dpy, (Window)o->native, (Window)parent->native, o->x * xwfont, o->y * xhfont);
	
	if (o->vflags & ttvisible_vflags_visible)
	    XMapWindow(dpy, (Window)o->native);
    }
}
static void DEF(SetVisible_ttwidget)(ttwidget o, ttbyte on_off) {
    if (!on_off != !(o->vflags & ttvisible_vflags_visible)) {
	o->vflags ^= ttvisible_vflags_visible;
	if (o->parent && on_off)
	    XMapWindow(dpy, (Window)o->native);
	else
	    XUnmapWindow(dpy, (Window)o->native);
    }
}
static void DEF(Remove_ttwidget)(ttwidget o) {
    if (o->parent) {
	FNDEFAULT(ttwidget)->Remove(o);
	
	XUnmapWindow(dpy, (Window)o->native);
    }
}


/* ttframe */
static BUILD(ttframe) {
    /* ttframe are non-visible when created */
    o->vflags &= ~ttvisible_vflags_visible;
    
    if (FNDEFAULT(ttframe)->Build(o)) {
	
	XChangeProperty(dpy, (Window)o->native, xWM_PROTOCOLS,
			XA_ATOM, 32, PropModeReplace,
			(unsigned char *) &xWM_DELETE_WINDOW, 1);
	XSetWMNormalHints(dpy, (Window)o->native, &xhints);

	o->Class->AddTo(o, (ttvisible)TClass(ttnative)->GetRoot(), ttlayout_constraint_root);

	return o;
    }
    return NULL;
}







static ttbyte X11_FireEvent(XEvent *ev) {
    /* FIXME: finish this */
    return TRUE;
}

static ttbyte HWDEF(Sync)(void) {
    return XSync(dpy, False);
}
static ttbyte HWDEF(Flush)(void) {
    return XFlush(dpy);
}
static ttbyte HWDEF(TimidFlush)(void) {
    return XFlush(dpy);
}
#if 0
static ttbyte HWDEF(MainLoop)(void) {
    XEvent ev;

    while (!exitmainloop && XNextEvent(dpy, &ev)) {
	if (!X11_FireEvent(&ev))
	    break;
    }
    
    exitmainloop = FALSE;
    return 1;
}
static void HWDEF(ExitMainLoop)(void) {
    exitmainloop = TT_TRUE;
}
#endif
static ttbyte HWDEF(MainLoopOnce)(ttbyte wait) {
    XEvent ev;

    do {
	if (wait || XEventsQueued(dpy, QueuedAfterFlush)) {
	    XNextEvent(dpy, &ev);
	    X11_FireEvent(&ev);
	}
    } while (XEventsQueued(dpy, QueuedAlready));

    return 1;
}
static void HWDEF(DeleteCallback)(ttcallback o) {
}
static void HWDEF(Close)(void) {
    XCloseDisplay(dpy);
    dpy = NULL;
}
static int HWDEF(ConnectionFd)(void) {
  return XConnectionNumber(dpy);
}
static ttuint HWDEF(GetErrno)(void) {
  return 0;
}
static ttuint HWDEF(GetErrnoDetail)(void) {
  return 0;
}
static TT_CONST ttbyte *HWDEF(StrError)(ttuint E) {
    switch (E) {
    }
    return "";
}
static TT_CONST ttbyte *HWDEF(StrErrorDetail)(ttuint E, ttuint S) {
    return "";
}







#ifdef CONF__UNICODE
static Tutf_function X11_UTF_16_to_charset_function(TT_CONST ttbyte *charset) {
    XFontProp *fp;
    Atom fontatom;
    TT_CONST ttbyte *s, *fontname = NULL;
    ttuint i;
    
    if (!charset) {
	/* attempt to autodetect encoding. */
	fp = xsfont->properties;
	fontatom = XInternAtom (dpy, "FONT", False);
	i = xsfont->n_properties;
	
	while (i--) {
	    if (fp->name == fontatom) {
		fontname = XGetAtomName(dpy, fp->card32);
		/*fprintf(stderr, "    X11_UTF_16_to...: font name: `%s\'\n", fontname);*/
		break;
	    }
	    fp++;
	}
	if (fontname && !strcmp(fontname, "vga")) {
	    charset = T_NAME_CP437;
	} else if (fontname) {
	    i = 2;
	    for (s = fontname + strlen(fontname) - 1; i && s >= fontname; s--) {
		if (*s == '-')
		    i--;
	    }
	    if (!i)
		charset = s + 2; /* skip current char and '-' */
	}
	    
	if (!charset) {
	    if (xsfont->min_byte1 < xsfont->max_byte1) {
		/* font is more than just 8-bit. For now, assume it's unicode */
		/*
		printk("    _TT_X11_Init: font `%."TTSB"s\' has no known charset encoding,\n"
		       "                assuming Unicode.\n", fontname);
		 */
		return NULL;
	    }
	    /* else assume codepage437. gross. */
	    /*
	    printk("    _TT_X11_Init: font `%."TTSB"s\' has no known charset encoding,\n"
		   "                assuming CP437 codepage (\"VGA\").\n", fontname);
	     */
	    return Tutf_UTF_16_to_CP437;
	}
    }
    
    i = Tutf_charset_id(charset);
    s = Tutf_charset_name(i);
    if (s && !strcmp(s, T_NAME_UTF_16)) {
	/* this is an Unicode font. good. */
	return NULL;
    }
    
    if (i == (ttuint)-1) {
	/*
	printk("      X11_InitHW(): libTutf warning: unknown charset `%."TTSB"s', assuming `CP437'\n", charset);
	 */
	return Tutf_UTF_16_to_CP437;
    }
    
    return Tutf_UTF_16_to_charset_function(i);
}
#endif







#ifdef THIS_MODULE
ttclasses InitModule(tthw *HW)
#else
ttclasses _TT_X11_InitHW(tthw *HW)
#endif
{
    XColor xcolor;
    ttuint i;
    TT_CONST ttbyte *fontname = NULL, *charset = NULL;
    
    /*
     * (*HW) must be set even if initialization fails,
     * to report error messages.
     */
    *HW = &X11_TTClasses.HW;

    do if ((dpy = XOpenDisplay(NULL))) {
	root = DefaultRootWindow(dpy);
	screen = DefaultScreen(dpy);
	depth = DefaultDepth(dpy, screen);
	colormap = DefaultColormap(dpy, screen);
	
	for (i = 0; i < XNUMCOL; i++) {
	    xcolor.red   = 0x5555 * ((i & 4 ? 2 : 0) | (i & 8 ? 1 : 0));
	    xcolor.green = 0x5555 * ((i & 2 ? 2 : 0) | (i & 8 ? 1 : 0));
	    xcolor.blue  = 0x5555 * ((i & 1 ? 2 : 0) | (i & 8 ? 1 : 0));
	    if (!XAllocColor(dpy, colormap, &xcolor)) {
		
		break;
	    }
	    xcol[i] = xcolor.pixel;
	}
	if (i < XNUMCOL)
	    break;

	if (((fontname && (xsfont = XLoadQueryFont(dpy, fontname))) ||
	     (xsfont = XLoadQueryFont(dpy, "vga")) ||
	     (xsfont = XLoadQueryFont(dpy, "fixed")))) {
	    
	    
	    xwfont = xsfont->min_bounds.width;
	    xhfont = (xupfont = xsfont->ascent) + xsfont->descent;

	    xsgc.foreground = xsgc.background = xcol[0];
	    xsgc.graphics_exposures = False;
	    xsgc.font = xsfont->fid;
	    
	    if ((xgc = XCreateGC(dpy, root, GCFont|GCForeground|GCBackground|
				 GCGraphicsExposures, &xsgc))) {

		/*
		 * ask ICCCM-compliant window manager to tell us when close window
		 * has been chosen, rather than just killing us
		 */
		xWM_PROTOCOLS = XInternAtom(dpy, "WM_PROTOCOLS", False);
		xWM_DELETE_WINDOW = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
		xTARGETS = XInternAtom(dpy, "TARGETS", False);
		
		xhints.flags = PResizeInc;
		xhints.width_inc  = xwfont;
		xhints.height_inc = xhfont;
		
#ifdef CONF__UNICODE
		if (!(xUTF_16_to_charset = X11_UTF_16_to_charset_function(charset)))
		    xUTF_16_to_charset = X11_UTF_16_to_UTF_16;
#endif	    
		return &X11_TTClasses;
	    }
	}
    } while (0);
    
    if (dpy)
	X11_Close();
    
    /*FAIL(, ); */

    return (ttclasses)0;
}


DECL_AFTER

