lots of updates, mode sorting, etc
[mesa.git] / src / egl / main / eglmode.c
1 #include <stdio.h>
2 #include <assert.h>
3 #include <stdlib.h>
4 #include <string.h>
5
6 #include "egldisplay.h"
7 #include "egldriver.h"
8 #include "eglmode.h"
9 #include "eglglobals.h"
10 #include "eglscreen.h"
11
12
13 #define MIN2(A, B) (((A) < (B)) ? (A) : (B))
14
15
16 static char *
17 my_strdup(const char *s)
18 {
19 if (s) {
20 int l = strlen(s);
21 char *s2 = malloc(l + 1);
22 if (s2)
23 strcpy(s2, s);
24 return s2;
25 }
26 else {
27 return NULL;
28 }
29 }
30
31
32 /**
33 * Given an EGLModeMESA handle, return the corresponding _EGLMode object
34 * or null if non-existant.
35 */
36 _EGLMode *
37 _eglLookupMode(EGLDisplay dpy, EGLModeMESA mode)
38 {
39 const _EGLDisplay *disp = _eglLookupDisplay(dpy);
40 EGLint scrnum;
41
42 /* loop over all screens on the display */
43 for (scrnum = 0; scrnum < disp->NumScreens; scrnum++) {
44 const _EGLScreen *scrn = disp->Screens[scrnum];
45 EGLint i;
46 /* search list of modes for handle */
47 for (i = 0; i < scrn->NumModes; i++) {
48 if (scrn->Modes[i].Handle == mode) {
49 return scrn->Modes + i;
50 }
51 }
52 }
53
54 return NULL;
55 }
56
57
58 /**
59 * Add a new mode with the given attributes (width, height, depth, refreshRate)
60 * to the given screen.
61 * Assign a new mode ID/handle to the mode as well.
62 * \return pointer to the new _EGLMode
63 */
64 _EGLMode *
65 _eglAddNewMode(_EGLScreen *screen, EGLint width, EGLint height,
66 EGLint refreshRate, const char *name)
67 {
68 EGLint n;
69 _EGLMode *newModes;
70
71 assert(screen);
72 assert(width > 0);
73 assert(height > 0);
74 assert(refreshRate > 0);
75
76 n = screen->NumModes;
77 newModes = (_EGLMode *) realloc(screen->Modes, (n+1) * sizeof(_EGLMode));
78 if (newModes) {
79 screen->Modes = newModes;
80 screen->Modes[n].Handle = n + 1;
81 screen->Modes[n].Width = width;
82 screen->Modes[n].Height = height;
83 screen->Modes[n].RefreshRate = refreshRate;
84 screen->Modes[n].Optimal = EGL_FALSE;
85 screen->Modes[n].Interlaced = EGL_FALSE;
86 screen->Modes[n].Name = my_strdup(name);
87 screen->NumModes++;
88 return screen->Modes + n;
89 }
90 else {
91 return NULL;
92 }
93 }
94
95
96
97 /**
98 * Parse the attrib_list to fill in the fields of the given _eglMode
99 * Return EGL_FALSE if any errors, EGL_TRUE otherwise.
100 */
101 static EGLBoolean
102 _eglParseModeAttribs(_EGLMode *mode, const EGLint *attrib_list)
103 {
104 EGLint i;
105
106 /* init all attribs to EGL_DONT_CARE */
107 mode->Handle = EGL_DONT_CARE;
108 mode->Width = EGL_DONT_CARE;
109 mode->Height = EGL_DONT_CARE;
110 mode->RefreshRate = EGL_DONT_CARE;
111 mode->Optimal = EGL_DONT_CARE;
112 mode->Interlaced = EGL_DONT_CARE;
113 mode->Name = NULL;
114
115 for (i = 0; attrib_list && attrib_list[i] != EGL_NONE; i++) {
116 switch (attrib_list[i]) {
117 case EGL_MODE_ID_MESA:
118 mode->Handle = attrib_list[++i];
119 if (mode->Handle <= 0) {
120 _eglError(EGL_BAD_PARAMETER, "eglChooseModeMESA(handle)");
121 return EGL_FALSE;
122 }
123 break;
124 case EGL_WIDTH:
125 mode->Width = attrib_list[++i];
126 if (mode->Width <= 0) {
127 _eglError(EGL_BAD_PARAMETER, "eglChooseModeMESA(width)");
128 return EGL_FALSE;
129 }
130 break;
131 case EGL_HEIGHT:
132 mode->Height = attrib_list[++i];
133 if (mode->Height <= 0) {
134 _eglError(EGL_BAD_PARAMETER, "eglChooseModeMESA(height)");
135 return EGL_FALSE;
136 }
137 break;
138 case EGL_REFRESH_RATE_MESA:
139 mode->RefreshRate = attrib_list[++i];
140 if (mode->RefreshRate <= 0) {
141 _eglError(EGL_BAD_PARAMETER, "eglChooseModeMESA(refresh rate)");
142 return EGL_FALSE;
143 }
144 break;
145 case EGL_INTERLACED_MESA:
146 mode->Interlaced = attrib_list[++i];
147 if (mode->Interlaced != EGL_TRUE && mode->Interlaced != EGL_FALSE) {
148 _eglError(EGL_BAD_PARAMETER, "eglChooseModeMESA(interlaced)");
149 return EGL_FALSE;
150 }
151 break;
152 case EGL_OPTIMAL_MESA:
153 mode->Optimal = attrib_list[++i];
154 if (mode->Optimal != EGL_TRUE && mode->Optimal != EGL_FALSE) {
155 _eglError(EGL_BAD_PARAMETER, "eglChooseModeMESA(optimal)");
156 return EGL_FALSE;
157 }
158 break;
159 default:
160 _eglError(EGL_BAD_ATTRIBUTE, "eglChooseModeMESA");
161 return EGL_FALSE;
162 }
163 }
164 return EGL_TRUE;
165 }
166
167
168 /**
169 * Determine if the candidate mode's attributes are at least as good
170 * as the minimal mode's.
171 * \return EGL_TRUE if qualifies, EGL_FALSE otherwise
172 */
173 static EGLBoolean
174 _eglModeQualifies(const _EGLMode *c, const _EGLMode *min)
175 {
176 if (min->Handle != EGL_DONT_CARE && c->Handle != min->Handle)
177 return EGL_FALSE;
178 if (min->Width != EGL_DONT_CARE && c->Width < min->Width)
179 return EGL_FALSE;
180 if (min->Height != EGL_DONT_CARE && c->Height < min->Height)
181 return EGL_FALSE;
182 if (min->RefreshRate != EGL_DONT_CARE && c->RefreshRate < min->RefreshRate)
183 return EGL_FALSE;
184 if (min->Optimal != EGL_DONT_CARE && c->Optimal != min->Optimal)
185 return EGL_FALSE;
186 if (min->Interlaced != EGL_DONT_CARE && c->Interlaced != min->Interlaced)
187 return EGL_FALSE;
188
189 return EGL_TRUE;
190 }
191
192
193 /**
194 * Return value of given mode attribute, or -1 if bad attrib.
195 */
196 static EGLint
197 getModeAttrib(const _EGLMode *m, EGLint attrib)
198 {
199 switch (attrib) {
200 case EGL_MODE_ID_MESA:
201 return m->Handle;
202 case EGL_WIDTH:
203 return m->Width;
204 case EGL_HEIGHT:
205 return m->Height;
206 case EGL_REFRESH_RATE_MESA:
207 return m->RefreshRate;
208 case EGL_OPTIMAL_MESA:
209 return m->Optimal;
210 case EGL_INTERLACED_MESA:
211 return m->Interlaced;
212 default:
213 return -1;
214 }
215 }
216
217
218 #define SMALLER 1
219 #define LARGER 2
220
221 struct sort_info {
222 EGLint Attrib;
223 EGLint Order; /* SMALLER or LARGER */
224 };
225
226 /* the order of these entries is the priority */
227 static struct sort_info SortInfo[] = {
228 { EGL_OPTIMAL_MESA, LARGER },
229 { EGL_INTERLACED_MESA, SMALLER },
230 { EGL_WIDTH, LARGER },
231 { EGL_HEIGHT, LARGER },
232 { EGL_REFRESH_RATE_MESA, LARGER },
233 { EGL_MODE_ID_MESA, SMALLER },
234 { 0, 0 }
235 };
236
237
238 /**
239 * Compare modes 'a' and 'b' and return -1 if a belongs before b, or 1 if a
240 * belongs after b, or 0 if they're equal.
241 * Used by qsort().
242 */
243 static int
244 _eglCompareModes(const void *a, const void *b)
245 {
246 const _EGLMode *aMode = *((const _EGLMode **) a);
247 const _EGLMode *bMode = *((const _EGLMode **) b);
248 EGLint i;
249
250 for (i = 0; SortInfo[i].Attrib; i++) {
251 const EGLint aVal = getModeAttrib(aMode, SortInfo[i].Attrib);
252 const EGLint bVal = getModeAttrib(bMode, SortInfo[i].Attrib);
253 if (aVal == bVal) {
254 /* a tie */
255 continue;
256 }
257 else if (SortInfo[i].Order == SMALLER) {
258 return (aVal < bVal) ? -1 : 1;
259 }
260 else if (SortInfo[i].Order == LARGER) {
261 return (aVal > bVal) ? -1 : 1;
262 }
263 }
264
265 /* all attributes identical */
266 return 0;
267 }
268
269
270 /**
271 * Search for EGLModes which match the given attribute list.
272 * Called via eglChooseModeMESA API function.
273 */
274 EGLBoolean
275 _eglChooseModeMESA(_EGLDriver *drv, EGLDisplay dpy, EGLScreenMESA screen,
276 const EGLint *attrib_list, EGLModeMESA *modes,
277 EGLint modes_size, EGLint *num_modes)
278 {
279 const _EGLScreen *scrn = _eglLookupScreen(dpy, screen);
280 _EGLMode **modeList, min;
281 EGLint i, count;
282
283 if (!scrn) {
284 _eglError(EGL_BAD_SCREEN_MESA, "eglChooseModeMESA");
285 return EGL_FALSE;
286 }
287
288 if (!_eglParseModeAttribs(&min, attrib_list)) {
289 /* error code will have been recorded */
290 return EGL_FALSE;
291 }
292
293 /* allocate array of mode pointers */
294 modeList = (_EGLMode **) malloc(modes_size * sizeof(_EGLMode *));
295 if (!modeList) {
296 _eglError(EGL_BAD_MODE_MESA, "eglChooseModeMESA(out of memory)");
297 return EGL_FALSE;
298 }
299
300 /* make array of pointers to qualifying modes */
301 for (i = count = 0; i < scrn->NumModes && count < modes_size; i++) {
302 if (_eglModeQualifies(scrn->Modes + i, &min)) {
303 modeList[count++] = scrn->Modes + i;
304 }
305 }
306
307 /* sort array of pointers */
308 qsort(modeList, count, sizeof(_EGLMode *), _eglCompareModes);
309
310 /* copy mode handles to output array */
311 for (i = 0; i < count; i++) {
312 modes[i] = modeList[i]->Handle;
313 }
314
315 free(modeList);
316
317 *num_modes = count;
318
319 return EGL_TRUE;
320 }
321
322
323
324 /**
325 * Return all possible modes for the given screen. No sorting of results.
326 * Called via eglGetModesMESA() API function.
327 */
328 EGLBoolean
329 _eglGetModesMESA(_EGLDriver *drv, EGLDisplay dpy, EGLScreenMESA screen,
330 EGLModeMESA *modes, EGLint modes_size, EGLint *num_modes)
331 {
332 _EGLScreen *scrn = _eglLookupScreen(dpy, screen);
333
334 if (!scrn) {
335 _eglError(EGL_BAD_SCREEN_MESA, "eglGetModesMESA");
336 return EGL_FALSE;
337 }
338
339 if (modes) {
340 EGLint i;
341 *num_modes = MIN2(scrn->NumModes, modes_size);
342 for (i = 0; i < *num_modes; i++) {
343 modes[i] = scrn->Modes[i].Handle;
344 }
345 }
346 else {
347 /* just return total number of supported modes */
348 *num_modes = scrn->NumModes;
349 }
350
351 return EGL_TRUE;
352 }
353
354
355 /**
356 * Query an attribute of a mode.
357 */
358 EGLBoolean
359 _eglGetModeAttribMESA(_EGLDriver *drv, EGLDisplay dpy,
360 EGLModeMESA mode, EGLint attribute, EGLint *value)
361 {
362 _EGLMode *m = _eglLookupMode(dpy, mode);
363 EGLint v;
364
365 if (!m) {
366 _eglError(EGL_BAD_MODE_MESA, "eglGetModeAttribMESA");
367 return EGL_FALSE;
368 }
369
370 v = getModeAttrib(m, attribute);
371 if (v < 0) {
372 _eglError(EGL_BAD_ATTRIBUTE, "eglGetModeAttribMESA");
373 return EGL_FALSE;
374 }
375 *value = v;
376 return EGL_TRUE;
377 }
378
379
380 /**
381 * Return human-readable string for given mode.
382 * This is the default function called by eglQueryModeStringMESA().
383 */
384 const char *
385 _eglQueryModeStringMESA(_EGLDriver *drv, EGLDisplay dpy, EGLModeMESA mode)
386 {
387 _EGLMode *m = _eglLookupMode(dpy, mode);
388 if (!m) {
389 _eglError(EGL_BAD_MODE_MESA, "eglQueryModeStringMESA");
390 return NULL;
391 }
392 return m->Name;
393 }
394
395
396 #if 0
397 static int
398 _eglRand(int max)
399 {
400 return rand() % max;
401 }
402
403 void
404 _eglTestModeModule(void)
405 {
406 EGLint count = 30;
407 _EGLMode *modes = (_EGLMode *) malloc(count * sizeof(_EGLMode));
408 _EGLMode **modeList = (_EGLMode **) malloc(count * sizeof(_EGLMode*));
409 EGLint i;
410
411 for (i = 0; i < count; i++) {
412 modes[i].Handle = _eglRand(20);
413 modes[i].Width = 512 + 256 * _eglRand(2);
414 modes[i].Height = 512 + 256 * _eglRand(2);
415 modes[i].RefreshRate = 50 + 5 * _eglRand(3);
416 modes[i].Interlaced = _eglRand(2);
417 modes[i].Optimal = _eglRand(4) == 0;
418 modeList[i] = modes + i;
419 }
420
421 /* sort array of pointers */
422 qsort(modeList, count, sizeof(_EGLMode *), compareModes);
423
424 for (i = 0; i < count; i++) {
425 _EGLMode *m = modeList[i];
426 printf("%2d: %3d %4d x %4d @ %3d opt %d int %d\n", i,
427 m->Handle, m->Width, m->Height, m->RefreshRate,
428 m->Optimal, m->Interlaced);
429 }
430 }
431 #endif