/*
 * Copyright © 2006 Keith Packard
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that copyright
 * notice and this permission notice appear in supporting documentation, and
 * that the name of the copyright holders not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  The copyright holders make no representations
 * about the suitability of this software for any purpose.  It is provided "as
 * is" without express or implied warranty.
 *
 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 * OF THIS SOFTWARE.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <X11/Xlib.h>
/* we need to be able to manipulate the Display structure on events */
#include <X11/Xlibint.h>
#include <X11/extensions/render.h>
#include <X11/extensions/Xrender.h>
#include "Xrandrint.h"
#include <limits.h>

Atom *
XRRListOutputProperties (Display *dpy, RROutput output, int *nprop)
{
    XExtDisplayInfo		*info = XRRFindDisplay(dpy);
    xRRListOutputPropertiesReply rep;
    xRRListOutputPropertiesReq	*req;
    int				nbytes, rbytes;
    Atom			*props = NULL;

    RRCheckExtension (dpy, info, NULL);

    LockDisplay (dpy);
    GetReq (RRListOutputProperties, req);
    req->reqType = info->codes->major_opcode;
    req->randrReqType = X_RRListOutputProperties;
    req->output = output;

    if (!_XReply (dpy, (xReply *) &rep, 0, xFalse)) {
	UnlockDisplay (dpy);
	SyncHandle ();
	*nprop = 0;
	return NULL;
    }

    if (rep.nAtoms) {
	rbytes = rep.nAtoms * sizeof (Atom);
	nbytes = rep.nAtoms << 2;

	props = (Atom *) Xmalloc (rbytes);
	if (props == NULL) {
	    _XEatDataWords (dpy, rep.length);
	    UnlockDisplay (dpy);
	    SyncHandle ();
	    *nprop = 0;
	    return NULL;
	}

	_XRead32 (dpy, (long *) props, nbytes);
    }

    *nprop = rep.nAtoms;
    UnlockDisplay (dpy);
    SyncHandle ();
    return props;
}

XRRPropertyInfo *
XRRQueryOutputProperty (Display *dpy, RROutput output, Atom property)
{
    XExtDisplayInfo		*info = XRRFindDisplay(dpy);
    xRRQueryOutputPropertyReply rep;
    xRRQueryOutputPropertyReq	*req;
    unsigned int		rbytes, nbytes;
    XRRPropertyInfo		*prop_info;

    RRCheckExtension (dpy, info, NULL);

    LockDisplay (dpy);
    GetReq (RRQueryOutputProperty, req);
    req->reqType = info->codes->major_opcode;
    req->randrReqType = X_RRQueryOutputProperty;
    req->output = output;
    req->property = property;

    if (!_XReply (dpy, (xReply *) &rep, 0, xFalse)) {
	UnlockDisplay (dpy);
	SyncHandle ();
	return NULL;
    }

    if (rep.length < ((INT_MAX / sizeof(long)) - sizeof (XRRPropertyInfo))) {
        rbytes = sizeof (XRRPropertyInfo) + (rep.length * sizeof (long));
        nbytes = rep.length << 2;

        prop_info = Xmalloc (rbytes);
    } else
        prop_info = NULL;

    if (prop_info == NULL) {
	_XEatDataWords(dpy, rep.length);
	UnlockDisplay (dpy);
	SyncHandle ();
	return NULL;
    }

    prop_info->pending = rep.pending;
    prop_info->range = rep.range;
    prop_info->immutable = rep.immutable;
    prop_info->num_values = rep.length;
    if (rep.length != 0) {
	prop_info->values = (long *) (prop_info + 1);
	_XRead32 (dpy, prop_info->values, nbytes);
    } else {
	prop_info->values = NULL;
    }

    UnlockDisplay (dpy);
    SyncHandle ();
    return prop_info;
}

void
XRRConfigureOutputProperty (Display *dpy, RROutput output, Atom property,
			    Bool pending, Bool range, int num_values,
			    long *values)
{
    XExtDisplayInfo		    *info = XRRFindDisplay(dpy);
    xRRConfigureOutputPropertyReq   *req;
    long len;

    RRSimpleCheckExtension (dpy, info);

    LockDisplay(dpy);
    GetReq (RRConfigureOutputProperty, req);
    req->reqType = info->codes->major_opcode;
    req->randrReqType = X_RRConfigureOutputProperty;
    req->output = output;
    req->property = property;
    req->pending = pending;
    req->range = range;

    len = num_values;
    if (dpy->bigreq_size || req->length + len <= (unsigned) 65535) {
	SetReqLen(req, len, len);
	len = (long)num_values << 2;
	Data32 (dpy, values, len);
    } /* else force BadLength */

    UnlockDisplay(dpy);
    SyncHandle();
}

