#include "ecore_xcb_private.h"

/**
 * @defgroup Ecore_X_Fixes_Group X Fixes Extension Functions
 *
 * Functions related to the X Fixes extension.
 */

#ifdef ECORE_XCB_FIXES
static int _xfixes_available = 0;
static xcb_xfixes_query_version_cookie_t _ecore_xcb_xfixes_init_cookie;
#endif /* ECORE_XCB_FIXES */

/* To avoid round trips, the initialization is separated in 2
   functions: _ecore_xcb_xfixes_init and
   _ecore_xcb_xfixes_init_finalize. The first one gets the cookies and
   the second one gets the replies and set the atoms. */

void
_ecore_x_xfixes_init(const xcb_query_extension_reply_t *reply)
{
#ifdef ECORE_XCB_FIXES
   if (reply && (reply->present))
      _ecore_xcb_xfixes_init_cookie = xcb_xfixes_query_version_unchecked(_ecore_xcb_conn, 4, 0);

#endif /* ECORE_XCB_FIXES */
} /* _ecore_x_xfixes_init */

void
_ecore_x_xfixes_init_finalize(void)
{
#ifdef ECORE_XCB_FIXES
   xcb_xfixes_query_version_reply_t *reply;

   reply = xcb_xfixes_query_version_reply(_ecore_xcb_conn,
                                          _ecore_xcb_xfixes_init_cookie, NULL);

   if (reply)
     {
        if (reply->major_version >= 3)
           _xfixes_available = 1;

        free(reply);
     }

#endif /* ECORE_XCB_FIXES */
} /* _ecore_x_xfixes_init_finalize */

/**
 * Return whether the X server supports the Fixes Extension.
 * @return 1 if the X Fixes Extension is available, 0 otherwise.
 *
 * Return 1 if the X server supports the Fixes Extension version 3.0,
 * 0 otherwise.
 * @ingroup Ecore_X_Fixes_Group
 */
EAPI int
ecore_x_xfixes_query(void)
{
#ifdef ECORE_XCB_FIXES
   return _xfixes_available;
#else /* ifdef ECORE_XCB_FIXES */
   return 0;
#endif /* ECORE_XCB_FIXES */
} /* ecore_x_xfixes_query */

/**
 * Create a region from rectangles.
 * @param rects The rectangles used to initialize the region.
 * @param num   The number of rectangles.
 * @return      The newly created region.
 *
 * Create a region initialized to the specified list of rectangles
 * @p rects. The rectangles may be specified in any order, their union
 * becomes the region.
 * @ingroup Ecore_X_Fixes_Group
 */
EAPI Ecore_X_Region
ecore_x_region_new(Ecore_X_Rectangle *rects,
                   int                num)
{
   Ecore_X_Region region = XCB_NONE;

#ifdef ECORE_XCB_FIXES
   region = xcb_generate_id(_ecore_xcb_conn);
   xcb_xfixes_create_region(_ecore_xcb_conn, region, num, (xcb_rectangle_t *)rects);
#endif /* ECORE_XCB_FIXES */

   return region;
} /* ecore_x_region_new */

/**
 * Create a region from a pixmap.
 * @param bitmap The bitmap used to initialize the region.
 * @return       The newly created region.
 *
 * Creates a region initialized to the set of 'one' pixels in @p bitmap
 * (which must be of depth 1, else Match error).
 * @ingroup Ecore_X_Fixes_Group
 */
EAPI Ecore_X_Region
ecore_x_region_new_from_bitmap(Ecore_X_Pixmap bitmap)
{
   Ecore_X_Region region = XCB_NONE;

#ifdef ECORE_XCB_FIXES
   region = xcb_generate_id(_ecore_xcb_conn);
   xcb_xfixes_create_region_from_bitmap(_ecore_xcb_conn, region, bitmap);
#endif /* ECORE_XCB_FIXES */

   return region;
} /* ecore_x_region_new_from_bitmap */

