/*
 * Xephyr - A kdrive X server thats runs in a host X window.
 *          Authored by Matthew Allum <mallum@openedhand.com>
 * 
 * Copyright © 2007 OpenedHand Ltd 
 *
 * 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 OpenedHand Ltd not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission. OpenedHand Ltd makes no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * OpenedHand Ltd DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL OpenedHand Ltd 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.
 *
 * Authors:
 *    Dodji Seketeli <dodji@openedhand.com>
 */

#ifdef HAVE_CONFIG_H
#include <kdrive-config.h>
#endif
#include <string.h>
#include <X11/extensions/Xv.h>
#include "ephyrlog.h"
#include "kdrive.h"
#include "kxv.h"
#include "ephyr.h"
#include "hostx.h"
#include "ephyrhostvideo.h"

struct _EphyrXVPriv {
    EphyrHostXVAdaptorArray *host_adaptors ;
    KdVideoAdaptorPtr adaptors ;
    int num_adaptors ;
};
typedef struct _EphyrXVPriv EphyrXVPriv ;

struct _EphyrPortPriv {
    int port_number ;
    KdVideoAdaptorPtr current_adaptor ;
    EphyrXVPriv *xv_priv;
    unsigned char *image_buf ;
    int image_buf_size ;
    int image_id ;
    int drw_x, drw_y, drw_w, drw_h ;
    int src_x, src_y, src_w, src_h ;
    int image_width, image_height ;
};
typedef struct _EphyrPortPriv EphyrPortPriv ;

static Bool DoSimpleClip (BoxPtr a_dst_drw,
                          BoxPtr a_clipper,
                          BoxPtr a_result) ;

static Bool ephyrLocalAtomToHost (int a_local_atom, int *a_host_atom) ;

/*
static Bool ephyrHostAtomToLocal (int a_host_atom, int *a_local_atom) ;
*/

static EphyrXVPriv* ephyrXVPrivNew (void) ;
static void ephyrXVPrivDelete (EphyrXVPriv *a_this) ;
static Bool ephyrXVPrivQueryHostAdaptors (EphyrXVPriv *a_this) ;
static Bool ephyrXVPrivSetAdaptorsHooks (EphyrXVPriv *a_this) ;
static Bool ephyrXVPrivRegisterAdaptors (EphyrXVPriv *a_this,
                                         ScreenPtr a_screen) ;

static Bool ephyrXVPrivIsAttrValueValid (KdAttributePtr a_attrs,
                                         int a_attrs_len,
                                         const char *a_attr_name,
                                         int a_attr_value,
                                         Bool *a_is_valid) ;

static Bool ephyrXVPrivGetImageBufSize (int a_port_id,
                                        int a_image_id,
                                        unsigned short a_width,
                                        unsigned short a_height,
                                        int *a_size) ;

static Bool ephyrXVPrivSaveImageToPortPriv (EphyrPortPriv *a_port_priv,
                                            const unsigned char *a_image,
                                            int a_image_len) ;

static void ephyrStopVideo (KdScreenInfo *a_info,
                            pointer a_xv_priv,
                            Bool a_exit);

static int ephyrSetPortAttribute (KdScreenInfo *a_info,
                                  Atom a_attr_name,
                                  int a_attr_value,
                                  pointer a_port_priv);

static int ephyrGetPortAttribute (KdScreenInfo *a_screen_info,
                                  Atom a_attr_name,
                                  int *a_attr_value,
                                  pointer a_port_priv);

static void ephyrQueryBestSize (KdScreenInfo *a_info,
                                Bool a_motion,
                                short a_src_w,
                                short a_src_h,
                                short a_drw_w,
                                short a_drw_h,
                                unsigned int *a_prefered_w,
                                unsigned int *a_prefered_h,
                                pointer a_port_priv);

static int ephyrPutImage (KdScreenInfo *a_info,
                          DrawablePtr a_drawable,
                          short a_src_x,
                          short a_src_y,
                          short a_drw_x,
                          short a_drw_y,
                          short a_src_w,
                          short a_src_h,
                          short a_drw_w,
                          short a_drw_h,
                          int a_id,
                          unsigned char *a_buf,
                          short a_width,
                          short a_height,
                          Bool a_sync,
                          RegionPtr a_clipping_region,
                          pointer a_port_priv);

static int ephyrReputImage (KdScreenInfo *a_info,
                            DrawablePtr a_drawable,
                            short a_drw_x,
                            short a_drw_y,
                            RegionPtr a_clipping_region,
                            pointer a_port_priv) ;

static int ephyrPutVideo (KdScreenInfo *a_info,
                          DrawablePtr a_drawable,
                          short a_vid_x, short a_vid_y,
                          short a_drw_x, short a_drw_y,
                          short a_vid_w, short a_vid_h,
                          short a_drw_w, short a_drw_h,
                          RegionPtr a_clip_region,
                          pointer a_port_priv) ;

static int ephyrGetVideo (KdScreenInfo *a_info,
                          DrawablePtr a_drawable,
                          short a_vid_x, short a_vid_y,
                          short a_drw_x, short a_drw_y,
                          short a_vid_w, short a_vid_h,
                          short a_drw_w, short a_drw_h,
                          RegionPtr a_clip_region,
                          pointer a_port_priv) ;

static int ephyrPutStill (KdScreenInfo *a_info,
                          DrawablePtr a_drawable,
                          short a_vid_x, short a_vid_y,
                          short a_drw_x, short a_drw_y,
                          short a_vid_w, short a_vid_h,
                          short a_drw_w, short a_drw_h,
                          RegionPtr a_clip_region,
                          pointer a_port_priv) ;

static int ephyrGetStill (KdScreenInfo *a_info,
                          DrawablePtr a_drawable,
                          short a_vid_x, short a_vid_y,
                          short a_drw_x, short a_drw_y,
                          short a_vid_w, short a_vid_h,
                          short a_drw_w, short a_drw_h,
                          RegionPtr a_clip_region,
                          pointer a_port_priv) ;

