1/**************************************************************************
2 *
3 * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
4 * Copyright 2009-2010 Chia-I Wu <olvaffe@gmail.com>
5 * Copyright 2010 LunarG, Inc.
6 * All Rights Reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sub license, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice (including the
17 * next paragraph) shall be included in all copies or substantial portions
18 * of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 * DEALINGS IN THE SOFTWARE.
27 *
28 **************************************************************************/
29
30
31#include <assert.h>
32#include <stdlib.h>
33#include <string.h>
34
35#include "egldisplay.h"
36#include "eglmode.h"
37#include "eglcurrent.h"
38#include "eglscreen.h"
39
40
41#ifdef EGL_MESA_screen_surface
42
43
44#define MIN2(A, B)  (((A) < (B)) ? (A) : (B))
45
46
47/**
48 * Given an EGLModeMESA handle, return the corresponding _EGLMode object
49 * or null if non-existant.
50 */
51_EGLMode *
52_eglLookupMode(EGLModeMESA mode, _EGLDisplay *disp)
53{
54   EGLint scrnum;
55
56   if (!disp || !disp->Screens)
57      return NULL;
58
59   /* loop over all screens on the display */
60   for (scrnum = 0; scrnum < disp->Screens->Size; scrnum++) {
61      const _EGLScreen *scrn = disp->Screens->Elements[scrnum];
62      EGLint idx;
63
64      /*
65       * the mode ids of a screen ranges from scrn->Handle to scrn->Handle +
66       * scrn->NumModes
67       */
68      if (mode >= scrn->Handle &&
69          mode < scrn->Handle + _EGL_SCREEN_MAX_MODES) {
70         idx = mode - scrn->Handle;
71
72         assert(idx < scrn->NumModes && scrn->Modes[idx].Handle == mode);
73
74         return &scrn->Modes[idx];
75      }
76   }
77
78   return NULL;
79}
80
81
82/**
83 * Parse the attrib_list to fill in the fields of the given _eglMode
84 * Return EGL_FALSE if any errors, EGL_TRUE otherwise.
85 */
86static EGLBoolean
87_eglParseModeAttribs(_EGLMode *mode, const EGLint *attrib_list)
88{
89   EGLint i;
90
91   /* init all attribs to EGL_DONT_CARE */
92   mode->Handle = EGL_DONT_CARE;
93   mode->Width = EGL_DONT_CARE;
94   mode->Height = EGL_DONT_CARE;
95   mode->RefreshRate = EGL_DONT_CARE;
96   mode->Optimal = EGL_DONT_CARE;
97   mode->Interlaced = EGL_DONT_CARE;
98   mode->Name = NULL;
99
100   for (i = 0; attrib_list && attrib_list[i] != EGL_NONE; i++) {
101      switch (attrib_list[i]) {
102      case EGL_MODE_ID_MESA:
103         mode->Handle = attrib_list[++i];
104         if (mode->Handle <= 0) {
105            _eglError(EGL_BAD_PARAMETER, "eglChooseModeMESA(handle)");
106            return EGL_FALSE;
107         }
108         break;
109      case EGL_WIDTH:
110         mode->Width = attrib_list[++i];
111         if (mode->Width <= 0) {
112            _eglError(EGL_BAD_PARAMETER, "eglChooseModeMESA(width)");
113            return EGL_FALSE;
114         }
115         break;
116      case EGL_HEIGHT:
117         mode->Height = attrib_list[++i];
118         if (mode->Height <= 0) {
119            _eglError(EGL_BAD_PARAMETER, "eglChooseModeMESA(height)");
120            return EGL_FALSE;
121         }
122         break;
123      case EGL_REFRESH_RATE_MESA:
124         mode->RefreshRate = attrib_list[++i];
125         if (mode->RefreshRate <= 0) {
126            _eglError(EGL_BAD_PARAMETER, "eglChooseModeMESA(refresh rate)");
127            return EGL_FALSE;
128         }
129         break;
130      case EGL_INTERLACED_MESA:
131         mode->Interlaced = attrib_list[++i];
132         if (mode->Interlaced != EGL_TRUE && mode->Interlaced != EGL_FALSE) {
133            _eglError(EGL_BAD_PARAMETER, "eglChooseModeMESA(interlaced)");
134            return EGL_FALSE;
135         }
136         break;
137      case EGL_OPTIMAL_MESA:
138         mode->Optimal = attrib_list[++i];
139         if (mode->Optimal != EGL_TRUE && mode->Optimal != EGL_FALSE) {
140            _eglError(EGL_BAD_PARAMETER, "eglChooseModeMESA(optimal)");
141            return EGL_FALSE;
142         }
143         break;
144      default:
145         _eglError(EGL_BAD_ATTRIBUTE, "eglChooseModeMESA");
146         return EGL_FALSE;
147      }
148   }
149   return EGL_TRUE;
150}
151
152
153/**
154 * Determine if the candidate mode's attributes are at least as good
155 * as the minimal mode's.
156 * \return EGL_TRUE if qualifies, EGL_FALSE otherwise
157 */
158static EGLBoolean
159_eglModeQualifies(const _EGLMode *c, const _EGLMode *min)
160{
161   if (min->Handle != EGL_DONT_CARE && c->Handle != min->Handle)
162      return EGL_FALSE;
163   if (min->Width != EGL_DONT_CARE && c->Width < min->Width)
164      return EGL_FALSE;
165   if (min->Height != EGL_DONT_CARE && c->Height < min->Height)
166      return EGL_FALSE;
167   if (min->RefreshRate != EGL_DONT_CARE && c->RefreshRate < min->RefreshRate)
168      return EGL_FALSE;
169   if (min->Optimal != EGL_DONT_CARE && c->Optimal != min->Optimal)
170      return EGL_FALSE;
171   if (min->Interlaced != EGL_DONT_CARE && c->Interlaced != min->Interlaced)
172      return EGL_FALSE;
173
174   return EGL_TRUE;
175}
176
177
178/**
179 * Return value of given mode attribute, or -1 if bad attrib.
180 */
181static EGLint
182getModeAttrib(const _EGLMode *m, EGLint attrib)
183{
184   switch (attrib) {
185   case EGL_MODE_ID_MESA:
186      return m->Handle;
187   case EGL_WIDTH:
188      return m->Width;
189   case EGL_HEIGHT:
190      return m->Height;
191   case EGL_REFRESH_RATE_MESA:
192      return m->RefreshRate;
193   case EGL_OPTIMAL_MESA:
194      return m->Optimal;
195   case EGL_INTERLACED_MESA:
196      return m->Interlaced;
197   default:
198      return -1;
199   }
200}
201
202
203#define SMALLER 1
204#define LARGER  2
205
206struct sort_info {
207   EGLint Attrib;
208   EGLint Order; /* SMALLER or LARGER */
209};
210
211/* the order of these entries is the priority */
212static struct sort_info SortInfo[] = {
213   { EGL_OPTIMAL_MESA, LARGER },
214   { EGL_INTERLACED_MESA, SMALLER },
215   { EGL_WIDTH, LARGER },
216   { EGL_HEIGHT, LARGER },
217   { EGL_REFRESH_RATE_MESA, LARGER },
218   { EGL_MODE_ID_MESA, SMALLER },
219   { 0, 0 }
220};
221
222
223/**
224 * Compare modes 'a' and 'b' and return -1 if a belongs before b, or 1 if a
225 * belongs after b, or 0 if they're equal.
226 * Used by qsort().
227 */
228static int
229_eglCompareModes(const void *a, const void *b)
230{
231   const _EGLMode *aMode = *((const _EGLMode **) a);
232   const _EGLMode *bMode = *((const _EGLMode **) b);
233   EGLint i;
234
235   for (i = 0; SortInfo[i].Attrib; i++) {
236      const EGLint aVal = getModeAttrib(aMode, SortInfo[i].Attrib);
237      const EGLint bVal = getModeAttrib(bMode, SortInfo[i].Attrib);
238      if (aVal == bVal) {
239         /* a tie */
240         continue;
241      }
242      else if (SortInfo[i].Order == SMALLER) {
243         return (aVal < bVal) ? -1 : 1;
244      }
245      else if (SortInfo[i].Order == LARGER) {
246         return (aVal > bVal) ? -1 : 1;
247      }
248   }
249
250   /* all attributes identical */
251   return 0;
252}
253
254
255/**
256 * Search for EGLModes which match the given attribute list.
257 * Called via eglChooseModeMESA API function.
258 */
259EGLBoolean
260_eglChooseModeMESA(_EGLDriver *drv, _EGLDisplay *dpy, _EGLScreen *scrn,
261                   const EGLint *attrib_list, EGLModeMESA *modes,
262                   EGLint modes_size, EGLint *num_modes)
263{
264   _EGLMode **modeList, min;
265   EGLint i, count;
266
267   if (!_eglParseModeAttribs(&min, attrib_list)) {
268      /* error code will have been recorded */
269      return EGL_FALSE;
270   }
271
272   /* allocate array of mode pointers */
273   modeList = (_EGLMode **) malloc(modes_size * sizeof(_EGLMode *));
274   if (!modeList) {
275      _eglError(EGL_BAD_MODE_MESA, "eglChooseModeMESA(out of memory)");
276      return EGL_FALSE;
277   }
278
279   /* make array of pointers to qualifying modes */
280   for (i = count = 0; i < scrn->NumModes && count < modes_size; i++) {
281      if (_eglModeQualifies(scrn->Modes + i, &min)) {
282         modeList[count++] = scrn->Modes + i;
283      }
284   }
285
286   /* sort array of pointers */
287   qsort(modeList, count, sizeof(_EGLMode *), _eglCompareModes);
288
289   /* copy mode handles to output array */
290   for (i = 0; i < count; i++) {
291      modes[i] = modeList[i]->Handle;
292   }
293
294   free(modeList);
295
296   *num_modes = count;
297
298   return EGL_TRUE;
299}
300
301
302
303/**
304 * Return all possible modes for the given screen.  No sorting of results.
305 * Called via eglGetModesMESA() API function.
306 */
307EGLBoolean
308_eglGetModesMESA(_EGLDriver *drv, _EGLDisplay *dpy, _EGLScreen *scrn,
309                 EGLModeMESA *modes, EGLint modes_size, EGLint *num_modes)
310{
311   if (modes) {
312      EGLint i;
313      *num_modes = MIN2(scrn->NumModes, modes_size);
314      for (i = 0; i < *num_modes; i++) {
315         modes[i] = scrn->Modes[i].Handle;
316      }
317   }
318   else {
319      /* just return total number of supported modes */
320      *num_modes = scrn->NumModes;
321   }
322
323   return EGL_TRUE;
324}
325
326
327/**
328 * Query an attribute of a mode.
329 */
330EGLBoolean
331_eglGetModeAttribMESA(_EGLDriver *drv, _EGLDisplay *dpy,
332                      _EGLMode *m, EGLint attribute, EGLint *value)
333{
334   EGLint v;
335
336   v = getModeAttrib(m, attribute);
337   if (v < 0) {
338      _eglError(EGL_BAD_ATTRIBUTE, "eglGetModeAttribMESA");
339      return EGL_FALSE;
340   }
341   *value = v;
342   return EGL_TRUE;
343}
344
345
346/**
347 * Return human-readable string for given mode.
348 * This is the default function called by eglQueryModeStringMESA().
349 */
350const char *
351_eglQueryModeStringMESA(_EGLDriver *drv, _EGLDisplay *dpy, _EGLMode *m)
352{
353   return m->Name;
354}
355
356
357#endif /* EGL_MESA_screen_surface */
358