/**
 * Create a region from a window.
 * @param window The window used to initialize the region.
 * @param type   The type of the region.
 * @return       The newly created region.
 *
 * Creates a region initialized to the specified @p window region. See
 * the Shape extension for the definition of Bounding and Clip
 * regions.
 * @ingroup Ecore_X_Fixes_Group
 */
EAPI Ecore_X_Region
ecore_x_region_new_from_window(Ecore_X_Window      window,
                               Ecore_X_Region_Type type)
{
   Ecore_X_Region region = XCB_NONE;

#ifdef ECORE_XCB_FIXES
   region = xcb_generate_id(_ecore_xcb_conn);
   xcb_xfixes_create_region_from_window(_ecore_xcb_conn, region, window, type);
#endif /* ECORE_XCB_FIXES */

   return region;
} /* ecore_x_region_new_from_window */

/**
 * Create a region from a graphic context.
 * @param gc The graphic context used to initialize the region.
 * @return   The newly created region.
 *
 * Creates a region initialized from the clip list of @p gc.
 * @ingroup Ecore_X_Fixes_Group
 */
EAPI Ecore_X_Region
ecore_x_region_new_from_gc(Ecore_X_GC gc)
{
   Ecore_X_Region region = XCB_NONE;

#ifdef ECORE_XCB_FIXES
   region = xcb_generate_id(_ecore_xcb_conn);
   xcb_xfixes_create_region_from_gc(_ecore_xcb_conn, region, gc);
#endif /* ECORE_XCB_FIXES */

   return region;
} /* ecore_x_region_new_from_gc */

/**
 * Create a region from a picture.
 * @param picture The picture used to initialize the region.
 * @return        The newly created region.
 *
 * Creates a region initialized from the clip list of @p picture.
 * @ingroup Ecore_X_Fixes_Group
 */
EAPI Ecore_X_Region
ecore_x_region_new_from_picture(Ecore_X_Picture picture)
{
   Ecore_X_Region region = XCB_NONE;

#ifdef ECORE_XCB_FIXES
   region = xcb_generate_id(_ecore_xcb_conn);
   xcb_xfixes_create_region_from_picture(_ecore_xcb_conn, region, picture);
#endif /* ECORE_XCB_FIXES */

   return region;
} /* ecore_x_region_new_from_picture */

/**
 * Destroy a region.
 * @param region The region to destroy.
 *
 * Destroy the specified @p region.
 * @ingroup Ecore_X_Fixes_Group
 */
EAPI void
ecore_x_region_free(Ecore_X_Region region)
{
#ifdef ECORE_XCB_FIXES
   xcb_xfixes_destroy_region(_ecore_xcb_conn, region);
#endif /* ECORE_XCB_FIXES */
} /* ecore_x_region_free */

/**
 * Set the content of a region.
 * @param region The region to destroy.
 * @param rects  The rectangles used to set the region.
 * @param num    The number of rectangles.
 *
 * Replace the current contents of @p region with the region formed
 * by the union of the rectangles @p rects.
 * @ingroup Ecore_X_Fixes_Group
 */
EAPI void
ecore_x_region_set(Ecore_X_Region     region,
                   Ecore_X_Rectangle *rects,
                   int                num)
{
#ifdef ECORE_XCB_FIXES
   xcb_xfixes_set_region(_ecore_xcb_conn, region, num, (xcb_rectangle_t *)rects);
#endif /* ECORE_XCB_FIXES */
} /* ecore_x_region_set */

/**
 * Copy the content of a region.
 * @param dest   The destination region.
 * @param source The source region.
 *
 * Replace the contents of @p dest with the contents of @p source.
 * @ingroup Ecore_X_Fixes_Group
 */
EAPI void
ecore_x_region_copy(Ecore_X_Region dest,
                    Ecore_X_Region source)
{
#ifdef ECORE_XCB_FIXES
   xcb_xfixes_copy_region(_ecore_xcb_conn, source, dest);
#endif /* ECORE_XCB_FIXES */
} /* ecore_x_region_copy */