static int ephyrQueryImageAttributes (KdScreenInfo *a_info,
                                      int a_id,
                                      unsigned short *a_w,
                                      unsigned short *a_h,
                                      int *a_pitches,
                                      int *a_offsets);
static int s_base_port_id ;

/**************
 * <helpers>
 * ************/

static Bool
DoSimpleClip (BoxPtr a_dst_box,
              BoxPtr a_clipper,
              BoxPtr a_result)
{
    BoxRec dstClippedBox ;

    EPHYR_RETURN_VAL_IF_FAIL (a_dst_box && a_clipper && a_result, FALSE) ;

    /*
     * setup the clipbox inside the destination.
     */
    dstClippedBox.x1 = a_dst_box->x1 ;
    dstClippedBox.x2 = a_dst_box->x2 ;
    dstClippedBox.y1 = a_dst_box->y1 ;
    dstClippedBox.y2 = a_dst_box->y2 ;

    /*
     * if the cliper leftmost edge is inside
     * the destination area then the leftmost edge of the resulting
     * clipped box is the leftmost edge of the cliper.
     */
    if (a_clipper->x1 > dstClippedBox.x1)
        dstClippedBox.x1 = a_clipper->x1 ;

    /*
     * if the cliper top edge is inside the destination area
     * then the bottom horizontal edge of the resulting clipped box
     * is the bottom edge of the cliper
     */
    if (a_clipper->y1 > dstClippedBox.y1)
        dstClippedBox.y1 = a_clipper->y1 ;

    /*ditto for right edge*/
    if (a_clipper->x2 < dstClippedBox.x2)
        dstClippedBox.x2 = a_clipper->x2 ;

    /*ditto for bottom edge*/
    if (a_clipper->y2 < dstClippedBox.y2)
        dstClippedBox.y2 = a_clipper->y2 ;

    memcpy (a_result, &dstClippedBox, sizeof (dstClippedBox)) ;
    return TRUE ;
}

static Bool
ephyrLocalAtomToHost (int a_local_atom, int *a_host_atom)
{
    const char *atom_name=NULL;
    int host_atom=None ;

    EPHYR_RETURN_VAL_IF_FAIL (a_host_atom, FALSE) ;

    if (!ValidAtom (a_local_atom))
        return FALSE ;

    atom_name = NameForAtom (a_local_atom) ;

    if (!atom_name)
        return FALSE ;

    if (!ephyrHostGetAtom (atom_name, FALSE, &host_atom) || host_atom == None) {
        EPHYR_LOG_ERROR ("no atom for string %s defined in host X\n",
                         atom_name) ;
        return FALSE ;
    }
    *a_host_atom = host_atom ;
    return TRUE ;
}

/*
 Not used yed.
static Bool
ephyrHostAtomToLocal (int a_host_atom, int *a_local_atom)
{
    Bool is_ok=FALSE ;
    char *atom_name=NULL ;
    int atom=None ;

    EPHYR_RETURN_VAL_IF_FAIL (a_local_atom, FALSE) ;

    atom_name = ephyrHostGetAtomName (a_host_atom) ;
    if (!atom_name)
        goto out ;

    atom = MakeAtom (atom_name, strlen (atom_name), TRUE) ;
    if (atom == None)
        goto out ;

    *a_local_atom = atom ;
    is_ok = TRUE ;

out:
    if (atom_name) {
        ephyrHostFree (atom_name) ;
    }
    return is_ok ;
}
*/

/**************
 *</helpers>
 * ************/

Bool
ephyrInitVideo (ScreenPtr pScreen)
{
    Bool is_ok = FALSE ;
    KdScreenPriv(pScreen);
    KdScreenInfo *screen = pScreenPriv->screen;
    static EphyrXVPriv *xv_priv;

    EPHYR_LOG ("enter\n") ;

    if (screen->fb.bitsPerPixel == 8) {
        EPHYR_LOG_ERROR ("8 bits depth not supported\n") ;
        return FALSE ;
    }

    if (!xv_priv) {
        xv_priv = ephyrXVPrivNew () ;
    }
    if (!xv_priv) {
        EPHYR_LOG_ERROR ("failed to create xv_priv\n") ;
        goto out ;
    }

    if (!ephyrXVPrivRegisterAdaptors (xv_priv, pScreen)) {
        EPHYR_LOG_ERROR ("failed to register adaptors\n") ;
        goto out ;
    }
    is_ok = TRUE ;

out:
    return is_ok ;
}

static EphyrXVPriv*
ephyrXVPrivNew (void)
{
    EphyrXVPriv *xv_priv=NULL ;

    EPHYR_LOG ("enter\n") ;

    xv_priv = xcalloc (1, sizeof (EphyrXVPriv)) ;
    if (!xv_priv) {
        EPHYR_LOG_ERROR ("failed to create EphyrXVPriv\n") ;
        goto error ;
    }

    ephyrHostXVInit () ;

    if (!ephyrXVPrivQueryHostAdaptors (xv_priv)) {
        EPHYR_LOG_ERROR ("failed to query the host x for xv properties\n") ;
        goto error ;
    }
    if (!ephyrXVPrivSetAdaptorsHooks (xv_priv)) {
        EPHYR_LOG_ERROR ("failed to set xv_priv hooks\n") ;
        goto error ;
    }

    EPHYR_LOG ("leave\n") ;
    return xv_priv ;

error:
    if (xv_priv) {
        ephyrXVPrivDelete (xv_priv) ;
        xv_priv = NULL ;
    }
    return NULL ;
}

