1 /*
2  * Copyright © 2009 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  *
23  * Authors:
24  *    Eric Anholt <eric@anholt.net>
25  *
26  */
27 
28 #include "piglit-util-gl.h"
29 #include "piglit-glx-util.h"
30 
31 #ifndef _WIN32
32 __attribute__((weak)) int piglit_width = 100;
33 __attribute__((weak)) int piglit_height = 100;
34 #endif
35 
36 Display *
piglit_get_glx_display()37 piglit_get_glx_display()
38 {
39 	Display *dpy;
40 
41 	dpy = XOpenDisplay(NULL);
42 	if (!dpy) {
43 		fprintf(stderr, "couldn't open display\n");
44 		piglit_report_result(PIGLIT_FAIL);
45 	}
46 
47 	return dpy;
48 }
49 
50 XVisualInfo *
piglit_get_glx_visual(Display * dpy)51 piglit_get_glx_visual(Display *dpy)
52 {
53 	XVisualInfo *visinfo;
54 	int attrib[] = {
55 		GLX_RGBA,
56 		GLX_RED_SIZE, 1,
57 		GLX_GREEN_SIZE, 1,
58 		GLX_BLUE_SIZE, 1,
59 		GLX_DEPTH_SIZE, 1,
60 		GLX_STENCIL_SIZE, 1,
61 		GLX_DOUBLEBUFFER,
62 		None
63 	};
64 	int screen = DefaultScreen(dpy);
65 
66 	visinfo = glXChooseVisual(dpy, screen, attrib);
67 	if (visinfo == NULL) {
68 		fprintf(stderr,
69 			"Couldn't get an RGBA, double-buffered visual\n");
70 		piglit_report_result(PIGLIT_FAIL);
71 	}
72 
73 	return visinfo;
74 }
75 
76 GLXContext
piglit_get_glx_context(Display * dpy,XVisualInfo * visinfo)77 piglit_get_glx_context(Display *dpy, XVisualInfo *visinfo)
78 {
79 	return piglit_get_glx_context_share(dpy, visinfo, NULL);
80 }
81 
82 GLXContext
piglit_get_glx_context_share(Display * dpy,XVisualInfo * visinfo,GLXContext share)83 piglit_get_glx_context_share(Display *dpy, XVisualInfo *visinfo, GLXContext share)
84 {
85 	GLXContext ctx;
86 
87 	ctx = glXCreateContext(dpy, visinfo, share, True);
88 	if (ctx == None) {
89 		fprintf(stderr, "glXCreateContext failed\n");
90 		piglit_report_result(PIGLIT_FAIL);
91 	}
92 
93 	return ctx;
94 }
95 
96 static Window
_piglit_get_glx_window(Display * dpy,XVisualInfo * visinfo,bool map,bool fullscreen)97 _piglit_get_glx_window(Display *dpy, XVisualInfo *visinfo, bool map, bool fullscreen)
98 {
99 	XSetWindowAttributes window_attr;
100 	unsigned long mask;
101 	int screen = DefaultScreen(dpy);
102 	Window root_win = RootWindow(dpy, screen);
103 	Window win;
104 
105 	window_attr.background_pixel = 0;
106 	window_attr.border_pixel = 0;
107 	window_attr.colormap = XCreateColormap(dpy, root_win,
108 					       visinfo->visual, AllocNone);
109 	window_attr.event_mask = StructureNotifyMask | ExposureMask |
110 		KeyPressMask;
111 	mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
112 
113 	if (fullscreen) {
114 		window_attr.override_redirect = True;
115 		mask |= CWOverrideRedirect;
116 		piglit_width = DisplayWidth(dpy, screen);
117 		piglit_height = DisplayHeight(dpy, screen);
118 	}
119 
120 	win = XCreateWindow(dpy, root_win, 0, 0,
121 			    piglit_width, piglit_height,
122 			    0, visinfo->depth, InputOutput,
123 			    visinfo->visual, mask, &window_attr);
124 
125 	if (piglit_automatic)
126 		piglit_glx_window_set_no_input(dpy, win);
127 
128 	if (map)
129 		XMapWindow(dpy, win);
130 
131 	return win;
132 }
133 
134 Window
piglit_get_glx_window_unmapped(Display * dpy,XVisualInfo * visinfo)135 piglit_get_glx_window_unmapped(Display *dpy, XVisualInfo *visinfo)
136 {
137 	return _piglit_get_glx_window(dpy, visinfo, false, false);
138 }
139 
140 Window
piglit_get_glx_window_fullscreen(Display * dpy,XVisualInfo * visinfo)141 piglit_get_glx_window_fullscreen(Display *dpy, XVisualInfo *visinfo)
142 {
143 	return _piglit_get_glx_window(dpy, visinfo, true, true);
144 }
145 
146 Window
piglit_get_glx_window(Display * dpy,XVisualInfo * visinfo)147 piglit_get_glx_window(Display *dpy, XVisualInfo *visinfo)
148 {
149 	return _piglit_get_glx_window(dpy, visinfo, true, false);
150 }
151 
152 bool
piglit_is_glx_extension_supported(Display * dpy,const char * name)153 piglit_is_glx_extension_supported(Display *dpy, const char *name)
154 {
155 	int screen = DefaultScreen(dpy);
156 	const char *const glx_extension_list =
157 		glXQueryExtensionsString(dpy, screen);
158 
159 	return piglit_is_extension_in_string(glx_extension_list, name);
160 }
161 
162 void
piglit_require_glx_extension(Display * dpy,const char * name)163 piglit_require_glx_extension(Display *dpy, const char *name)
164 {
165 	if (!piglit_is_glx_extension_supported(dpy, name)) {
166 		fprintf(stderr, "Test requires %s\n", name);
167 		piglit_report_result(PIGLIT_SKIP);
168 	}
169 }
170 
171 
172 void
piglit_require_glx_version(Display * dpy,int major,int minor)173 piglit_require_glx_version(Display *dpy, int major, int minor)
174 {
175 	int glxMajor;
176 	int glxMinor;
177 
178 	if (! glXQueryVersion(dpy, & glxMajor, & glxMinor)) {
179 		fprintf(stderr, "Could not query GLX version!\n");
180 		piglit_report_result(PIGLIT_FAIL);
181 	}
182 
183 	if (glxMajor != major || glxMinor < minor) {
184 		fprintf(stderr, "Test requires GLX %d.%d.  Got %d.%d.\n",
185 			major, minor, glxMajor, glxMinor);
186 		piglit_report_result(PIGLIT_SKIP);
187 	}
188 }
189 
190 
191 void
piglit_glx_event_loop(Display * dpy,enum piglit_result (* draw)(Display * dpy))192 piglit_glx_event_loop(Display *dpy, enum piglit_result (*draw)(Display *dpy))
193 {
194 	for (;;) {
195 		XEvent event;
196 		XNextEvent (dpy, &event);
197 
198 		if (event.type == KeyPress) {
199 			int keysyms_per_keycode_return;
200 			KeySym *sym = XGetKeyboardMapping (dpy,
201 							   event.xkey.keycode,
202 							   1,
203 							   &keysyms_per_keycode_return);
204 
205 			if (sym[0] == XK_Escape || sym[0] == XK_q || sym[0] == XK_Q)
206 				break;
207 			else
208 				draw(dpy);
209 		} else if (event.type == Expose) {
210 			enum piglit_result result = draw(dpy);
211 
212 			if (piglit_automatic) {
213 				/*
214 				 * Rerun if we have failed and have a
215 				 * pending expose event, which might be an
216 				 * indication of invalid front buffer
217 				 * contents.
218 				 */
219 				if (result == PIGLIT_FAIL &&
220 				    XCheckTypedEvent(dpy, Expose, &event)) {
221 					fprintf(stderr,
222 						"Pending expose event- "
223 						"rerunning.\n");
224 					XPutBackEvent(dpy, &event);
225 					continue;
226 				}
227 				XCloseDisplay(dpy);
228 				piglit_report_result(result);
229 				break;
230 			}
231 		}
232         }
233 }
234 
235 
236 static enum piglit_result
piglit_iterate_visuals_event_loop(Display * dpy,enum piglit_result (* draw)(Display * dpy,GLXFBConfig config),GLXFBConfig config)237 piglit_iterate_visuals_event_loop(Display *dpy,
238 				  enum piglit_result (*draw)(Display *dpy,
239 							     GLXFBConfig config),
240 				  GLXFBConfig config)
241 {
242 	for (;;) {
243 		XEvent event;
244 		XNextEvent (dpy, &event);
245 
246 		if (event.type == Expose) {
247 			return draw(dpy, config);
248 		}
249         }
250 }
251 
252 void
piglit_glx_window_set_no_input(Display * dpy,GLXDrawable win)253 piglit_glx_window_set_no_input(Display *dpy, GLXDrawable win)
254 {
255 	XWMHints *hints;
256 	hints = XAllocWMHints();
257 	hints->flags |= InputHint;
258 	hints->input = False;
259 
260 	XSetWMHints(dpy, win, hints);
261 
262 	XFree(hints);
263 }
264 
265 void
piglit_glx_set_no_input(void)266 piglit_glx_set_no_input(void)
267 {
268 	Display *d;
269 	GLXDrawable win;
270 
271 	d = glXGetCurrentDisplay();
272 	win = glXGetCurrentDrawable();
273 
274 	piglit_glx_window_set_no_input(d, win);
275 }
276 
277 enum piglit_result
piglit_glx_iterate_pixmap_fbconfigs(enum piglit_result (* draw)(Display * dpy,GLXFBConfig config))278 piglit_glx_iterate_pixmap_fbconfigs(enum piglit_result (*draw)(Display *dpy,
279 						      GLXFBConfig config))
280 {
281 	int screen;
282 	GLXFBConfig *configs;
283 	int n_configs;
284 	int i;
285 	bool any_fail = false;
286 	bool any_pass = false;
287 	Window root_win;
288 
289 	Display *dpy = XOpenDisplay(NULL);
290 	if (!dpy) {
291 		fprintf(stderr, "couldn't open display\n");
292 		piglit_report_result(PIGLIT_FAIL);
293 	}
294 	screen = DefaultScreen(dpy);
295 	root_win = RootWindow(dpy, screen);
296 
297 	configs = glXGetFBConfigs(dpy, screen, &n_configs);
298 	if (!configs) {
299 		fprintf(stderr, "No GLX FB configs\n");
300 		piglit_report_result(PIGLIT_SKIP);
301 	}
302 
303 	for (i = 0; i < n_configs; i++) {
304 		GLXFBConfig config = configs[i];
305 		enum piglit_result result;
306 		GLXContext ctx;
307 		Pixmap pix;
308 		GLXPixmap glx_pix;
309 		int draw_types;
310 		int depth;
311 
312 		glXGetFBConfigAttrib(dpy, config, GLX_DRAWABLE_TYPE,
313 				     &draw_types);
314 
315 		if (!(draw_types & GLX_PIXMAP_BIT))
316 			continue;
317 
318 		glXGetFBConfigAttrib(dpy, config, GLX_BUFFER_SIZE,
319 				     &depth);
320 		ctx = glXCreateNewContext(dpy, config, GLX_RGBA_TYPE,
321 					  NULL, true);
322 		pix = XCreatePixmap(dpy, root_win,
323 				    piglit_width, piglit_height, depth);
324 		glx_pix = glXCreatePixmap(dpy, config, pix, NULL);
325 		glXMakeCurrent(dpy, glx_pix, ctx);
326 
327 		result = draw(dpy, config);
328 
329 		if (result == PIGLIT_FAIL)
330 			any_fail = true;
331 		else if (result == PIGLIT_PASS)
332 			any_pass = true;
333 
334 		XFreePixmap(dpy, pix);
335 		glXDestroyContext(dpy, ctx);
336 	}
337 
338 	XFree(configs);
339 
340 	if (any_fail)
341 		return PIGLIT_FAIL;
342 	else if (any_pass)
343 		return PIGLIT_PASS;
344 	else
345 		return PIGLIT_SKIP;
346 }
347 
348 enum piglit_result
piglit_glx_iterate_visuals(enum piglit_result (* draw)(Display * dpy,GLXFBConfig config))349 piglit_glx_iterate_visuals(enum piglit_result (*draw)(Display *dpy,
350 						      GLXFBConfig config))
351 {
352 	int screen;
353 	GLXFBConfig *configs;
354 	int n_configs;
355 	int i;
356 	bool any_fail = false;
357 	bool any_pass = false;
358 
359 	Display *dpy = XOpenDisplay(NULL);
360 	if (!dpy) {
361 		fprintf(stderr, "couldn't open display\n");
362 		piglit_report_result(PIGLIT_FAIL);
363 	}
364 	screen = DefaultScreen(dpy);
365 
366 	configs = glXGetFBConfigs(dpy, screen, &n_configs);
367 	if (!configs) {
368 		fprintf(stderr, "No GLX FB configs\n");
369 		piglit_report_result(PIGLIT_SKIP);
370 	}
371 
372 	for (i = 0; i < n_configs; i++) {
373 		enum piglit_result result;
374 		XVisualInfo *visinfo;
375 		GLXContext ctx;
376 		GLXDrawable d;
377 
378 		visinfo = glXGetVisualFromFBConfig(dpy, configs[i]);
379 		if (!visinfo)
380 			continue;
381 
382 		ctx = piglit_get_glx_context(dpy, visinfo);
383 		d = piglit_get_glx_window(dpy, visinfo);
384 		glXMakeCurrent(dpy, d, ctx);
385 		XFree(visinfo);
386 
387 		result = piglit_iterate_visuals_event_loop(dpy, draw,
388 							   configs[i]);
389 		if (result == PIGLIT_FAIL)
390 			any_fail = true;
391 		else if (result == PIGLIT_PASS)
392 			any_pass = true;
393 
394 		XDestroyWindow(dpy, d);
395 		glXDestroyContext(dpy, ctx);
396 	}
397 
398 	XFree(configs);
399 
400 	if (any_fail)
401 		return PIGLIT_FAIL;
402 	else if (any_pass)
403 		return PIGLIT_PASS;
404 	else
405 		return PIGLIT_SKIP;
406 }
407 
408 GLXFBConfig
piglit_glx_get_fbconfig_for_visinfo(Display * dpy,XVisualInfo * visinfo)409 piglit_glx_get_fbconfig_for_visinfo(Display *dpy, XVisualInfo *visinfo)
410 {
411 	int i, nconfigs;
412 	GLXFBConfig ret = None, *configs;
413 
414 	configs = glXGetFBConfigs(dpy, visinfo->screen, &nconfigs);
415 	if (!configs)
416 		return None;
417 
418 	for (i = 0; i < nconfigs; i++) {
419 		int v;
420 
421 		if (glXGetFBConfigAttrib(dpy, configs[i], GLX_VISUAL_ID, &v))
422 			continue;
423 
424 		if (v == visinfo->visualid) {
425 			ret = configs[i];
426 			break;
427 		}
428 	}
429 
430 	XFree(configs);
431 	return ret;
432 }
433 
434 /*
435  * If you use this in an X error handler - and you will - pre-call it as:
436  *  piglit_glx_get_error(dpy, NULL);
437  * outside the error handler to cache errbase.  Otherwise this will
438  * generate protocol, and you'll deadlock.
439  *
440  * Returns -1 if the error is not a GLX error, otherwise returns the
441  * GLX error code.
442  */
443 int
piglit_glx_get_error(Display * dpy,XErrorEvent * err)444 piglit_glx_get_error(Display *dpy, XErrorEvent *err)
445 {
446 	static int errbase, evbase;
447 
448 	if (!errbase)
449 		glXQueryExtension(dpy, &errbase, &evbase);
450 
451 	if (!err)
452 		return -1;
453 
454 	if (err->error_code < errbase ||
455 	    err->error_code > errbase + GLXBadProfileARB)
456 		return -1;
457 
458 	return err->error_code - errbase;
459 }
460 
461 /**
462  * Convert a GLX error code to a printable string
463  *
464  * \sa piglit_glx_get_error
465  */
466 const char *
piglit_glx_error_string(int err)467 piglit_glx_error_string(int err)
468 {
469 	static const char *const error_table[] = {
470 		"GLXBadContext",
471 		"GLXBadContextState",
472 		"GLXBadDrawable",
473 		"GLXBadPixmap",
474 		"GLXBadContextTag",
475 		"GLXBadCurrentWindow",
476 		"GLXBadRenderRequest",
477 		"GLXBadLargeRequest",
478 		"GLXUnsupportedPrivateRequest",
479 		"GLXBadFBConfig",
480 		"GLXBadPbuffer",
481 		"GLXBadCurrentDrawable",
482 		"GLXBadWindow",
483 		"GLXBadProfileARB"
484 	};
485 
486 	if (err < 0) {
487 		return "non-GLX error";
488 	} else if (err >= ARRAY_SIZE(error_table)) {
489 		return "unknown GLX error";
490 	} else {
491 		return error_table[err];
492 	}
493 }
494 
495 /**
496  * Get the procedure adddresses for a group of function names
497  *
498  * \note
499  * If any call to \c glXGetProcAddress fails, this function will call
500  * \c piglit_report_result with \c PIGLIT_FAIL.
501  */
502 void
piglit_glx_get_all_proc_addresses(const struct piglit_glx_proc_reference * procedures,unsigned num)503 piglit_glx_get_all_proc_addresses(const struct piglit_glx_proc_reference *procedures,
504 				  unsigned num)
505 {
506 	unsigned i;
507 
508 	for (i = 0; i < num; i++) {
509 		*procedures[i].procedure =
510 			glXGetProcAddress((const GLubyte *) procedures[i].name);
511 		if (*procedures[i].procedure == NULL) {
512 			fprintf(stderr,
513 				"Failed to get function pointer for %s.\n",
514 				procedures[i].name);
515 			piglit_report_result(PIGLIT_FAIL);
516 		}
517 	}
518 }
519