/**
 * Make the union of two regions.
 * @param dest    The destination region.
 * @param source1 The first source region.
 * @param source2 The second source region.
 *
 * Replace the contents of @p dest with the union of @p source1 and
 * @p source2.
 * @ingroup Ecore_X_Fixes_Group
 */
EAPI void
ecore_x_region_combine(Ecore_X_Region dest,
                       Ecore_X_Region source1,
                       Ecore_X_Region source2)
{
#ifdef ECORE_XCB_FIXES
   xcb_xfixes_union_region(_ecore_xcb_conn, source1, source2, dest);
#endif /* ECORE_XCB_FIXES */
} /* ecore_x_region_combine */

/**
 * Make the intersection of two regions.
 * @param dest    The destination region.
 * @param source1 The first source region.
 * @param source2 The second source region.
 *
 * Replace the contents of @p dest with the intersection of @p source1 and
 * @p source2.
 * @ingroup Ecore_X_Fixes_Group
 */
EAPI void
ecore_x_region_intersect(Ecore_X_Region dest,
                         Ecore_X_Region source1,
                         Ecore_X_Region source2)
{
#ifdef ECORE_XCB_FIXES
   xcb_xfixes_intersect_region(_ecore_xcb_conn, source1, source2, dest);
#endif /* ECORE_XCB_FIXES */
} /* ecore_x_region_intersect */

/**
 * Make the subtraction of two regions.
 * @param dest    The destination region.
 * @param source1 The first source region.
 * @param source2 The second source region.
 *
 * Replace the contents of @p dest with the subtraction of @p source1 by
 * @p source2.
 * @ingroup Ecore_X_Fixes_Group
 */
EAPI void
ecore_x_region_subtract(Ecore_X_Region dest,
                        Ecore_X_Region source1,
                        Ecore_X_Region source2)
{
#ifdef ECORE_XCB_FIXES
   xcb_xfixes_subtract_region(_ecore_xcb_conn, source1, source2, dest);
#endif /* ECORE_XCB_FIXES */
} /* ecore_x_region_subtract */

/**
 * Make the subtraction of regions by bounds.
 * @param dest   The destination region.
 * @param bounds The bounds.
 * @param source The source region.
 *
 * The @p source region is subtracted from the region specified by
 * @p bounds.  The result is placed in @p dest, replacing its
 * contents.
 * @ingroup Ecore_X_Fixes_Group
 */
EAPI void
ecore_x_region_invert(Ecore_X_Region     dest,
                      Ecore_X_Rectangle *bounds,
                      Ecore_X_Region     source)
{
#ifdef ECORE_XCB_FIXES
   xcb_rectangle_t rect;

   rect.x = bounds->x;
   rect.y = bounds->y;
   rect.width = bounds->width;
   rect.height = bounds->height;
   xcb_xfixes_invert_region(_ecore_xcb_conn, source, rect, dest);
#endif /* ECORE_XCB_FIXES */
} /* ecore_x_region_invert */

/**
 * Translate a region.
 * @param region The region to translate.
 * @param dx     The horizontal translation.
 * @param dy     The vertical translation.
 *
 * The @p region is translated by @p dx and @p dy in place.
 * @ingroup Ecore_X_Fixes_Group
 */
EAPI void
ecore_x_region_translate(Ecore_X_Region region,
                         int            dx,
                         int            dy)
{
#ifdef ECORE_XCB_FIXES
   xcb_xfixes_translate_region(_ecore_xcb_conn, region, dx, dy);
#endif /* ECORE_XCB_FIXES */
} /* ecore_x_region_translate */

/**
 * Extent a region.
 * @param dest   The destination region.
 * @param source The source region.
 *
 * The extents of the @p source region are placed in @p dest.
 * @ingroup Ecore_X_Fixes_Group
 */