static void
ephyrXVPrivDelete (EphyrXVPriv *a_this)
{
    EPHYR_LOG ("enter\n") ;

    if (!a_this)
        return ;
    if (a_this->host_adaptors) {
        ephyrHostXVAdaptorArrayDelete (a_this->host_adaptors) ;
        a_this->host_adaptors = NULL ;
    }
    xfree (a_this->adaptors) ;
    a_this->adaptors = NULL ;
    xfree (a_this) ;
    EPHYR_LOG ("leave\n") ;
}

static KdVideoEncodingPtr
videoEncodingDup (EphyrHostEncoding *a_encodings,
                   int a_num_encodings)
{
    KdVideoEncodingPtr result = NULL ;
    int i=0 ;

    EPHYR_RETURN_VAL_IF_FAIL (a_encodings && a_num_encodings, NULL) ;

    result = xcalloc (a_num_encodings, sizeof (KdVideoEncodingRec)) ;
    for (i=0 ; i < a_num_encodings; i++) {
        result[i].id = a_encodings[i].id ;
        result[i].name = strdup (a_encodings[i].name) ;
        result[i].width = a_encodings[i].width ;
        result[i].height = a_encodings[i].height ;
        result[i].rate.numerator = a_encodings[i].rate.numerator ;
        result[i].rate.denominator = a_encodings[i].rate.denominator ;
    }
    return result ;
}

static KdAttributePtr
portAttributesDup (EphyrHostAttribute *a_encodings,
                   int a_num_encodings)
{
    int i=0 ;
    KdAttributePtr result=NULL ;

    EPHYR_RETURN_VAL_IF_FAIL (a_encodings && a_num_encodings, NULL) ;

    result = xcalloc (a_num_encodings, sizeof (KdAttributeRec)) ;
    if (!result) {
        EPHYR_LOG_ERROR ("failed to allocate attributes\n") ;
        return NULL ;
    }
    for (i=0; i < a_num_encodings; i++) {
        result[i].flags = a_encodings[i].flags ;
        result[i].min_value = a_encodings[i].min_value ;
        result[i].max_value = a_encodings[i].max_value ;
        result[i].name = strdup (a_encodings[i].name) ;
    }
    return result ;
}

static Bool
ephyrXVPrivQueryHostAdaptors (EphyrXVPriv *a_this)
{
    EphyrHostXVAdaptor *cur_host_adaptor=NULL ;
    EphyrHostVideoFormat *video_formats=NULL ;
    EphyrHostEncoding *encodings=NULL ;
    EphyrHostAttribute *attributes=NULL ;
    EphyrHostImageFormat *image_formats=NULL ;
    int num_video_formats=0, base_port_id=0,
        num_attributes=0, num_formats=0, i=0,
        port_priv_offset=0;
    unsigned num_encodings=0 ;
    Bool is_ok = FALSE ;

    EPHYR_RETURN_VAL_IF_FAIL (a_this, FALSE) ;

    EPHYR_LOG ("enter\n") ;

    if (!ephyrHostXVQueryAdaptors (&a_this->host_adaptors)) {
        EPHYR_LOG_ERROR ("failed to query host adaptors\n") ;
        goto out ;
    }
    if (a_this->host_adaptors)
        a_this->num_adaptors =
                    ephyrHostXVAdaptorArrayGetSize (a_this->host_adaptors) ;
    if (a_this->num_adaptors < 0) {
        EPHYR_LOG_ERROR ("failed to get number of host adaptors\n") ;
        goto out ;
    }
    EPHYR_LOG ("host has %d adaptors\n", a_this->num_adaptors) ;
    /*
     * copy what we can from adaptors into a_this->adaptors
     */
    if (a_this->num_adaptors) {
        a_this->adaptors = xcalloc (a_this->num_adaptors,
                                    sizeof (KdVideoAdaptorRec)) ;
        if (!a_this->adaptors) {
            EPHYR_LOG_ERROR ("failed to create internal adaptors\n") ;
            goto out ;
        }
    }
    for (i=0; i < a_this->num_adaptors; i++) {
        int j=0 ;
        cur_host_adaptor =
                   ephyrHostXVAdaptorArrayAt (a_this->host_adaptors, i) ;
        if (!cur_host_adaptor)
            continue ;
        a_this->adaptors[i].nPorts =
                            ephyrHostXVAdaptorGetNbPorts (cur_host_adaptor) ;
        if (a_this->adaptors[i].nPorts <=0) {
            EPHYR_LOG_ERROR ("Could not find any port of adaptor %d\n", i) ;
            continue ;
        }
        a_this->adaptors[i].type =
                        ephyrHostXVAdaptorGetType (cur_host_adaptor) ;
        a_this->adaptors[i].type |= XvWindowMask ;
        a_this->adaptors[i].flags =
                        VIDEO_OVERLAID_IMAGES | VIDEO_CLIP_TO_VIEWPORT;
        if (ephyrHostXVAdaptorGetName (cur_host_adaptor))
            a_this->adaptors[i].name =
                    strdup (ephyrHostXVAdaptorGetName (cur_host_adaptor)) ;
        else
            a_this->adaptors[i].name = strdup ("Xephyr Video Overlay");
        base_port_id = ephyrHostXVAdaptorGetFirstPortID (cur_host_adaptor) ;
        if (base_port_id < 0) {
            EPHYR_LOG_ERROR ("failed to get port id for adaptor %d\n", i) ;
            continue ;
        }
        if (!s_base_port_id)
            s_base_port_id = base_port_id ;

        if (!ephyrHostXVQueryEncodings (base_port_id,
                                        &encodings,
                                        &num_encodings)) {
            EPHYR_LOG_ERROR ("failed to get encodings for port port id %d,"
                             " adaptors %d\n",
                             base_port_id, i) ;
            continue ;
        }
        a_this->adaptors[i].nEncodings = num_encodings ;
        a_this->adaptors[i].pEncodings =
                            videoEncodingDup (encodings, num_encodings) ;
        video_formats = (EphyrHostVideoFormat*)
            ephyrHostXVAdaptorGetVideoFormats (cur_host_adaptor,
                                               &num_video_formats);
        a_this->adaptors[i].pFormats = (KdVideoFormatPtr) video_formats ;
        a_this->adaptors[i].nFormats = num_video_formats ;
        /* got a_this->adaptors[i].nPorts already
        a_this->adaptors[i].nPorts =
                            ephyrHostXVAdaptorGetNbPorts (cur_host_adaptor) ;
        */
        a_this->adaptors[i].pPortPrivates =
                xcalloc (a_this->adaptors[i].nPorts,
                         sizeof (DevUnion) + sizeof (EphyrPortPriv)) ;
        port_priv_offset = a_this->adaptors[i].nPorts;
        for (j=0; j < a_this->adaptors[i].nPorts; j++) {
            EphyrPortPriv *port_privs_base =
                    (EphyrPortPriv*)&a_this->adaptors[i].pPortPrivates[port_priv_offset];
            EphyrPortPriv *port_priv = &port_privs_base[j] ;
            port_priv->port_number = base_port_id + j;
            port_priv->current_adaptor = &a_this->adaptors[i] ;
            port_priv->xv_priv = a_this ;
            a_this->adaptors[i].pPortPrivates[j].ptr = port_priv;
        }
        if (!ephyrHostXVQueryPortAttributes (base_port_id,
                                             &attributes,
                                             &num_attributes)) {
            EPHYR_LOG_ERROR ("failed to get port attribute "
                             "for adaptor %d\n", i) ;
            continue ;
        }
        a_this->adaptors[i].pAttributes =
                    portAttributesDup (attributes, num_attributes);
        a_this->adaptors[i].nAttributes = num_attributes ;
        /*make sure atoms of attrs names are created in xephyr*/
        for (j=0; j < a_this->adaptors[i].nAttributes; j++) {
            if (a_this->adaptors[i].pAttributes[j].name)
                MakeAtom (a_this->adaptors[i].pAttributes[j].name,
                          strlen (a_this->adaptors[i].pAttributes[j].name),
                          TRUE) ;
        }
        if (!ephyrHostXVQueryImageFormats (base_port_id,
                                           &image_formats,
                                           &num_formats)) {
            EPHYR_LOG_ERROR ("failed to get image formats "
                             "for adaptor %d\n", i) ;
            continue ;
        }
        a_this->adaptors[i].pImages = (KdImagePtr) image_formats ;
        a_this->adaptors[i].nImages = num_formats ;
    }
    is_ok = TRUE ;

out:
    if (encodings) {
        ephyrHostEncodingsDelete (encodings, num_encodings) ;
        encodings = NULL ;
    }
    if (attributes) {
        ephyrHostAttributesDelete (attributes) ;
        attributes = NULL ;
    }
    EPHYR_LOG ("leave\n") ;
    return is_ok ;
}

