/* Copyright (C) 2003 Jamey Sharp.
 * This file is licensed under the MIT license. See the file COPYING. */

#include "Xlibint.h"
#include "xclint.h"
#include <X11/Xatom.h>
#include <X11/Xresource.h>
#include <stdio.h>

void _XFreeDisplayStructure(Display *dpy);

static XCBAuthInfo xauth;

static void *alloc_copy(const void *src, size_t *dstn, size_t n)
{
	void *dst;
	if(n <= 0)
	{
		*dstn = 0;
		return 0;
	}
	dst = Xmalloc(n);
	if(!dst)
		return 0;
	memcpy(dst, src, n);
	*dstn = n;
	return dst;
}

XCBConnection *XCBConnectionOfDisplay(Display *dpy)
{
	return dpy->xcl->connection;
}

void XSetAuthorization(char *name, int namelen, char *data, int datalen)
{
	_XLockMutex(_Xglobal_lock);
	Xfree(xauth.name);
	Xfree(xauth.data);

	/* if either of these allocs fail, _XConnectXCB won't use this auth
	 * data, so we don't need to check it here. */
	xauth.name = alloc_copy(name, &xauth.namelen, namelen);
	xauth.data = alloc_copy(data, &xauth.datalen, datalen);

#if 0 /* but, for the paranoid among us: */
	if((namelen > 0 && !xauth.name) || (datalen > 0 && !xauth.data))
	{
		Xfree(xauth.name);
		Xfree(xauth.data);
		xauth.name = xauth.data = 0;
		xauth.namelen = xauth.datalen = 0;
	}
#endif

	_XUnlockMutex(_Xglobal_lock);
}

int _XConnectXCB(Display *dpy, _Xconst char *display, char **fullnamep, int *screenp)
{
	char *host;
	int n = 0;
	int len;
	XCBConnection *c;

	dpy->fd = -1;

	dpy->xcl = Xcalloc(1, sizeof(XCLPrivate));
	if(!dpy->xcl)
		return 0;

	if(!XCBParseDisplay(display, &host, &n, screenp))
		return 0;

	len = strlen(host) + (1 + 20 + 1 + 20 + 1);
	*fullnamep = Xmalloc(len);
	snprintf(*fullnamep, len, "%s:%d.%d", host, n, *screenp);
	free(host);

	_XLockMutex(_Xglobal_lock);
	if(xauth.name && xauth.data)
		c = XCBConnectToDisplayWithAuthInfo(display, &xauth, 0);
	else
		c = XCBConnect(display, 0);
	_XUnlockMutex(_Xglobal_lock);

	if(!c)
		return 0;

	dpy->fd = XCBGetFileDescriptor(c);

	dpy->xcl->connection = c;
	dpy->xcl->pending_requests_tail = &dpy->xcl->pending_requests;
	return 1;
}

static int init_pixmap_formats(Display *dpy, XCBConnection *c)
{
	int i;
	ScreenFormat *fmtdst;
	XCBFORMAT *fmtsrc;
	dpy->nformats = XCBConnSetupSuccessRepPixmapFormatsLength(XCBGetSetup(c));

	/* Now iterate down setup information... */
	fmtdst = Xmalloc(dpy->nformats * sizeof(ScreenFormat));
	if(!fmtdst)
		return 0;
	dpy->pixmap_format = fmtdst;
	fmtsrc = XCBConnSetupSuccessRepPixmapFormats(XCBGetSetup(c));

	/* First decode the Z axis Screen format information. */
	for(i = dpy->nformats; i; --i, ++fmtsrc, ++fmtdst)
	{
		fmtdst->depth = fmtsrc->depth;
		fmtdst->bits_per_pixel = fmtsrc->bits_per_pixel;
		fmtdst->scanline_pad = fmtsrc->scanline_pad;
		fmtdst->ext_data = NULL;
	}
	return 1;
}

static int init_visuals(int len, XCBVISUALTYPE *vpsrc, Visual **dst)
{
	Visual *vpdst;

	*dst = vpdst = Xmalloc(len * sizeof(Visual));
	if(!vpdst)
		return 0;

	for(; len; --len, ++vpsrc, ++vpdst)
	{
		vpdst->visualid		= vpsrc->visual_id.id;
		vpdst->class		= vpsrc->_class;
		vpdst->bits_per_rgb	= vpsrc->bits_per_rgb_value;
		vpdst->map_entries	= vpsrc->colormap_entries;
		vpdst->red_mask		= vpsrc->red_mask;
		vpdst->green_mask	= vpsrc->green_mask;
		vpdst->blue_mask	= vpsrc->blue_mask;
		vpdst->ext_data		= NULL;
	}
	return 1;
}