EAPI void
ecore_x_region_extents(Ecore_X_Region dest,
                       Ecore_X_Region source)
{
#ifdef ECORE_XCB_FIXES
   xcb_xfixes_region_extents(_ecore_xcb_conn, source, dest);
#endif /* ECORE_XCB_FIXES */
} /* ecore_x_region_extents */

/**
 * Sends the XFixesFetchRegion request.
 * @param region Requested region.
 * @ingroup Ecore_X_Fixes_Group
 */
EAPI void
ecore_x_region_fetch_prefetch(Ecore_X_Region region)
{
#ifdef ECORE_XCB_FIXES
   xcb_xfixes_fetch_region_cookie_t cookie;

   cookie = xcb_xfixes_fetch_region_unchecked(_ecore_xcb_conn, region);
   _ecore_xcb_cookie_cache(cookie.sequence);
#endif /* ECORE_XCB_FIXES */
} /* ecore_x_region_fetch_prefetch */

/**
 * Gets the reply of the XFixesFetchRegion request sent by ecore_xcb_region_fetch_prefetch().
 * @ingroup Ecore_X_Fixes_Group
 */
EAPI void
ecore_x_region_fetch_fetch(void)
{
#ifdef ECORE_XCB_FIXES
   xcb_xfixes_fetch_region_cookie_t cookie;
   xcb_xfixes_fetch_region_reply_t *reply;

   cookie.sequence = _ecore_xcb_cookie_get();
   reply = xcb_xfixes_fetch_region_reply(_ecore_xcb_conn, cookie, NULL);
   _ecore_xcb_reply_cache(reply);
#endif /* ECORE_XCB_FIXES */
} /* ecore_x_region_fetch_fetch */

/**
 * Return the rectangles that compose a region.
 * @param  region The region (Unused).
 * @param  num    The number of returned rectangles.
 * @param  bounds The returned bounds of the region.
 * @return        The returned rectangles.
 *
 * The @p region passed to ecore_xcb_region_fetch_prefetch() is
 * returned as a list of rectagles in XY-banded order.
 *
 * To use this function, you must call before, and in order,
 * ecore_xcb_region_fetch_prefetch(), which sends the XFixesFetchRegion request,
 * then ecore_xcb_region_fetch_fetch(), which gets the reply.
 * @ingroup Ecore_X_Fixes_Group
 */
EAPI Ecore_X_Rectangle *
ecore_x_region_fetch(Ecore_X_Region region __UNUSED__,
                     int                  *num,
                     Ecore_X_Rectangle    *bounds)
{
   Ecore_X_Rectangle extents = { 0, 0, 0, 0};
   Ecore_X_Rectangle *rects = NULL;
#ifdef ECORE_XCB_FIXES
   int n;
   xcb_xfixes_fetch_region_reply_t *reply;

   reply = _ecore_xcb_reply_get();
   if (!reply)
     {
        if (num)
           *num = 0;

        if (bounds)
           *bounds = extents;

        return NULL;
     }

   n = xcb_xfixes_fetch_region_rectangles_length(reply);
   rects = (Ecore_X_Rectangle *)malloc(n * sizeof(Ecore_X_Rectangle));
   if (!rects)
     {
        if (num)
           *num = 0;

        if (bounds)
           *bounds = extents;

        return NULL;
     }

   if (num)
      *num = n;

   if (bounds)
     {
        bounds->x = reply->extents.x;
        bounds->y = reply->extents.y;
        bounds->width = reply->extents.width;
        bounds->height = reply->extents.height;
     }

   memcpy(rects,
          xcb_xfixes_fetch_region_rectangles(reply),
          sizeof(Ecore_X_Rectangle) * n);

   return rects;
#else /* ifdef ECORE_XCB_FIXES */
   if (num)
      *num = 0;

   if (bounds)
      *bounds = extents;

   return NULL;
#endif /* ECORE_XCB_FIXES */
} /* ecore_x_region_fetch */