static Bool
ephyrXVPrivSetAdaptorsHooks (EphyrXVPriv *a_this)
{
    int i=0 ;
    Bool has_it=FALSE ;
    EphyrHostXVAdaptor *cur_host_adaptor=NULL ;

    EPHYR_RETURN_VAL_IF_FAIL (a_this, FALSE) ;

    EPHYR_LOG ("enter\n") ;

    for (i=0; i < a_this->num_adaptors; i++) {
        a_this->adaptors[i].ReputImage = ephyrReputImage ;
        a_this->adaptors[i].StopVideo = ephyrStopVideo ;
        a_this->adaptors[i].SetPortAttribute = ephyrSetPortAttribute ;
        a_this->adaptors[i].GetPortAttribute = ephyrGetPortAttribute ;
        a_this->adaptors[i].QueryBestSize = ephyrQueryBestSize ;
        a_this->adaptors[i].QueryImageAttributes = ephyrQueryImageAttributes ;

        cur_host_adaptor =
                   ephyrHostXVAdaptorArrayAt (a_this->host_adaptors, i) ;
        if (!cur_host_adaptor) {
            EPHYR_LOG_ERROR ("failed to get host adaptor at index %d\n", i) ;
            continue ;
        }
        has_it = FALSE ;
        if (!ephyrHostXVAdaptorHasPutImage (cur_host_adaptor, &has_it)) {
            EPHYR_LOG_ERROR ("error\n") ;
        }
        if (has_it) {
            a_this->adaptors[i].PutImage = ephyrPutImage;
        }

        has_it = FALSE ;
        if (!ephyrHostXVAdaptorHasPutVideo (cur_host_adaptor, &has_it)) {
            EPHYR_LOG_ERROR ("error\n") ;
        }
        if (has_it) {
            a_this->adaptors[i].PutVideo = ephyrPutVideo;
        }

        has_it = FALSE ;
        if (!ephyrHostXVAdaptorHasGetVideo (cur_host_adaptor, &has_it)) {
            EPHYR_LOG_ERROR ("error\n") ;
        }
        if (has_it) {
            a_this->adaptors[i].GetVideo = ephyrGetVideo;
        }

        has_it = FALSE ;
        if (!ephyrHostXVAdaptorHasPutStill (cur_host_adaptor, &has_it)) {
            EPHYR_LOG_ERROR ("error\n") ;
        }
        if (has_it) {
            a_this->adaptors[i].PutStill = ephyrPutStill;
        }

        has_it = FALSE ;
        if (!ephyrHostXVAdaptorHasGetStill (cur_host_adaptor, &has_it)) {
            EPHYR_LOG_ERROR ("error\n") ;
        }
        if (has_it) {
            a_this->adaptors[i].GetStill = ephyrGetStill;
        }
    }
    EPHYR_LOG ("leave\n") ;
    return TRUE ;
}