void
XRRChangeOutputProperty (Display *dpy, RROutput output,
			 Atom property, Atom type,
			 int format, int mode,
			 _Xconst unsigned char *data, int nelements)
{
    XExtDisplayInfo		*info = XRRFindDisplay(dpy);
    xRRChangeOutputPropertyReq	*req;
    long len;

    RRSimpleCheckExtension (dpy, info);

    LockDisplay(dpy);
    GetReq (RRChangeOutputProperty, req);
    req->reqType = info->codes->major_opcode;
    req->randrReqType = X_RRChangeOutputProperty;
    req->output = output;
    req->property = property;
    req->type = type;
    req->mode = mode;
    if (nelements < 0) {
	req->nUnits = 0;
	req->format = 0; /* ask for garbage, get garbage */
    } else {
	req->nUnits = nelements;
	req->format = format;
    }

    switch (req->format) {
    case 8:
	len = ((long)nelements + 3) >> 2;
	if (dpy->bigreq_size || req->length + len <= (unsigned) 65535) {
	    SetReqLen(req, len, len);
	    Data (dpy, (char *)data, nelements);
	} /* else force BadLength */
	break;

    case 16:
	len = ((long)nelements + 1) >> 1;
	if (dpy->bigreq_size || req->length + len <= (unsigned) 65535) {
	    SetReqLen(req, len, len);
	    len = (long)nelements << 1;
	    Data16 (dpy, (short *) data, len);
	} /* else force BadLength */
	break;

    case 32:
	len = nelements;
	if (dpy->bigreq_size || req->length + len <= (unsigned) 65535) {
	    SetReqLen(req, len, len);
	    len = (long)nelements << 2;
	    Data32 (dpy, (long *) data, len);
	} /* else force BadLength */
	break;

    default:
	/* BadValue will be generated */ ;
    }

    UnlockDisplay(dpy);
    SyncHandle();
}

void
XRRDeleteOutputProperty (Display *dpy, RROutput output, Atom property)
{
    XExtDisplayInfo		*info = XRRFindDisplay(dpy);
    xRRDeleteOutputPropertyReq *req;

    RRSimpleCheckExtension (dpy, info);

    LockDisplay(dpy);
    GetReq(RRDeleteOutputProperty, req);
    req->reqType = info->codes->major_opcode;
    req->randrReqType = X_RRDeleteOutputProperty;
    req->output = output;
    req->property = property;
    UnlockDisplay(dpy);
    SyncHandle();
}

int
XRRGetOutputProperty (Display *dpy, RROutput output,
		      Atom property, long offset, long length,
		      Bool delete, Bool pending, Atom req_type,
		      Atom *actual_type, int *actual_format,
		      unsigned long *nitems, unsigned long *bytes_after,
		      unsigned char **prop)
{
    XExtDisplayInfo		*info = XRRFindDisplay(dpy);
    xRRGetOutputPropertyReply	rep;
    xRRGetOutputPropertyReq	*req;
    unsigned long		nbytes, rbytes;

    /* Always initialize return values, in case callers fail to initialize
       them and fail to check the return code for an error. */
    *actual_type = None;
    *actual_format = 0;
    *nitems = *bytes_after = 0L;
    *prop = (unsigned char *) NULL;

    RRCheckExtension (dpy, info, 1);

    LockDisplay (dpy);
    GetReq (RRGetOutputProperty, req);
    req->reqType = info->codes->major_opcode;
    req->randrReqType = X_RRGetOutputProperty;
    req->output = output;
    req->property = property;
    req->type = req_type;
    req->longOffset = offset;
    req->longLength = length;
    req->delete = delete;
    req->pending = pending;

    if (!_XReply (dpy, (xReply *) &rep, 0, xFalse))
    {
	UnlockDisplay (dpy);
	SyncHandle ();
	return ((xError *)&rep)->errorCode;
    }

    if (rep.propertyType != None) {
	int format = rep.format;

	/*
	 * Protect against both integer overflow and just plain oversized
	 * memory allocation - no server should ever return this many props.
	 */
	if (rep.nItems >= (INT_MAX >> 4))
	    format = -1;        /* fall through to default error case */

	/*
	 * One extra byte is malloced than is needed to contain the property
	 * data, but this last byte is null terminated and convenient for
	 * returning string properties, so the client doesn't then have to
	 * recopy the string to make it null terminated.
	 */
	switch (format) {
	case 8:
	    nbytes = rep.nItems;
	    rbytes = rep.nItems + 1;
	    if (rbytes > 0 && (*prop = Xmalloc (rbytes)))
		_XReadPad (dpy, (char *) *prop, nbytes);
	    break;

	case 16:
	    nbytes = rep.nItems << 1;
	    rbytes = rep.nItems * sizeof (short) + 1;
	    if (rbytes > 0 && (*prop = Xmalloc (rbytes)))
		_XRead16Pad (dpy, (short *) *prop, nbytes);
	    break;

	case 32:
	    nbytes = rep.nItems << 2;
	    rbytes = rep.nItems * sizeof (long) + 1;
	    if (rbytes > 0 && (*prop = Xmalloc (rbytes)))
		_XRead32 (dpy, (long *) *prop, nbytes);
	    break;

	default:
	    /*
	     * This part of the code should never be reached.  If it is,
	     * the server sent back a property with an invalid format.
	     */
	    _XEatDataWords(dpy, rep.length);
	    UnlockDisplay(dpy);
	    SyncHandle();
	    return(BadImplementation);
	}
	if (! *prop) {
	    _XEatDataWords(dpy, rep.length);
	    UnlockDisplay(dpy);
	    SyncHandle();
	    return(BadAlloc);
	}
	(*prop)[rbytes - 1] = '\0';
    }

    *actual_type = rep.propertyType;
    *actual_format = rep.format;
    *nitems = rep.nItems;
    *bytes_after = rep.bytesAfter;
    UnlockDisplay (dpy);
    SyncHandle ();

    return Success;
}
