/**************************************************************************
 *
 * Copyright 2008 VMware, Inc.
 * Copyright 2009-2010 Chia-I Wu <olvaffe@gmail.com>
 * Copyright 2010 LunarG, Inc.
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sub license, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 **************************************************************************/


/*
 * Ideas for screen management extension to EGL.
 *
 * Each EGLDisplay has one or more screens (CRTs, Flat Panels, etc).
 * The screens' handles can be obtained with eglGetScreensMESA().
 *
 * A new kind of EGLSurface is possible- one which can be directly scanned
 * out on a screen.  Such a surface is created with eglCreateScreenSurface().
 *
 * To actually display a screen surface on a screen, the eglShowSurface()
 * function is called.
 */

#include <assert.h>
#include <stdlib.h>
#include <string.h>

#include "egldisplay.h"
#include "eglcurrent.h"
#include "eglmode.h"
#include "eglsurface.h"
#include "eglscreen.h"
#include "eglmutex.h"


#ifdef EGL_MESA_screen_surface


/* ugh, no atomic op? */
static _EGLMutex _eglNextScreenHandleMutex = _EGL_MUTEX_INITIALIZER;
static EGLScreenMESA _eglNextScreenHandle = 1;


/**
 * Return a new screen handle/ID.
 * NOTE: we never reuse these!
 */
static EGLScreenMESA
_eglAllocScreenHandle(void)
{
   EGLScreenMESA s;

   _eglLockMutex(&_eglNextScreenHandleMutex);
   s = _eglNextScreenHandle;
   _eglNextScreenHandle += _EGL_SCREEN_MAX_MODES;
   _eglUnlockMutex(&_eglNextScreenHandleMutex);

   return s;
}


/**
 * Initialize an _EGLScreen object to default values.
 */
void
_eglInitScreen(_EGLScreen *screen, _EGLDisplay *dpy, EGLint num_modes)
{
   memset(screen, 0, sizeof(_EGLScreen));

   screen->Display = dpy;
   screen->NumModes = num_modes;
   screen->StepX = 1;
   screen->StepY = 1;

   if (num_modes > _EGL_SCREEN_MAX_MODES)
      num_modes = _EGL_SCREEN_MAX_MODES;
   screen->Modes = calloc(num_modes, sizeof(*screen->Modes));
   screen->NumModes = (screen->Modes) ? num_modes : 0;
}


/**
 * Link a screen to its display and return the handle of the link.
 * The handle can be passed to client directly.
 */
EGLScreenMESA
_eglLinkScreen(_EGLScreen *screen)
{
   _EGLDisplay *display;
   EGLint i;

   assert(screen && screen->Display);
   display = screen->Display;

   if (!display->Screens) {
      display->Screens = _eglCreateArray("Screen", 4);
      if (!display->Screens)
         return (EGLScreenMESA) 0;
   }

   screen->Handle = _eglAllocScreenHandle();
   for (i = 0; i < screen->NumModes; i++)
      screen->Modes[i].Handle = screen->Handle + i;

   _eglAppendArray(display->Screens, (void *) screen);

   return screen->Handle;
}


/**
 * Lookup a handle to find the linked config.
 * Return NULL if the handle has no corresponding linked config.
 */
_EGLScreen *
_eglLookupScreen(EGLScreenMESA screen, _EGLDisplay *display)
{
   EGLint i;

   if (!display || !display->Screens)
      return NULL;

   for (i = 0; i < display->Screens->Size; i++) {
      _EGLScreen *scr = (_EGLScreen *) display->Screens->Elements[i];
      if (scr->Handle == screen) {
         assert(scr->Display == display);
         return scr;
      }
   }
   return NULL;
}


static EGLBoolean
_eglFlattenScreen(void *elem, void *buffer)
{
   _EGLScreen *scr = (_EGLScreen *) elem;
   EGLScreenMESA *handle = (EGLScreenMESA *) buffer;
   *handle = _eglGetScreenHandle(scr);
   return EGL_TRUE;
}


EGLBoolean
_eglGetScreensMESA(_EGLDriver *drv, _EGLDisplay *display, EGLScreenMESA *screens,
                   EGLint max_screens, EGLint *num_screens)
{
   *num_screens = _eglFlattenArray(display->Screens, (void *) screens,
         sizeof(screens[0]), max_screens, _eglFlattenScreen);

   return EGL_TRUE;
}


/**
 * Set a screen's surface origin.
 */
EGLBoolean
_eglScreenPositionMESA(_EGLDriver *drv, _EGLDisplay *dpy,
                       _EGLScreen *scrn, EGLint x, EGLint y)
{
   scrn->OriginX = x;
   scrn->OriginY = y;

   return EGL_TRUE;
}


/**
 * Query a screen's current surface.
 */
EGLBoolean
_eglQueryScreenSurfaceMESA(_EGLDriver *drv, _EGLDisplay *dpy,
                           _EGLScreen *scrn, _EGLSurface **surf)
{
   *surf = scrn->CurrentSurface;
   return EGL_TRUE;
}


/**
 * Query a screen's current mode.
 */
EGLBoolean
_eglQueryScreenModeMESA(_EGLDriver *drv, _EGLDisplay *dpy, _EGLScreen *scrn,
                        _EGLMode **m)
{
   *m = scrn->CurrentMode;
   return EGL_TRUE;
}


EGLBoolean
_eglQueryScreenMESA(_EGLDriver *drv, _EGLDisplay *dpy, _EGLScreen *scrn,
                    EGLint attribute, EGLint *value)
{
   switch (attribute) {
   case EGL_SCREEN_POSITION_MESA:
      value[0] = scrn->OriginX;
      value[1] = scrn->OriginY;
      break;
   case EGL_SCREEN_POSITION_GRANULARITY_MESA:
      value[0] = scrn->StepX;
      value[1] = scrn->StepY;
      break;
   default:
      _eglError(EGL_BAD_ATTRIBUTE, "eglQueryScreenMESA");
      return EGL_FALSE;
   }

   return EGL_TRUE;
}


#endif /* EGL_MESA_screen_surface */