static Bool
ephyrXVPrivRegisterAdaptors (EphyrXVPriv *a_this,
                             ScreenPtr a_screen)
{
    KdScreenPriv(a_screen);
    KdScreenInfo *screen = pScreenPriv->screen;
    Bool is_ok = FALSE ;
    KdVideoAdaptorPtr *adaptors=NULL, *registered_adaptors=NULL ;
    int num_registered_adaptors=0, i=0, num_adaptors=0 ;

    EPHYR_RETURN_VAL_IF_FAIL (a_this && a_screen, FALSE) ;

    EPHYR_LOG ("enter\n") ;

    if (!a_this->num_adaptors)
        goto out ;
    num_registered_adaptors =
                KdXVListGenericAdaptors (screen, &registered_adaptors);

    num_adaptors = num_registered_adaptors + a_this->num_adaptors ;
    adaptors = xcalloc (num_adaptors, sizeof (KdVideoAdaptorPtr)) ;
    if (!adaptors) {
        EPHYR_LOG_ERROR ("failed to allocate adaptors tab\n") ;
        goto out ;
    }
    memmove (adaptors, registered_adaptors, num_registered_adaptors) ;
    for (i=0 ; i < a_this->num_adaptors; i++) {
        *(adaptors + num_registered_adaptors + i) = &a_this->adaptors[i] ;
    }
    if (!KdXVScreenInit (a_screen, adaptors, num_adaptors)) {
        EPHYR_LOG_ERROR ("failed to register adaptors\n");
        goto out ;
    }
    EPHYR_LOG ("there are  %d registered adaptors\n", num_adaptors) ;
    is_ok = TRUE ;

out:
    xfree (registered_adaptors) ;
    registered_adaptors = NULL ;
    xfree (adaptors) ;
    adaptors = NULL ;

    EPHYR_LOG ("leave\n") ;
    return is_ok ;
}

static Bool
ephyrXVPrivIsAttrValueValid (KdAttributePtr a_attrs,
                             int a_attrs_len,
                             const char *a_attr_name,
                             int a_attr_value,
                             Bool *a_is_valid)
{
    int i=0 ;

    EPHYR_RETURN_VAL_IF_FAIL (a_attrs && a_attr_name && a_is_valid,
                              FALSE) ;

    for (i=0; i < a_attrs_len; i++) {
        if (a_attrs[i].name && strcmp (a_attrs[i].name, a_attr_name))
            continue ;
        if (a_attrs[i].min_value > a_attr_value ||
            a_attrs[i].max_value < a_attr_value) {
            *a_is_valid = FALSE ;
            EPHYR_LOG_ERROR ("attribute was not valid\n"
                             "value:%d. min:%d. max:%d\n",
                             a_attr_value,
                             a_attrs[i].min_value,
                             a_attrs[i].max_value) ;
        } else {
            *a_is_valid = TRUE ;
        }
        return TRUE ;
    }
    return FALSE ;
}

static Bool
ephyrXVPrivGetImageBufSize (int a_port_id,
                            int a_image_id,
                            unsigned short a_width,
                            unsigned short a_height,
                            int *a_size)
{
    Bool is_ok=FALSE ;
    unsigned short width=a_width, height=a_height ;

    EPHYR_RETURN_VAL_IF_FAIL (a_size, FALSE) ;

    EPHYR_LOG ("enter\n") ;

    if (!ephyrHostXVQueryImageAttributes (a_port_id, a_image_id,
                                          &width, &height, a_size, NULL, NULL)) {
        EPHYR_LOG_ERROR ("failed to get image attributes\n") ;
        goto out ;
    }
    is_ok = TRUE ;

out:
    EPHYR_LOG ("leave\n") ;
    return is_ok ;
}

static Bool
ephyrXVPrivSaveImageToPortPriv (EphyrPortPriv *a_port_priv,
                                const unsigned char *a_image_buf,
                                int a_image_len)
{
    Bool is_ok=FALSE ;

    EPHYR_LOG ("enter\n") ;

    if (a_port_priv->image_buf_size < a_image_len) {
        unsigned char *buf=NULL ;
        buf = realloc (a_port_priv->image_buf, a_image_len) ;
        if (!buf) {
            EPHYR_LOG_ERROR ("failed to realloc image buffer\n") ;
            goto out ;
        }
        a_port_priv->image_buf = buf ;
        a_port_priv->image_buf_size = a_image_len;
    }
    memmove (a_port_priv->image_buf, a_image_buf, a_image_len) ;
    is_ok = TRUE ;

out:
    return is_ok ;
    EPHYR_LOG ("leave\n") ;
}

static void
ephyrStopVideo (KdScreenInfo *a_info, pointer a_port_priv, Bool a_exit)
{
    EphyrPortPriv *port_priv = a_port_priv ;

    EPHYR_RETURN_IF_FAIL (a_info && a_info->pScreen) ;
    EPHYR_RETURN_IF_FAIL (port_priv) ;

    EPHYR_LOG ("enter\n") ;
    if (!ephyrHostXVStopVideo (a_info->pScreen->myNum,
                               port_priv->port_number)) {
        EPHYR_LOG_ERROR ("XvStopVideo() failed\n") ;
    }
    EPHYR_LOG ("leave\n") ;
}