static int init_depths(XCBDEPTHIter dpsrc, Depth **dst, CARD8 root_depth)
{
	Depth *dpdst;

	*dst = dpdst = Xmalloc(dpsrc.rem * sizeof(Depth));
	if(!dpdst)
		return 0;

	/* for all depths on this screen. */
	for(; dpsrc.rem; XCBDEPTHNext(&dpsrc), ++dpdst)
	{
		dpdst->depth = dpsrc.data->depth;

		if (dpsrc.data->depth != root_depth && getenv ("XLIB_SKIP_EXTRA_VISUALS"))
		{
			dpdst->nvisuals = 0;
			dpdst->visuals = 0;
		}
		else
		{
			dpdst->nvisuals	= XCBDEPTHVisualsLength(dpsrc.data);
			if(!init_visuals(dpdst->nvisuals, XCBDEPTHVisuals(dpsrc.data), &dpdst->visuals))
				return 0;
		}
	}
	return 1;
}

static int init_screens(Display *dpy, XCBConnection *c)
{
	Screen *spdst;
	XCBSCREENIter spsrc = XCBConnSetupSuccessRepRootsIter(XCBGetSetup(c));

	dpy->nscreens = spsrc.rem;

	spdst = Xmalloc(spsrc.rem * sizeof(Screen));
	if(!spdst)
		return 0;
	dpy->screens = spdst;

	/* Now go deal with each screen structure. */
	for(; spsrc.rem; XCBSCREENNext(&spsrc), ++spdst)
	{
		spdst->display		= dpy;
		spdst->root 		= spsrc.data->root.xid;
		spdst->cmap 		= spsrc.data->default_colormap.xid;
		spdst->white_pixel	= spsrc.data->white_pixel;
		spdst->black_pixel	= spsrc.data->black_pixel;
		spdst->root_input_mask	= spsrc.data->current_input_masks;
		spdst->width		= spsrc.data->width_in_pixels;
		spdst->height		= spsrc.data->height_in_pixels;
		spdst->mwidth		= spsrc.data->width_in_millimeters;
		spdst->mheight		= spsrc.data->height_in_millimeters;
		spdst->min_maps		= spsrc.data->min_installed_maps;
		spdst->max_maps		= spsrc.data->max_installed_maps;
		spdst->backing_store	= spsrc.data->backing_stores;
		spdst->save_unders	= spsrc.data->save_unders;
		spdst->root_depth	= spsrc.data->root_depth;
		spdst->ndepths		= spsrc.data->allowed_depths_len;
		spdst->ext_data		= NULL;

		if(!init_depths(XCBSCREENAllowedDepthsIter(spsrc.data), &spdst->depths, spsrc.data->root_depth))
			return 0;

		spdst->root_visual = _XVIDtoVisual(dpy, spsrc.data->root_visual.id);
	}
	return 1;
}

int _XConnectSetupXCB(Display *dpy)
{
	XCBConnection *c = dpy->xcl->connection;
	XCBConnSetupSuccessRep *setup = XCBGetSetup(c);

	dpy->proto_major_version	= setup->protocol_major_version;
	dpy->proto_minor_version	= setup->protocol_minor_version;
	dpy->release 			= setup->release_number;
	dpy->resource_base		= setup->resource_id_base;
	dpy->resource_mask		= setup->resource_id_mask;
	dpy->min_keycode		= setup->min_keycode.id;
	dpy->max_keycode		= setup->max_keycode.id;
	dpy->motion_buffer		= setup->motion_buffer_size;
	dpy->byte_order			= setup->image_byte_order;
	dpy->bitmap_unit		= setup->bitmap_format_scanline_unit;
	dpy->bitmap_pad			= setup->bitmap_format_scanline_pad;
	dpy->bitmap_bit_order		= setup->bitmap_format_bit_order;
	dpy->max_request_size		= setup->maximum_request_length;
	dpy->resource_shift		= 0;

	{
	    unsigned long mask;
	    for (mask = dpy->resource_mask; !(mask & 1); mask >>= 1)
		++dpy->resource_shift;
	}
	dpy->resource_max = (dpy->resource_mask >> dpy->resource_shift) - 5;

	{
		int len = XCBConnSetupSuccessRepVendorLength(setup);
		dpy->vendor = Xmalloc(len + 1);
		if(!dpy->vendor)
			return 0;
		memcpy(dpy->vendor, XCBConnSetupSuccessRepVendor(setup), len);
		dpy->vendor[len] = '\0';
	}

	if(!init_pixmap_formats(dpy, c))
		return 0;

	if(!init_screens(dpy, c))
		return 0;

	dpy->bigreq_size = XCBGetMaximumRequestLength(c);
	if(dpy->bigreq_size <= dpy->max_request_size)
		dpy->bigreq_size = 0;

	return 1;
}
