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 *
_eglLookupMode(EGLModeMESA mode,_EGLDisplay * disp)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  */
86 static EGLBoolean
_eglParseModeAttribs(_EGLMode * mode,const EGLint * attrib_list)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  */
158 static EGLBoolean
_eglModeQualifies(const _EGLMode * c,const _EGLMode * min)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  */
181 static EGLint
getModeAttrib(const _EGLMode * m,EGLint attrib)182 getModeAttrib(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 
206 struct sort_info {
207    EGLint Attrib;
208    EGLint Order; /* SMALLER or LARGER */
209 };
210 
211 /* the order of these entries is the priority */
212 static 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  */
228 static int
_eglCompareModes(const void * a,const void * b)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  */
259 EGLBoolean
_eglChooseModeMESA(_EGLDriver * drv,_EGLDisplay * dpy,_EGLScreen * scrn,const EGLint * attrib_list,EGLModeMESA * modes,EGLint modes_size,EGLint * num_modes)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  */
307 EGLBoolean
_eglGetModesMESA(_EGLDriver * drv,_EGLDisplay * dpy,_EGLScreen * scrn,EGLModeMESA * modes,EGLint modes_size,EGLint * num_modes)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  */
330 EGLBoolean
_eglGetModeAttribMESA(_EGLDriver * drv,_EGLDisplay * dpy,_EGLMode * m,EGLint attribute,EGLint * value)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  */
350 const char *
_eglQueryModeStringMESA(_EGLDriver * drv,_EGLDisplay * dpy,_EGLMode * m)351 _eglQueryModeStringMESA(_EGLDriver *drv, _EGLDisplay *dpy, _EGLMode *m)
352 {
353    return m->Name;
354 }
355 
356 
357 #endif /* EGL_MESA_screen_surface */
358