static int
ephyrSetPortAttribute (KdScreenInfo *a_info,
                       Atom a_attr_name,
                       int a_attr_value,
                       pointer a_port_priv)
{
    int res=Success, host_atom=0 ;
    EphyrPortPriv *port_priv = a_port_priv ;
    Bool is_attr_valid=FALSE ;

    EPHYR_RETURN_VAL_IF_FAIL (port_priv, BadMatch) ;
    EPHYR_RETURN_VAL_IF_FAIL (port_priv->current_adaptor, BadMatch) ;
    EPHYR_RETURN_VAL_IF_FAIL (port_priv->current_adaptor->pAttributes,
                              BadMatch) ;
    EPHYR_RETURN_VAL_IF_FAIL (port_priv->current_adaptor->nAttributes,
                              BadMatch) ;
    EPHYR_RETURN_VAL_IF_FAIL (ValidAtom (a_attr_name), BadMatch) ;

    EPHYR_LOG ("enter, portnum:%d, atomid:%d, attr_name:%s, attr_val:%d\n",
               port_priv->port_number,
               (int)a_attr_name,
               NameForAtom (a_attr_name),
               a_attr_value) ;

    if (!ephyrLocalAtomToHost (a_attr_name, &host_atom)) {
        EPHYR_LOG_ERROR ("failed to convert local atom to host atom\n") ;
        res = BadMatch ;
        goto out ;
    }

    if (!ephyrXVPrivIsAttrValueValid (port_priv->current_adaptor->pAttributes,
                                      port_priv->current_adaptor->nAttributes,
                                      NameForAtom (a_attr_name),
                                      a_attr_value,
                                      &is_attr_valid)) {
        EPHYR_LOG_ERROR ("failed to validate attribute %s\n",
                         NameForAtom (a_attr_name)) ;
        /*
         res = BadMatch ;
         goto out ;
         */
    }
    if (!is_attr_valid) {
        EPHYR_LOG_ERROR ("attribute %s is not valid\n",
                         NameForAtom (a_attr_name)) ;
        /*
        res = BadMatch ;
        goto out ;
        */
    }

    if (!ephyrHostXVSetPortAttribute (port_priv->port_number,
                                      host_atom,
                                      a_attr_value)) {
        EPHYR_LOG_ERROR ("failed to set port attribute\n") ;
        res = BadMatch ;
        goto out ;
    }

    res = Success ;
out:
    EPHYR_LOG ("leave\n") ;
    return res ;
}

static int
ephyrGetPortAttribute (KdScreenInfo *a_screen_info,
                       Atom a_attr_name,
                       int *a_attr_value,
                       pointer a_port_priv)
{
    int res=Success, host_atom=0 ;
    EphyrPortPriv *port_priv = a_port_priv ;

    EPHYR_RETURN_VAL_IF_FAIL (port_priv, BadMatch) ;
    EPHYR_RETURN_VAL_IF_FAIL (ValidAtom (a_attr_name), BadMatch) ;

    EPHYR_LOG ("enter, portnum:%d, atomid:%d, attr_name:%s\n",
               port_priv->port_number,
               (int)a_attr_name,
               NameForAtom (a_attr_name)) ;

    if (!ephyrLocalAtomToHost (a_attr_name, &host_atom)) {
        EPHYR_LOG_ERROR ("failed to convert local atom to host atom\n") ;
        res = BadMatch ;
        goto out ;
    }

    if (!ephyrHostXVGetPortAttribute (port_priv->port_number,
                                      host_atom,
                                      a_attr_value)) {
        EPHYR_LOG_ERROR ("failed to get port attribute\n") ;
        res = BadMatch ;
        goto out ;
    }

    res = Success ;
out:
    EPHYR_LOG ("leave\n") ;
    return res ;
}

static void
ephyrQueryBestSize (KdScreenInfo *a_info,
                    Bool a_motion,
                    short a_src_w,
                    short a_src_h,
                    short a_drw_w,
                    short a_drw_h,
                    unsigned int *a_prefered_w,
                    unsigned int *a_prefered_h,
                    pointer a_port_priv)
{
    int res=0 ;
    EphyrPortPriv *port_priv = a_port_priv ;

    EPHYR_RETURN_IF_FAIL (port_priv) ;

    EPHYR_LOG ("enter\n") ;
    res = ephyrHostXVQueryBestSize (port_priv->port_number,
                                    a_motion,
                                    a_src_w, a_src_h,
                                    a_drw_w, a_drw_h,
                                    a_prefered_w, a_prefered_h) ;
    if (!res) {
        EPHYR_LOG_ERROR ("Failed to query best size\n") ;
    }
    EPHYR_LOG ("leave\n") ;
}

static int
ephyrPutImage (KdScreenInfo *a_info,
               DrawablePtr a_drawable,
               short a_src_x,
               short a_src_y,
               short a_drw_x,
               short a_drw_y,
               short a_src_w,
               short a_src_h,
               short a_drw_w,
               short a_drw_h,
               int a_id,
               unsigned char *a_buf,
               short a_width,
               short a_height,
               Bool a_sync,
               RegionPtr a_clipping_region,
               pointer a_port_priv)
{
    EphyrPortPriv *port_priv = a_port_priv ;
    Bool is_ok=FALSE ;
    int result=BadImplementation, image_size=0 ;

    EPHYR_RETURN_VAL_IF_FAIL (a_info && a_info->pScreen, BadValue) ;
    EPHYR_RETURN_VAL_IF_FAIL (a_drawable, BadValue) ;

    EPHYR_LOG ("enter\n") ;

    if (!ephyrHostXVPutImage (a_info->pScreen->myNum,
                              port_priv->port_number,
                              a_id,
                              a_drw_x, a_drw_y, a_drw_w, a_drw_h,
                              a_src_x, a_src_y, a_src_w, a_src_h,
                              a_width, a_height, a_buf,
                              (EphyrHostBox*)REGION_RECTS (a_clipping_region),
                              REGION_NUM_RECTS (a_clipping_region))) {
        EPHYR_LOG_ERROR ("EphyrHostXVPutImage() failed\n") ;
        goto out ;
    }

    /*
     * Now save the image so that we can resend it to host it
     * later, in ReputImage.
     */
    if (!ephyrXVPrivGetImageBufSize (port_priv->port_number,
                                     a_id, a_width, a_height, &image_size)) {
        EPHYR_LOG_ERROR ("failed to get image size\n") ;
        /*this is a minor error so we won't get bail out abruptly*/
        is_ok = FALSE ;
    } else {
        is_ok = TRUE ;
    }
    if (is_ok) {
        if (!ephyrXVPrivSaveImageToPortPriv (port_priv, a_buf, image_size)) {
            is_ok=FALSE ;
        } else {
            port_priv->image_id = a_id;
            port_priv->drw_x = a_drw_x;
            port_priv->drw_y = a_drw_y;
            port_priv->drw_w = a_drw_w ;
            port_priv->drw_h = a_drw_h ;
            port_priv->src_x = a_src_x;
            port_priv->src_y = a_src_y ;
            port_priv->src_w = a_src_w ;
            port_priv->src_h = a_src_h ;
            port_priv->image_width = a_width ;
            port_priv->image_height = a_height ;
        }
    }
    if (!is_ok) {
        if (port_priv->image_buf) {
            free (port_priv->image_buf) ;
            port_priv->image_buf = NULL ;
            port_priv->image_buf_size = 0 ;
        }
    }

    result = Success ;

out:
    EPHYR_LOG ("leave\n") ;
    return result ;
}