/**
 * Expand a region.
 * @param dest   The destination region.
 * @param source The source region.
 * @param left   The number of pixels to add on the left.
 * @param right  The number of pixels to add on the right.
 * @param top    The number of pixels to add at the top.
 * @param bottom The number of pixels to add at the bottom.
 *
 * Put in @p dest the area specified by expanding each rectangle in
 * the @p source  region by the specified number of pixels to the
 * @p left, @p right, @p top and @p bottom.
 * @ingroup Ecore_X_Fixes_Group
 */
EAPI void
ecore_x_region_expand(Ecore_X_Region dest,
                      Ecore_X_Region source,
                      unsigned int   left,
                      unsigned int   right,
                      unsigned int   top,
                      unsigned int   bottom)
{
#ifdef ECORE_XCB_FIXES
   xcb_xfixes_expand_region(_ecore_xcb_conn, source, dest, left, right, top, bottom);
#endif /* ECORE_XCB_FIXES */
} /* ecore_x_region_expand */

/**
 * Change clip-mask in a graphic context to the specified region.
 * @param region   The region to change.
 * @param gc       The clip-mask graphic context.
 * @param x_origin The horizontal translation.
 * @param y_origin The vertical translation.
 *
 * Changes clip-mask in @p gc to the specified @p region and
 * sets the clip origin with the values of @p x_origin and @p y_origin.
 * Output will be clippped to remain contained within the region. The
 * clip origin is interpreted relative to the origin of whatever
 * destination drawable is specified in a graphics request. The
 * region is interpreted relative to the clip origin. Future changes
 * to region have no effect on the gc clip-mask.
 * @ingroup Ecore_X_Fixes_Group
 */
EAPI void
ecore_x_region_gc_clip_set(Ecore_X_Region region,
                           Ecore_X_GC     gc,
                           int            x_origin,
                           int            y_origin)
{
#ifdef ECORE_XCB_FIXES
   xcb_xfixes_set_gc_clip_region(_ecore_xcb_conn, gc, region, x_origin, y_origin);
#endif /* ECORE_XCB_FIXES */
} /* ecore_x_region_gc_clip_set */

/**
 * Change the shape extension of a window.
 * @param region   The region.
 * @param dest     The window whose shape is changed.
 * @param type     The kind of shape.
 * @param x_offset The horizontal offset.
 * @param y_offset The vertical offset.
 *
 * Set the specified Shape extension region of @p window to @p region,
 * offset by @p x_offset and @p y_offset. Future changes to region
 * have no effect on the window shape.
 * @ingroup Ecore_X_Fixes_Group
 */
EAPI void
ecore_x_region_window_shape_set(Ecore_X_Region     region,
                                Ecore_X_Window     dest,
                                Ecore_X_Shape_Type type,
                                int                x_offset,
                                int                y_offset)
{
#ifdef ECORE_XCB_FIXES
   xcb_xfixes_set_window_shape_region(_ecore_xcb_conn, dest, type, x_offset, y_offset, region);
#endif /* ECORE_XCB_FIXES */
} /* ecore_x_region_window_shape_set */

/**
 * Change clip-mask in picture to the specified region.
 * @param region   The region.
 * @param picture  The picture.
 * @param x_origin The X coordinate of the origin.
 * @param y_origin The Y coordinate of the origin.
 *
 * Changes clip-mask in picture to the specified @p region
 * and sets the clip origin.  Input and output will be clipped to
 * remain contained within the region.  The clip origin is interpreted
 * relative to the origin of the drawable associated with @p picture.  The
 * region is interpreted relative to the clip origin.  Future changes
 * to region have no effect on the picture clip-mask.
 * @ingroup Ecore_X_Fixes_Group
 */
EAPI void
ecore_x_region_picture_clip_set(Ecore_X_Region  region,
                                Ecore_X_Picture picture,
                                int             x_origin,
                                int             y_origin)
{
#ifdef ECORE_XCB_FIXES
   xcb_xfixes_set_picture_clip_region(_ecore_xcb_conn, picture, region, x_origin, y_origin);
#endif /* ECORE_XCB_FIXES */
} /* ecore_x_region_picture_clip_set */

