File: | tools/cafe-session-check-accelerated-gl-helper.c |
Warning: | line 507, column 17 This statement is never executed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* |
2 | * Copyright (C) 2010 Novell, Inc. |
3 | * Copyright (C) 2006-2009 Red Hat, Inc. |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation; either version 2 of the License, or |
8 | * (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License |
16 | * along with this program; if not, write to the Free Software |
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
18 | * |
19 | * Author: |
20 | * Vincent Untz <vuntz@gnome.org> |
21 | * |
22 | * Most of the code comes from desktop-effects [1], released under GPLv2+. |
23 | * desktop-effects was written by: |
24 | * Soren Sandmann <sandmann@redhat.com> |
25 | * |
26 | * [1] http://git.fedorahosted.org/git/?p=desktop-effects.git;a=blob_plain;f=desktop-effects.c;hb=HEAD |
27 | */ |
28 | |
29 | /* |
30 | * Here's the rationale behind this helper, quoting Owen, in his mail to the |
31 | * release team: |
32 | * (http://mail.gnome.org/archives/release-team/2010-June/msg00079.html) |
33 | * |
34 | * """ |
35 | * There are some limits to what we can do here automatically without |
36 | * knowing anything about the driver situation on the system. The basic |
37 | * problem is that there are all sorts of suck: |
38 | * |
39 | * * No GL at all. This typically only happens if a system is |
40 | * misconfigured. |
41 | * |
42 | * * Only software GL. This one is easy to detect. We have code in |
43 | * the Fedora desktop-effects tool, etc. |
44 | * |
45 | * * GL that isn't featureful enough. (Tiny texture size limits, no |
46 | * texture-from-pixmap, etc.) Possible to detect with more work, but |
47 | * largely a fringe case. |
48 | * |
49 | * * Buggy GL. This isn't possible to detect. Except for the case where |
50 | * all GL programs crash. For that reason, we probably don't want |
51 | * gnome-session to directly try and do any GL detection; better to |
52 | * use a helper binary. |
53 | * |
54 | * * Horribly slow hardware GL. We could theoretically develop some sort |
55 | * of benchmark, but it's a tricky area. And how slow is too slow? |
56 | * """ |
57 | * |
58 | * Some other tools are doing similar checks: |
59 | * - desktop-effects (Fedora Config Tool) [1] |
60 | * - drak3d (Mandriva Config Tool) [2] |
61 | * - compiz-manager (Compiz wrapper) [3] |
62 | * |
63 | * [1] http://git.fedorahosted.org/git/?p=desktop-effects.git;a=blob_plain;f=desktop-effects.c;hb=HEAD |
64 | * [2] http://svn.mandriva.com/cgi-bin/viewvc.cgi/soft/drak3d/trunk/lib/Xconfig/glx.pm?view=markup |
65 | * [3] http://git.compiz.org/fusion/misc/compiz-manager/tree/compiz-manager |
66 | */ |
67 | |
68 | /* for strcasestr */ |
69 | #define _GNU_SOURCE |
70 | |
71 | #include <ctype.h> |
72 | #include <locale.h> |
73 | #include <stdio.h> |
74 | #include <stdlib.h> |
75 | #include <string.h> |
76 | #include <glib.h> |
77 | |
78 | #include <regex.h> |
79 | |
80 | #ifdef __FreeBSD__ |
81 | #include <kenv.h> |
82 | #endif |
83 | |
84 | #include <X11/Xlib.h> |
85 | #include <X11/Xatom.h> |
86 | #include <X11/extensions/Xcomposite.h> |
87 | #include <GL/gl.h> |
88 | #include <GL/glx.h> |
89 | |
90 | #include "cafe-session-check-accelerated-common.h" |
91 | |
92 | #define SIZE_UNSET0 0 |
93 | #define SIZE_ERROR-1 -1 |
94 | static int max_texture_size = SIZE_UNSET0; |
95 | static int max_renderbuffer_size = SIZE_UNSET0; |
96 | static gboolean has_llvmpipe = FALSE(0); |
97 | |
98 | static inline void |
99 | _print_error (const char *str) |
100 | { |
101 | fprintf (stderrstderr, "cafe-session-is-accelerated: %s\n", str); |
102 | } |
103 | |
104 | #define CMDLINE_UNSET-1 -1 |
105 | #define CMDLINE_NON_FALLBACK_FORCED0 0 |
106 | #define CMDLINE_FALLBACK_FORCED1 1 |
107 | |
108 | #if defined(__linux__1) |
109 | static int |
110 | _parse_kcmdline (void) |
111 | { |
112 | int ret = CMDLINE_UNSET-1; |
113 | GRegex *regex; |
114 | GMatchInfo *match; |
115 | char *contents; |
116 | char *word; |
117 | const char *arg; |
118 | |
119 | if (!g_file_get_contents ("/proc/cmdline", &contents, NULL((void*)0), NULL((void*)0))) |
120 | return ret; |
121 | |
122 | regex = g_regex_new ("cafe.fallback=(\\S+)", 0, G_REGEX_MATCH_NOTEMPTY, NULL((void*)0)); |
123 | if (!g_regex_match (regex, contents, G_REGEX_MATCH_NOTEMPTY, &match)) |
124 | goto out; |
125 | |
126 | word = g_match_info_fetch (match, 0); |
127 | g_debug ("Found command-line match '%s'", word); |
128 | arg = word + strlen ("cafe.fallback="); |
129 | if (*arg != '0' && *arg != '1') |
130 | fprintf (stderrstderr, "cafe-session-check-accelerated: Invalid value '%s' for cafe.fallback passed in kernel command line.\n", arg); |
131 | else |
132 | ret = atoi (arg); |
133 | g_free (word); |
134 | |
135 | out: |
136 | g_match_info_free (match); |
137 | g_regex_unref (regex); |
138 | g_free (contents); |
139 | |
140 | g_debug ("Command-line parsed to %d", ret); |
141 | |
142 | return ret; |
143 | } |
144 | #elif defined(__FreeBSD__) |
145 | static int |
146 | _parse_kcmdline (void) |
147 | { |
148 | int ret = CMDLINE_UNSET-1; |
149 | char value[KENV_MVALLEN]; |
150 | |
151 | /* a compile time check to avoid unexpected stack overflow */ |
152 | _Static_assert(KENV_MVALLEN < 1024 * 1024, "KENV_MVALLEN is too large"); |
153 | |
154 | if (kenv (KENV_GET, "cafe.fallback", value, KENV_MVALLEN) == -1) |
155 | return ret; |
156 | |
157 | if (*value != '0' && *value != '1') |
158 | fprintf (stderrstderr, "cafe-session-is-accelerated: Invalid value '%s' for cafe.fallback passed in kernel environment.\n", value); |
159 | else |
160 | ret = atoi (value); |
161 | |
162 | g_debug ("Kernel environment parsed to %d", ret); |
163 | |
164 | return ret; |
165 | } |
166 | #else |
167 | static int |
168 | _parse_kcmdline (void) |
169 | { |
170 | return CMDLINE_UNSET-1; |
171 | } |
172 | #endif |
173 | |
174 | static gboolean |
175 | _has_composite (Display *display) |
176 | { |
177 | int dummy1, dummy2; |
178 | |
179 | return XCompositeQueryExtension (display, &dummy1, &dummy2); |
180 | } |
181 | |
182 | static gboolean |
183 | _is_comment (const char *line) |
184 | { |
185 | while (*line && isspace(*line)((*__ctype_b_loc ())[(int) ((*line))] & (unsigned short int ) _ISspace)) |
186 | line++; |
187 | |
188 | if (*line == '#' || *line == '\0') |
189 | return TRUE(!(0)); |
190 | return FALSE(0); |
191 | } |
192 | |
193 | static gboolean |
194 | _is_gl_renderer_blacklisted (const char *renderer) |
195 | { |
196 | FILE *blacklist; |
197 | char *line = NULL((void*)0); |
198 | size_t line_len = 0; |
199 | gboolean ret = TRUE(!(0)); |
200 | |
201 | blacklist = fopen(PKGDATADIR"/usr/share/cafe-session-manager" "/hardware-compatibility", "r"); |
202 | if (blacklist == NULL((void*)0)) |
203 | goto out; |
204 | |
205 | while (getline (&line, &line_len, blacklist) != -1) { |
206 | int whitelist = 0; |
207 | const char *re_str; |
208 | regex_t re; |
209 | int status; |
210 | |
211 | if (line == NULL((void*)0)) |
212 | break; |
213 | |
214 | /* Drop trailing \n */ |
215 | line[strlen(line) - 1] = '\0'; |
216 | |
217 | if (_is_comment (line)) { |
218 | free (line); |
219 | line = NULL((void*)0); |
220 | continue; |
221 | } |
222 | |
223 | if (line[0] == '+') |
224 | whitelist = 1; |
225 | else if (line[0] == '-') |
226 | whitelist = 0; |
227 | else { |
228 | _print_error ("Invalid syntax in this line for hardware compatibility:"); |
229 | _print_error (line); |
230 | free (line); |
231 | line = NULL((void*)0); |
232 | continue; |
233 | } |
234 | |
235 | re_str = line + 1; |
236 | |
237 | if (regcomp (&re, re_str, REG_EXTENDED1|REG_ICASE(1 << 1)|REG_NOSUB(1 << 3)) != 0) { |
238 | _print_error ("Cannot use this regular expression for hardware compatibility:"); |
239 | _print_error (re_str); |
240 | } else { |
241 | status = regexec (&re, renderer, 0, NULL((void*)0), 0); |
242 | regfree(&re); |
243 | |
244 | if (status == 0) { |
245 | if (whitelist) |
246 | ret = FALSE(0); |
247 | goto out; |
248 | } |
249 | } |
250 | |
251 | free (line); |
252 | line = NULL((void*)0); |
253 | } |
254 | |
255 | ret = FALSE(0); |
256 | |
257 | out: |
258 | if (line != NULL((void*)0)) |
259 | free (line); |
260 | |
261 | if (blacklist != NULL((void*)0)) |
262 | fclose (blacklist); |
263 | |
264 | return ret; |
265 | } |
266 | |
267 | static char * |
268 | _get_hardware_gl (Display *display) |
269 | { |
270 | int screen; |
271 | Window root; |
272 | XVisualInfo *visual = NULL((void*)0); |
273 | GLXContext context = NULL((void*)0); |
274 | XSetWindowAttributes cwa = { 0 }; |
275 | Window window = None0L; |
276 | char *renderer = NULL((void*)0); |
277 | |
278 | int attrlist[] = { |
279 | GLX_RGBA4, |
280 | GLX_RED_SIZE8, 1, |
281 | GLX_GREEN_SIZE9, 1, |
282 | GLX_BLUE_SIZE10, 1, |
283 | GLX_DOUBLEBUFFER5, |
284 | None0L |
285 | }; |
286 | |
287 | screen = DefaultScreen (display)(((_XPrivDisplay)(display))->default_screen); |
288 | root = RootWindow (display, screen)((&((_XPrivDisplay)(display))->screens[screen])->root ); |
289 | |
290 | visual = glXChooseVisual (display, screen, attrlist); |
291 | if (!visual) |
292 | goto out; |
293 | |
294 | context = glXCreateContext (display, visual, NULL((void*)0), True1); |
295 | if (!context) |
296 | goto out; |
297 | |
298 | cwa.colormap = XCreateColormap (display, root, |
299 | visual->visual, AllocNone0); |
300 | cwa.background_pixel = 0; |
301 | cwa.border_pixel = 0; |
302 | window = XCreateWindow (display, root, |
303 | 0, 0, 1, 1, 0, |
304 | visual->depth, InputOutput1, visual->visual, |
305 | CWColormap(1L<<13) | CWBackPixel(1L<<1) | CWBorderPixel(1L<<3), |
306 | &cwa); |
307 | |
308 | if (!glXMakeCurrent (display, window, context)) |
309 | goto out; |
310 | |
311 | renderer = g_strdup ((const char *) glGetString (GL_RENDERER))g_strdup_inline ((const char *) glGetString (0x1F01)); |
312 | if (_is_gl_renderer_blacklisted (renderer)) { |
313 | g_clear_pointer (&renderer, g_free)do { _Static_assert (sizeof *(&renderer) == sizeof (gpointer ), "Expression evaluates to false"); __typeof__ ((&renderer )) _pp = (&renderer); __typeof__ (*(&renderer)) _ptr = *_pp; *_pp = ((void*)0); if (_ptr) (g_free) (_ptr); } while ( 0); |
314 | goto out; |
315 | } |
316 | if (renderer && strcasestr (renderer, "llvmpipe")) |
317 | has_llvmpipe = TRUE(!(0)); |
318 | |
319 | /* we need to get the max texture and renderbuffer sizes while we have |
320 | * a context, but we'll check their values later */ |
321 | |
322 | glGetIntegerv (GL_MAX_TEXTURE_SIZE0x0D33, &max_texture_size); |
323 | if (glGetError() != GL_NO_ERROR0) |
324 | max_texture_size = SIZE_ERROR-1; |
325 | |
326 | glGetIntegerv (GL_MAX_RENDERBUFFER_SIZE_EXT0x84E8, &max_renderbuffer_size); |
327 | if (glGetError() != GL_NO_ERROR0) |
328 | max_renderbuffer_size = SIZE_ERROR-1; |
329 | |
330 | out: |
331 | glXMakeCurrent (display, None0L, None0L); |
332 | if (context) |
333 | glXDestroyContext (display, context); |
334 | if (window) |
335 | XDestroyWindow (display, window); |
336 | if (cwa.colormap) |
337 | XFreeColormap (display, cwa.colormap); |
338 | |
339 | return renderer; |
340 | } |
341 | |
342 | static gboolean |
343 | _has_extension (const char *extension_list, |
344 | const char *extension) |
345 | { |
346 | char **extensions; |
347 | guint i; |
348 | gboolean ret; |
349 | |
350 | g_return_val_if_fail (extension != NULL, TRUE)do { if ((extension != ((void*)0))) { } else { g_return_if_fail_warning (((gchar*) 0), ((const char*) (__func__)), "extension != NULL" ); return ((!(0))); } } while (0); |
351 | |
352 | /* Extension_list is one big string, containing extensions |
353 | * separated by spaces. */ |
354 | if (extension_list == NULL((void*)0)) |
355 | return FALSE(0); |
356 | |
357 | ret = FALSE(0); |
358 | |
359 | extensions = g_strsplit (extension_list, " ", -1); |
360 | if (extensions == NULL((void*)0)) |
361 | return FALSE(0); |
362 | |
363 | for (i = 0; extensions[i] != NULL((void*)0); i++) { |
364 | if (g_str_equal (extensions[i], extension)(strcmp ((const char *) (extensions[i]), (const char *) (extension )) == 0)) { |
365 | ret = TRUE(!(0)); |
366 | break; |
367 | } |
368 | } |
369 | |
370 | g_strfreev (extensions); |
371 | |
372 | return ret; |
373 | } |
374 | |
375 | static gboolean |
376 | _has_texture_from_pixmap (Display *display) |
377 | { |
378 | int screen; |
379 | const char *server_extensions; |
380 | const char *client_extensions; |
381 | gboolean ret = FALSE(0); |
382 | |
383 | screen = DefaultScreen (display)(((_XPrivDisplay)(display))->default_screen); |
384 | |
385 | server_extensions = glXQueryServerString (display, screen, |
386 | GLX_EXTENSIONS3); |
387 | if (!_has_extension (server_extensions, |
388 | "GLX_EXT_texture_from_pixmap")) |
389 | goto out; |
390 | |
391 | client_extensions = glXGetClientString (display, GLX_EXTENSIONS3); |
392 | if (!_has_extension (client_extensions, |
393 | "GLX_EXT_texture_from_pixmap")) |
394 | goto out; |
395 | |
396 | ret = TRUE(!(0)); |
397 | |
398 | out: |
399 | return ret; |
400 | } |
401 | |
402 | static void |
403 | _set_max_screen_size_property (Display *display, int screen, int size) |
404 | { |
405 | Atom max_screen_size_atom; |
406 | |
407 | max_screen_size_atom = XInternAtom (display, "_GNOME_MAX_SCREEN_SIZE", |
408 | False0); |
409 | |
410 | /* Will be read by gnome-settings-daemon and |
411 | * gnome-control-center to avoid display configurations where 3D |
412 | * is not available (and would break gnome-shell) */ |
413 | XChangeProperty (display, RootWindow(display, screen)((&((_XPrivDisplay)(display))->screens[screen])->root ), |
414 | max_screen_size_atom, |
415 | XA_CARDINAL((Atom) 6), 32, PropModeReplace0, |
416 | (unsigned char *)&size, 1); |
417 | |
418 | XSync(display, False0); |
419 | } |
420 | |
421 | static gboolean |
422 | _is_max_texture_size_big_enough (Display *display) |
423 | { |
424 | int screen, size; |
425 | |
426 | screen = DefaultScreen (display)(((_XPrivDisplay)(display))->default_screen); |
427 | size = MIN(max_renderbuffer_size, max_texture_size)(((max_renderbuffer_size) < (max_texture_size)) ? (max_renderbuffer_size ) : (max_texture_size)); |
428 | if (size < DisplayWidth (display, screen)((&((_XPrivDisplay)(display))->screens[screen])->width ) || |
429 | size < DisplayHeight (display, screen)((&((_XPrivDisplay)(display))->screens[screen])->height )) |
430 | return FALSE(0); |
431 | |
432 | _set_max_screen_size_property (display, screen, size); |
433 | |
434 | return TRUE(!(0)); |
435 | } |
436 | |
437 | static gboolean print_renderer = FALSE(0); |
438 | |
439 | static const GOptionEntry entries[] = { |
440 | { "print-renderer", 'p', 0, G_OPTION_ARG_NONE, &print_renderer, "Print GL renderer name", NULL((void*)0) }, |
441 | { NULL((void*)0) }, |
442 | }; |
443 | |
444 | int |
445 | main (int argc, char **argv) |
446 | { |
447 | int kcmdline_parsed; |
448 | Display *display = NULL((void*)0); |
449 | int ret = HELPER_NO_ACCEL1; |
450 | GOptionContext *context; |
451 | GError *error = NULL((void*)0); |
452 | char *renderer = NULL((void*)0); |
453 | |
454 | setlocale (LC_ALL6, ""); |
455 | |
456 | context = g_option_context_new (NULL((void*)0)); |
457 | g_option_context_add_main_entries (context, entries, NULL((void*)0)); |
458 | |
459 | if (!g_option_context_parse (context, &argc, &argv, &error)) { |
460 | g_error ("Can't parse command line: %s\n", error->message); |
461 | g_error_free (error); |
462 | goto out; |
463 | } |
464 | |
465 | kcmdline_parsed = _parse_kcmdline (); |
466 | if (kcmdline_parsed > CMDLINE_UNSET-1) { |
467 | if (kcmdline_parsed == CMDLINE_NON_FALLBACK_FORCED0) { |
468 | _print_error ("Non-fallback mode forced by kernel command line."); |
469 | ret = HELPER_ACCEL0; |
470 | goto out; |
471 | } else if (kcmdline_parsed == CMDLINE_FALLBACK_FORCED1) { |
472 | _print_error ("Fallback mode forced by kernel command line."); |
473 | goto out; |
474 | } |
475 | } |
476 | |
477 | display = XOpenDisplay (NULL((void*)0)); |
478 | if (!display) { |
479 | _print_error ("No X display."); |
480 | goto out; |
481 | } |
482 | |
483 | if (!_has_composite (display)) { |
484 | _print_error ("No composite extension."); |
485 | goto out; |
486 | } |
487 | |
488 | renderer = _get_hardware_gl (display); |
489 | if (!renderer) { |
490 | _print_error ("No hardware 3D support."); |
491 | goto out; |
492 | } |
493 | |
494 | if (!_has_texture_from_pixmap (display)) { |
495 | _print_error ("No GLX_EXT_texture_from_pixmap support."); |
496 | goto out; |
497 | } |
498 | |
499 | if (!_is_max_texture_size_big_enough (display)) { |
500 | _print_error ("GL_MAX_{TEXTURE,RENDERBUFFER}_SIZE is too small."); |
501 | goto out; |
502 | } |
503 | |
504 | ret = has_llvmpipe ? HELPER_SOFTWARE_RENDERING2 : HELPER_ACCEL0; |
505 | |
506 | if (print_renderer) |
507 | g_print ("%s", renderer); |
This statement is never executed | |
508 | |
509 | out: |
510 | if (display) |
511 | XCloseDisplay (display); |
512 | g_free (renderer); |
513 | |
514 | return ret; |
515 | } |