static int
ephyrReputImage (KdScreenInfo *a_info,
                 DrawablePtr a_drawable,
                 short a_drw_x,
                 short a_drw_y,
                 RegionPtr a_clipping_region,
                 pointer a_port_priv)
{
    EphyrPortPriv *port_priv = a_port_priv ;
    int result=BadImplementation ;

    EPHYR_RETURN_VAL_IF_FAIL (a_info->pScreen, FALSE) ;
    EPHYR_RETURN_VAL_IF_FAIL (a_drawable && port_priv, BadValue) ;

    EPHYR_LOG ("enter\n") ;

    if (!port_priv->image_buf_size || !port_priv->image_buf) {
        EPHYR_LOG_ERROR ("has null image buf in cache\n") ;
        goto out ;
    }
    if (!ephyrHostXVPutImage (a_info->pScreen->myNum,
                              port_priv->port_number,
                              port_priv->image_id,
                              a_drw_x, a_drw_y,
                              port_priv->drw_w, port_priv->drw_h,
                              port_priv->src_x, port_priv->src_y,
                              port_priv->src_w, port_priv->src_h,
                              port_priv->image_width, port_priv->image_height,
                              port_priv->image_buf,
                              (EphyrHostBox*)REGION_RECTS (a_clipping_region),
                              REGION_NUM_RECTS (a_clipping_region))) {
        EPHYR_LOG_ERROR ("ephyrHostXVPutImage() failed\n") ;
        goto out ;
    }

    result = Success ;

out:
    EPHYR_LOG ("leave\n") ;
    return result ;
}

static int
ephyrPutVideo (KdScreenInfo *a_info,
               DrawablePtr a_drawable,
               short a_vid_x, short a_vid_y,
               short a_drw_x, short a_drw_y,
               short a_vid_w, short a_vid_h,
               short a_drw_w, short a_drw_h,
               RegionPtr a_clipping_region,
               pointer a_port_priv)
{
    EphyrPortPriv *port_priv = a_port_priv ;
    BoxRec clipped_area, dst_box ;
    int result=BadImplementation ;
    int drw_x=0, drw_y=0, drw_w=0, drw_h=0 ;

    EPHYR_RETURN_VAL_IF_FAIL (a_info->pScreen, BadValue) ;
    EPHYR_RETURN_VAL_IF_FAIL (a_drawable && port_priv, BadValue) ;

    EPHYR_LOG ("enter\n") ;

    dst_box.x1 = a_drw_x ;
    dst_box.x2 = a_drw_x + a_drw_w;
    dst_box.y1 = a_drw_y ;
    dst_box.y2 = a_drw_y + a_drw_h;

    if (!DoSimpleClip (&dst_box,
                       REGION_EXTENTS (pScreen->pScreen, a_clipping_region),
                       &clipped_area)) {
        EPHYR_LOG_ERROR ("failed to simple clip\n") ;
        goto out ;
    }

    drw_x = clipped_area.x1 ;
    drw_y = clipped_area.y1 ;
    drw_w = clipped_area.x2 - clipped_area.x1 ;
    drw_h = clipped_area.y2 - clipped_area.y1 ;

    if (!ephyrHostXVPutVideo (a_info->pScreen->myNum,
                              port_priv->port_number,
                              a_vid_x, a_vid_y, a_vid_w, a_vid_h,
                              a_drw_x, a_drw_y, a_drw_w, a_drw_h)) {
        EPHYR_LOG_ERROR ("ephyrHostXVPutVideo() failed\n") ;
        goto out ;
    }
    result = Success ;

out:
    EPHYR_LOG ("leave\n") ;
    return result ;
}

static int
ephyrGetVideo (KdScreenInfo *a_info,
               DrawablePtr a_drawable,
               short a_vid_x, short a_vid_y,
               short a_drw_x, short a_drw_y,
               short a_vid_w, short a_vid_h,
               short a_drw_w, short a_drw_h,
               RegionPtr a_clipping_region,
               pointer a_port_priv)
{
    EphyrPortPriv *port_priv = a_port_priv ;
    BoxRec clipped_area, dst_box ;
    int result=BadImplementation ;
    int drw_x=0, drw_y=0, drw_w=0, drw_h=0 ;

    EPHYR_RETURN_VAL_IF_FAIL (a_info && a_info->pScreen, BadValue) ;
    EPHYR_RETURN_VAL_IF_FAIL (a_drawable && port_priv, BadValue) ;

    EPHYR_LOG ("enter\n") ;

    dst_box.x1 = a_drw_x ;
    dst_box.x2 = a_drw_x + a_drw_w;
    dst_box.y1 = a_drw_y ;
    dst_box.y2 = a_drw_y + a_drw_h;

    if (!DoSimpleClip (&dst_box,
                       REGION_EXTENTS (pScreen->pScreen, a_clipping_region),
                       &clipped_area)) {
        EPHYR_LOG_ERROR ("failed to simple clip\n") ;
        goto out ;
    }

    drw_x = clipped_area.x1 ;
    drw_y = clipped_area.y1 ;
    drw_w = clipped_area.x2 - clipped_area.x1 ;
    drw_h = clipped_area.y2 - clipped_area.y1 ;

    if (!ephyrHostXVGetVideo (a_info->pScreen->myNum,
                              port_priv->port_number,
                              a_vid_x, a_vid_y, a_vid_w, a_vid_h,
                              a_drw_x, a_drw_y, a_drw_w, a_drw_h)) {
        EPHYR_LOG_ERROR ("ephyrHostXVGetVideo() failed\n") ;
        goto out ;
    }
    result = Success ;

out:
    EPHYR_LOG ("leave\n") ;
    return result ;
}

static int
ephyrPutStill (KdScreenInfo *a_info,
               DrawablePtr a_drawable,
               short a_vid_x, short a_vid_y,
               short a_drw_x, short a_drw_y,
               short a_vid_w, short a_vid_h,
               short a_drw_w, short a_drw_h,
               RegionPtr a_clipping_region,
               pointer a_port_priv)
{
    EphyrPortPriv *port_priv = a_port_priv ;
    BoxRec clipped_area, dst_box ;
    int result=BadImplementation ;
    int drw_x=0, drw_y=0, drw_w=0, drw_h=0 ;

    EPHYR_RETURN_VAL_IF_FAIL (a_info && a_info->pScreen, BadValue) ;
    EPHYR_RETURN_VAL_IF_FAIL (a_drawable && port_priv, BadValue) ;

    EPHYR_LOG ("enter\n") ;

    dst_box.x1 = a_drw_x ;
    dst_box.x2 = a_drw_x + a_drw_w;
    dst_box.y1 = a_drw_y ;
    dst_box.y2 = a_drw_y + a_drw_h;

    if (!DoSimpleClip (&dst_box,
                       REGION_EXTENTS (pScreen->pScreen, a_clipping_region),
                       &clipped_area)) {
        EPHYR_LOG_ERROR ("failed to simple clip\n") ;
        goto out ;
    }

    drw_x = clipped_area.x1 ;
    drw_y = clipped_area.y1 ;
    drw_w = clipped_area.x2 - clipped_area.x1 ;
    drw_h = clipped_area.y2 - clipped_area.y1 ;

    if (!ephyrHostXVPutStill (a_info->pScreen->myNum,
                              port_priv->port_number,
                              a_vid_x, a_vid_y, a_vid_w, a_vid_h,
                              a_drw_x, a_drw_y, a_drw_w, a_drw_h)) {
        EPHYR_LOG_ERROR ("ephyrHostXVPutStill() failed\n") ;
        goto out ;
    }
    result = Success ;

out:
    EPHYR_LOG ("leave\n") ;
    return result ;
}

static int
ephyrGetStill (KdScreenInfo *a_info,
               DrawablePtr a_drawable,
               short a_vid_x, short a_vid_y,
               short a_drw_x, short a_drw_y,
               short a_vid_w, short a_vid_h,
               short a_drw_w, short a_drw_h,
               RegionPtr a_clipping_region,
               pointer a_port_priv)
{
    EphyrPortPriv *port_priv = a_port_priv ;
    BoxRec clipped_area, dst_box ;
    int result=BadImplementation ;
    int drw_x=0, drw_y=0, drw_w=0, drw_h=0 ;

    EPHYR_RETURN_VAL_IF_FAIL (a_info && a_info->pScreen, BadValue) ;
    EPHYR_RETURN_VAL_IF_FAIL (a_drawable && port_priv, BadValue) ;

    EPHYR_LOG ("enter\n") ;

    dst_box.x1 = a_drw_x ;
    dst_box.x2 = a_drw_x + a_drw_w;
    dst_box.y1 = a_drw_y ;
    dst_box.y2 = a_drw_y + a_drw_h;

    if (!DoSimpleClip (&dst_box,
                       REGION_EXTENTS (pScreen->pScreen, a_clipping_region),
                       &clipped_area)) {
        EPHYR_LOG_ERROR ("failed to simple clip\n") ;
        goto out ;
    }

    drw_x = clipped_area.x1 ;
    drw_y = clipped_area.y1 ;
    drw_w = clipped_area.x2 - clipped_area.x1 ;
    drw_h = clipped_area.y2 - clipped_area.y1 ;

    if (!ephyrHostXVGetStill (a_info->pScreen->myNum,
                              port_priv->port_number,
                              a_vid_x, a_vid_y, a_vid_w, a_vid_h,
                              a_drw_x, a_drw_y, a_drw_w, a_drw_h)) {
        EPHYR_LOG_ERROR ("ephyrHostXVGetStill() failed\n") ;
        goto out ;
    }
    result = Success ;

out:
    EPHYR_LOG ("leave\n") ;
    return result ;
}

static int
ephyrQueryImageAttributes (KdScreenInfo *a_info,
                           int a_id,
                           unsigned short *a_w,
                           unsigned short *a_h,
                           int *a_pitches,
                           int *a_offsets)
{
    int image_size=0 ;

    EPHYR_RETURN_VAL_IF_FAIL (a_w && a_h, FALSE) ;

    EPHYR_LOG ("enter: dim (%dx%d), pitches: %p, offsets: %p\n",
               *a_w, *a_h, a_pitches, a_offsets) ;

   if (!ephyrHostXVQueryImageAttributes (s_base_port_id,
                                         a_id,
                                         a_w, a_h,
                                         &image_size,
                                         a_pitches, a_offsets)) {
       EPHYR_LOG_ERROR ("EphyrHostXVQueryImageAttributes() failed\n") ;
       goto out ;
   }
   EPHYR_LOG ("image size: %d, dim (%dx%d)\n", image_size, *a_w, *a_h) ;

out:
    EPHYR_LOG ("leave\n") ;
    return image_size ;
}