summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrbuj <[email protected]>2019-04-17 23:53:35 +0200
committerRobert Antoni Buj Gelonch <[email protected]>2019-04-19 21:26:31 +0200
commitda0e17fbb19958d838a9ea6093a05384aa6fc207 (patch)
tree8fa500bceb8954544bf46426e93a6ca3b2f8a4ce
parentc4c134d1a1745baa3491e6f6d999f8e6f1ef5239 (diff)
downloadmate-session-manager-da0e17fbb19958d838a9ea6093a05384aa6fc207.tar.bz2
mate-session-manager-da0e17fbb19958d838a9ea6093a05384aa6fc207.tar.xz
Add mate_session_check_accelerated helpers
tools/mate-session-check-accelerated.{c,h} tools/mate-session-check-accelerated-gles-helper.c tools/mate-session-check-accelerated-gl-helper.c based on https://github.com/GNOME/gnome-session/commit/3aafcf0f0129e28b5c52f32284f1164a93867ea3 configure.ac tools/Makefile.am based on: based on https://github.com/GNOME/gnome-session/tree/eeefdc8e1a436d0c58cea756e8a3ea63e59145b5
-rw-r--r--.travis.yml5
-rw-r--r--configure.ac3
-rw-r--r--tools/Makefile.am45
-rw-r--r--tools/mate-session-check-accelerated-common.h29
-rw-r--r--tools/mate-session-check-accelerated-gl-helper.c515
-rw-r--r--tools/mate-session-check-accelerated-gles-helper.c201
-rw-r--r--tools/mate-session-check-accelerated.c312
7 files changed, 1110 insertions, 0 deletions
diff --git a/.travis.yml b/.travis.yml
index c60f817..c932f0c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -58,6 +58,8 @@ requires:
- git
- intltool
- libdbus-glib-1-dev
+ - libgl1-mesa-dev
+ - libgles2-mesa-dev
- libglib2.0-dev
- libgtk-3-dev
- libice-dev
@@ -86,6 +88,7 @@ requires:
- libXtst-devel
- make
- mate-common
+ - mesa-libGLES-devel
- pangox-compat-devel
- redhat-rpm-config
- systemd-devel
@@ -96,6 +99,8 @@ requires:
- git
- intltool
- libdbus-glib-1-dev
+ - libgl1-mesa-dev
+ - libgles2-mesa-dev
- libglib2.0-dev
- libgtk-3-dev
- libice-dev
diff --git a/configure.ac b/configure.ac
index a71617f..8806c72 100644
--- a/configure.ac
+++ b/configure.ac
@@ -61,6 +61,9 @@ PKG_CHECK_MODULES(X11, x11)
PKG_CHECK_MODULES(SM, sm)
PKG_CHECK_MODULES(ICE, ice)
PKG_CHECK_MODULES(XEXT, xext xau)
+PKG_CHECK_MODULES(GTK3, gtk+-3.0 >= $GTK_REQUIRED)
+PKG_CHECK_MODULES(GL_TEST, xcomposite gl glib-2.0 epoxy)
+PKG_CHECK_MODULES(GLES_TEST, egl glesv2)
PKG_CHECK_MODULES(DBUS_GLIB, dbus-glib-1 >= $DBUS_GLIB_REQUIRED)
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 5680214..01734e5 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -1,4 +1,8 @@
bin_PROGRAMS = mate-session-save mate-session-inhibit
+libexec_PROGRAMS = \
+ mate-session-check-accelerated \
+ mate-session-check-accelerated-gl-helper \
+ mate-session-check-accelerated-gles-helper
AM_CPPFLAGS = \
$(MATE_SESSION_CFLAGS) \
@@ -27,4 +31,45 @@ mate_session_inhibit_CPPFLAGS = \
mate_session_inhibit_LDADD = \
$(MATE_SESSION_LIBS)
+mate_session_check_accelerated_gles_helper_SOURCES = \
+ mate-session-check-accelerated-common.h \
+ mate-session-check-accelerated-gles-helper.c
+
+mate_session_check_accelerated_gles_helper_CPPFLAGS = \
+ -DPKGDATADIR=\""$(pkgdatadir)"\" \
+ $(GLES_TEST_CFLAGS) \
+ $(GTK3_CFLAGS)
+
+mate_session_check_accelerated_gles_helper_LDADD = \
+ $(GLES_TEST_LIBS) \
+ $(GTK3_LIBS) \
+ $(X11_LIBS)
+
+mate_session_check_accelerated_gl_helper_SOURCES = \
+ mate-session-check-accelerated-common.h \
+ mate-session-check-accelerated-gl-helper.c
+
+mate_session_check_accelerated_gl_helper_CPPFLAGS = \
+ -DPKGDATADIR=\""$(pkgdatadir)"\" \
+ $(GL_TEST_CFLAGS)
+
+mate_session_check_accelerated_gl_helper_LDADD = \
+ $(GL_TEST_LIBS) \
+ $(X11_LIBS)
+
+mate_session_check_accelerated_SOURCES = \
+ mate-session-check-accelerated-common.h \
+ mate-session-check-accelerated.c
+
+mate_session_check_accelerated_CPPFLAGS = \
+ -DLIBEXECDIR=\""$(libexecdir)"\" \
+ $(AM_CPPFLAGS) \
+ $(GTK3_CFLAGS) \
+ $(GL_TEST_CFLAGS)
+
+mate_session_check_accelerated_LDADD = \
+ $(GTK3_LIBS) \
+ $(X11_LIBS) \
+ $(GL_TEST_LIBS)
+
-include $(top_srcdir)/git.mk
diff --git a/tools/mate-session-check-accelerated-common.h b/tools/mate-session-check-accelerated-common.h
new file mode 100644
index 0000000..59631d9
--- /dev/null
+++ b/tools/mate-session-check-accelerated-common.h
@@ -0,0 +1,29 @@
+/* -*- mode:c; c-basic-offset: 8; indent-tabs-mode: nil; -*- */
+/* Tool to set the property _GNOME_SESSION_ACCELERATED on the root window */
+/*
+ * Copyright (C) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author:
+ * Frederic Crozat <[email protected]>
+ */
+
+/* Exit value for helper */
+#define HELPER_ACCEL 0
+#define HELPER_NO_ACCEL 1
+#define HELPER_SOFTWARE_RENDERING 2
+
diff --git a/tools/mate-session-check-accelerated-gl-helper.c b/tools/mate-session-check-accelerated-gl-helper.c
new file mode 100644
index 0000000..0526336
--- /dev/null
+++ b/tools/mate-session-check-accelerated-gl-helper.c
@@ -0,0 +1,515 @@
+/*
+ * Copyright (C) 2010 Novell, Inc.
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author:
+ * Vincent Untz <[email protected]>
+ *
+ * Most of the code comes from desktop-effects [1], released under GPLv2+.
+ * desktop-effects was written by:
+ * Soren Sandmann <[email protected]>
+ *
+ * [1] http://git.fedorahosted.org/git/?p=desktop-effects.git;a=blob_plain;f=desktop-effects.c;hb=HEAD
+ */
+
+/*
+ * Here's the rationale behind this helper, quoting Owen, in his mail to the
+ * release team:
+ * (http://mail.gnome.org/archives/release-team/2010-June/msg00079.html)
+ *
+ * """
+ * There are some limits to what we can do here automatically without
+ * knowing anything about the driver situation on the system. The basic
+ * problem is that there are all sorts of suck:
+ *
+ * * No GL at all. This typically only happens if a system is
+ * misconfigured.
+ *
+ * * Only software GL. This one is easy to detect. We have code in
+ * the Fedora desktop-effects tool, etc.
+ *
+ * * GL that isn't featureful enough. (Tiny texture size limits, no
+ * texture-from-pixmap, etc.) Possible to detect with more work, but
+ * largely a fringe case.
+ *
+ * * Buggy GL. This isn't possible to detect. Except for the case where
+ * all GL programs crash. For that reason, we probably don't want
+ * gnome-session to directly try and do any GL detection; better to
+ * use a helper binary.
+ *
+ * * Horribly slow hardware GL. We could theoretically develop some sort
+ * of benchmark, but it's a tricky area. And how slow is too slow?
+ * """
+ *
+ * Some other tools are doing similar checks:
+ * - desktop-effects (Fedora Config Tool) [1]
+ * - drak3d (Mandriva Config Tool) [2]
+ * - compiz-manager (Compiz wrapper) [3]
+ *
+ * [1] http://git.fedorahosted.org/git/?p=desktop-effects.git;a=blob_plain;f=desktop-effects.c;hb=HEAD
+ * [2] http://svn.mandriva.com/cgi-bin/viewvc.cgi/soft/drak3d/trunk/lib/Xconfig/glx.pm?view=markup
+ * [3] http://git.compiz.org/fusion/misc/compiz-manager/tree/compiz-manager
+ */
+
+/* for strcasestr */
+#define _GNU_SOURCE
+
+#include <ctype.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#include <regex.h>
+
+#ifdef __FreeBSD__
+#include <kenv.h>
+#endif
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/Xcomposite.h>
+#include <GL/gl.h>
+#include <GL/glx.h>
+
+#include "mate-session-check-accelerated-common.h"
+
+#define SIZE_UNSET 0
+#define SIZE_ERROR -1
+static int max_texture_size = SIZE_UNSET;
+static int max_renderbuffer_size = SIZE_UNSET;
+static gboolean has_llvmpipe = FALSE;
+
+static inline void
+_print_error (const char *str)
+{
+ fprintf (stderr, "mate-session-is-accelerated: %s\n", str);
+}
+
+#define CMDLINE_UNSET -1
+#define CMDLINE_NON_FALLBACK_FORCED 0
+#define CMDLINE_FALLBACK_FORCED 1
+
+#if defined(__linux__)
+static int
+_parse_kcmdline (void)
+{
+ int ret = CMDLINE_UNSET;
+ GRegex *regex;
+ GMatchInfo *match;
+ char *contents;
+ char *word;
+ const char *arg;
+
+ if (!g_file_get_contents ("/proc/cmdline", &contents, NULL, NULL))
+ return ret;
+
+ regex = g_regex_new ("mate.fallback=(\\S+)", 0, G_REGEX_MATCH_NOTEMPTY, NULL);
+ if (!g_regex_match (regex, contents, G_REGEX_MATCH_NOTEMPTY, &match))
+ goto out;
+
+ word = g_match_info_fetch (match, 0);
+ g_debug ("Found command-line match '%s'", word);
+ arg = word + strlen ("mate.fallback=");
+ if (*arg != '0' && *arg != '1')
+ fprintf (stderr, "mate-session-check-accelerated: Invalid value '%s' for mate.fallback passed in kernel command line.\n", arg);
+ else
+ ret = atoi (arg);
+ g_free (word);
+
+out:
+ g_match_info_free (match);
+ g_regex_unref (regex);
+ g_free (contents);
+
+ g_debug ("Command-line parsed to %d", ret);
+
+ return ret;
+}
+#elif defined(__FreeBSD__)
+static int
+_parse_kcmdline (void)
+{
+ int ret = CMDLINE_UNSET;
+ char value[KENV_MVALLEN];
+
+ /* a compile time check to avoid unexpected stack overflow */
+ _Static_assert(KENV_MVALLEN < 1024 * 1024, "KENV_MVALLEN is too large");
+
+ if (kenv (KENV_GET, "mate.fallback", value, KENV_MVALLEN) == -1)
+ return ret;
+
+ if (*value != '0' && *value != '1')
+ fprintf (stderr, "mate-session-is-accelerated: Invalid value '%s' for mate.fallback passed in kernel environment.\n", value);
+ else
+ ret = atoi (value);
+
+ g_debug ("Kernel environment parsed to %d", ret);
+
+ return ret;
+}
+#else
+static int
+_parse_kcmdline (void)
+{
+ return CMDLINE_UNSET;
+}
+#endif
+
+static gboolean
+_has_composite (Display *display)
+{
+ int dummy1, dummy2;
+
+ return XCompositeQueryExtension (display, &dummy1, &dummy2);
+}
+
+static gboolean
+_is_comment (const char *line)
+{
+ while (*line && isspace(*line))
+ line++;
+
+ if (*line == '#' || *line == '\0')
+ return TRUE;
+ return FALSE;
+}
+
+static gboolean
+_is_gl_renderer_blacklisted (const char *renderer)
+{
+ FILE *blacklist;
+ char *line = NULL;
+ size_t line_len = 0;
+ gboolean ret = TRUE;
+
+ blacklist = fopen(PKGDATADIR "/hardware-compatibility", "r");
+ if (blacklist == NULL)
+ goto out;
+
+ while (getline (&line, &line_len, blacklist) != -1) {
+ int whitelist = 0;
+ const char *re_str;
+ regex_t re;
+ int status;
+
+ if (line == NULL)
+ break;
+
+ /* Drop trailing \n */
+ line[strlen(line) - 1] = '\0';
+
+ if (_is_comment (line)) {
+ free (line);
+ line = NULL;
+ continue;
+ }
+
+ if (line[0] == '+')
+ whitelist = 1;
+ else if (line[0] == '-')
+ whitelist = 0;
+ else {
+ _print_error ("Invalid syntax in this line for hardware compatibility:");
+ _print_error (line);
+ free (line);
+ line = NULL;
+ continue;
+ }
+
+ re_str = line + 1;
+
+ if (regcomp (&re, re_str, REG_EXTENDED|REG_ICASE|REG_NOSUB) != 0) {
+ _print_error ("Cannot use this regular expression for hardware compatibility:");
+ _print_error (re_str);
+ } else {
+ status = regexec (&re, renderer, 0, NULL, 0);
+ regfree(&re);
+
+ if (status == 0) {
+ if (whitelist)
+ ret = FALSE;
+ goto out;
+ }
+ }
+
+ free (line);
+ line = NULL;
+ }
+
+ ret = FALSE;
+
+out:
+ if (line != NULL)
+ free (line);
+
+ if (blacklist != NULL)
+ fclose (blacklist);
+
+ return ret;
+}
+
+static char *
+_get_hardware_gl (Display *display)
+{
+ int screen;
+ Window root;
+ XVisualInfo *visual = NULL;
+ GLXContext context = NULL;
+ XSetWindowAttributes cwa = { 0 };
+ Window window = None;
+ char *renderer = NULL;
+
+ int attrlist[] = {
+ GLX_RGBA,
+ GLX_RED_SIZE, 1,
+ GLX_GREEN_SIZE, 1,
+ GLX_BLUE_SIZE, 1,
+ GLX_DOUBLEBUFFER,
+ None
+ };
+
+ screen = DefaultScreen (display);
+ root = RootWindow (display, screen);
+
+ visual = glXChooseVisual (display, screen, attrlist);
+ if (!visual)
+ goto out;
+
+ context = glXCreateContext (display, visual, NULL, True);
+ if (!context)
+ goto out;
+
+ cwa.colormap = XCreateColormap (display, root,
+ visual->visual, AllocNone);
+ cwa.background_pixel = 0;
+ cwa.border_pixel = 0;
+ window = XCreateWindow (display, root,
+ 0, 0, 1, 1, 0,
+ visual->depth, InputOutput, visual->visual,
+ CWColormap | CWBackPixel | CWBorderPixel,
+ &cwa);
+
+ if (!glXMakeCurrent (display, window, context))
+ goto out;
+
+ renderer = g_strdup ((const char *) glGetString (GL_RENDERER));
+ if (_is_gl_renderer_blacklisted (renderer)) {
+ g_clear_pointer (&renderer, g_free);
+ goto out;
+ }
+ if (renderer && strcasestr (renderer, "llvmpipe"))
+ has_llvmpipe = TRUE;
+
+ /* we need to get the max texture and renderbuffer sizes while we have
+ * a context, but we'll check their values later */
+
+ glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_texture_size);
+ if (glGetError() != GL_NO_ERROR)
+ max_texture_size = SIZE_ERROR;
+
+ glGetIntegerv (GL_MAX_RENDERBUFFER_SIZE_EXT, &max_renderbuffer_size);
+ if (glGetError() != GL_NO_ERROR)
+ max_renderbuffer_size = SIZE_ERROR;
+
+out:
+ glXMakeCurrent (display, None, None);
+ if (context)
+ glXDestroyContext (display, context);
+ if (window)
+ XDestroyWindow (display, window);
+ if (cwa.colormap)
+ XFreeColormap (display, cwa.colormap);
+
+ return renderer;
+}
+
+static gboolean
+_has_extension (const char *extension_list,
+ const char *extension)
+{
+ char **extensions;
+ guint i;
+ gboolean ret;
+
+ g_return_val_if_fail (extension != NULL, TRUE);
+
+ /* Extension_list is one big string, containing extensions
+ * separated by spaces. */
+ if (extension_list == NULL)
+ return FALSE;
+
+ ret = FALSE;
+
+ extensions = g_strsplit (extension_list, " ", -1);
+ if (extensions == NULL)
+ return FALSE;
+
+ for (i = 0; extensions[i] != NULL; i++) {
+ if (g_str_equal (extensions[i], extension)) {
+ ret = TRUE;
+ break;
+ }
+ }
+
+ g_strfreev (extensions);
+
+ return ret;
+}
+
+static gboolean
+_has_texture_from_pixmap (Display *display)
+{
+ int screen;
+ const char *server_extensions;
+ const char *client_extensions;
+ gboolean ret = FALSE;
+
+ screen = DefaultScreen (display);
+
+ server_extensions = glXQueryServerString (display, screen,
+ GLX_EXTENSIONS);
+ if (!_has_extension (server_extensions,
+ "GLX_EXT_texture_from_pixmap"))
+ goto out;
+
+ client_extensions = glXGetClientString (display, GLX_EXTENSIONS);
+ if (!_has_extension (client_extensions,
+ "GLX_EXT_texture_from_pixmap"))
+ goto out;
+
+ ret = TRUE;
+
+out:
+ return ret;
+}
+
+static void
+_set_max_screen_size_property (Display *display, int screen, int size)
+{
+ Atom max_screen_size_atom;
+
+ max_screen_size_atom = XInternAtom (display, "_GNOME_MAX_SCREEN_SIZE",
+ False);
+
+ /* Will be read by gnome-settings-daemon and
+ * gnome-control-center to avoid display configurations where 3D
+ * is not available (and would break gnome-shell) */
+ XChangeProperty (display, RootWindow(display, screen),
+ max_screen_size_atom,
+ XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char *)&size, 1);
+
+ XSync(display, False);
+}
+
+static gboolean
+_is_max_texture_size_big_enough (Display *display)
+{
+ int screen, size;
+
+ screen = DefaultScreen (display);
+ size = MIN(max_renderbuffer_size, max_texture_size);
+ if (size < DisplayWidth (display, screen) ||
+ size < DisplayHeight (display, screen))
+ return FALSE;
+
+ _set_max_screen_size_property (display, screen, size);
+
+ return TRUE;
+}
+
+static gboolean print_renderer = FALSE;
+
+static const GOptionEntry entries[] = {
+ { "print-renderer", 'p', 0, G_OPTION_ARG_NONE, &print_renderer, "Print GL renderer name", NULL },
+ { NULL },
+};
+
+int
+main (int argc, char **argv)
+{
+ int kcmdline_parsed;
+ Display *display = NULL;
+ int ret = HELPER_NO_ACCEL;
+ GOptionContext *context;
+ GError *error = NULL;
+ char *renderer = NULL;
+
+ setlocale (LC_ALL, "");
+
+ context = g_option_context_new (NULL);
+ g_option_context_add_main_entries (context, entries, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, &error)) {
+ g_error ("Can't parse command line: %s\n", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ kcmdline_parsed = _parse_kcmdline ();
+ if (kcmdline_parsed > CMDLINE_UNSET) {
+ if (kcmdline_parsed == CMDLINE_NON_FALLBACK_FORCED) {
+ _print_error ("Non-fallback mode forced by kernel command line.");
+ ret = HELPER_ACCEL;
+ goto out;
+ } else if (kcmdline_parsed == CMDLINE_FALLBACK_FORCED) {
+ _print_error ("Fallback mode forced by kernel command line.");
+ goto out;
+ }
+ }
+
+ display = XOpenDisplay (NULL);
+ if (!display) {
+ _print_error ("No X display.");
+ goto out;
+ }
+
+ if (!_has_composite (display)) {
+ _print_error ("No composite extension.");
+ goto out;
+ }
+
+ renderer = _get_hardware_gl (display);
+ if (!renderer) {
+ _print_error ("No hardware 3D support.");
+ goto out;
+ }
+
+ if (!_has_texture_from_pixmap (display)) {
+ _print_error ("No GLX_EXT_texture_from_pixmap support.");
+ goto out;
+ }
+
+ if (!_is_max_texture_size_big_enough (display)) {
+ _print_error ("GL_MAX_{TEXTURE,RENDERBUFFER}_SIZE is too small.");
+ goto out;
+ }
+
+ ret = has_llvmpipe ? HELPER_SOFTWARE_RENDERING : HELPER_ACCEL;
+
+ if (print_renderer)
+ g_print ("%s", renderer);
+
+out:
+ if (display)
+ XCloseDisplay (display);
+ g_free (renderer);
+
+ return ret;
+}
diff --git a/tools/mate-session-check-accelerated-gles-helper.c b/tools/mate-session-check-accelerated-gles-helper.c
new file mode 100644
index 0000000..ff6a62e
--- /dev/null
+++ b/tools/mate-session-check-accelerated-gles-helper.c
@@ -0,0 +1,201 @@
+/* -*- mode:c; c-basic-offset: 8; indent-tabs-mode: nil; -*- */
+/*
+ *
+ * Copyright (C) 2016 Endless Mobile, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author:
+ * Cosimo Cecchi <[email protected]>
+ */
+
+/* for strcasestr */
+#define _GNU_SOURCE
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef GDK_WINDOWING_X11
+#define GL_GLEXT_PROTOTYPES
+
+#include <gdk/gdkx.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <EGL/egl.h>
+#endif
+
+#include "mate-session-check-accelerated-common.h"
+
+#ifdef GDK_WINDOWING_X11
+static char *
+get_gles_renderer (void)
+{
+ /* Select GLESv2 config */
+ EGLint attribs[] = {
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 1,
+ EGL_GREEN_SIZE, 1,
+ EGL_BLUE_SIZE, 1,
+ EGL_NONE
+ };
+
+ EGLint ctx_attribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+
+ gboolean egl_inited = FALSE;
+ Display *display;
+ Window win = None;
+ EGLContext egl_ctx = NULL;
+ EGLDisplay egl_dpy = NULL;
+ EGLSurface egl_surf = NULL;
+ char *renderer = NULL;
+
+ gdk_error_trap_push ();
+
+ display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
+ egl_dpy = eglGetDisplay (display);
+ if (!egl_dpy) {
+ g_warning ("eglGetDisplay() failed");
+ goto out;
+ }
+
+ EGLint egl_major, egl_minor;
+ if (!eglInitialize (egl_dpy, &egl_major, &egl_minor)) {
+ g_warning ("eglInitialize() failed");
+ goto out;
+ }
+
+ egl_inited = TRUE;
+
+ EGLint num_configs;
+ EGLConfig config;
+ if (!eglChooseConfig (egl_dpy, attribs, &config, 1, &num_configs)) {
+ g_warning ("Failed to get EGL configuration");
+ goto out;
+ }
+
+ EGLint vid;
+ if (!eglGetConfigAttrib (egl_dpy, config, EGL_NATIVE_VISUAL_ID, &vid)) {
+ g_warning ("Failed to get EGL visual");
+ goto out;
+ }
+
+ /* The X window visual must match the EGL config */
+ XVisualInfo *vis_info, vis_template;
+ int num_visuals;
+ vis_template.visualid = vid;
+ vis_info = XGetVisualInfo (display, VisualIDMask, &vis_template, &num_visuals);
+ if (!vis_info) {
+ g_warning ("Failed to get X visual");
+ goto out;
+ }
+
+ XSetWindowAttributes attr;
+ attr.colormap = XCreateColormap (display, DefaultRootWindow (display),
+ vis_info->visual, AllocNone);
+ win = XCreateWindow (display, DefaultRootWindow (display),
+ 0, 0, /* x, y */
+ 1, 1, /* width, height */
+ 0, /* border_width */
+ vis_info->depth, InputOutput,
+ vis_info->visual, CWColormap, &attr);
+ XFree (vis_info);
+
+ eglBindAPI (EGL_OPENGL_ES_API);
+
+ egl_ctx = eglCreateContext (egl_dpy, config, EGL_NO_CONTEXT, ctx_attribs);
+ if (!egl_ctx) {
+ g_warning ("Failed to create EGL context");
+ goto out;
+ }
+
+ egl_surf = eglCreateWindowSurface (egl_dpy, config, win, NULL);
+ if (!egl_surf) {
+ g_warning ("Failed to create EGL surface");
+ goto out;
+ }
+
+ if (!eglMakeCurrent (egl_dpy, egl_surf, egl_surf, egl_ctx)) {
+ g_warning ("eglMakeCurrent() failed");
+ goto out;
+ }
+
+ renderer = g_strdup ((const char *) glGetString (GL_RENDERER));
+
+ out:
+ if (egl_ctx)
+ eglDestroyContext (egl_dpy, egl_ctx);
+ if (egl_surf)
+ eglDestroySurface (egl_dpy, egl_surf);
+ if (egl_inited)
+ eglTerminate (egl_dpy);
+ if (win != None)
+ XDestroyWindow (display, win);
+
+ gdk_error_trap_pop_ignored ();
+ return renderer;
+}
+#endif
+
+static gboolean print_renderer = FALSE;
+
+static const GOptionEntry entries[] = {
+ { "print-renderer", 'p', 0, G_OPTION_ARG_NONE, &print_renderer, "Print EGL renderer name", NULL },
+ { NULL },
+};
+
+int
+main (int argc,
+ char **argv)
+{
+ char *renderer = NULL;
+ GOptionContext *context;
+ int ret = HELPER_NO_ACCEL;
+ GError *error = NULL;
+
+ setlocale (LC_ALL, "");
+
+ context = g_option_context_new (NULL);
+ g_option_context_add_group (context, gtk_get_option_group (TRUE));
+ g_option_context_add_main_entries (context, entries, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, &error)) {
+ g_error ("Can't parse command line: %s\n", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+#ifdef GDK_WINDOWING_X11
+ renderer = get_gles_renderer ();
+#endif
+
+ if (renderer != NULL) {
+ if (print_renderer)
+ g_print ("%s", renderer);
+ if (strcasestr (renderer, "llvmpipe"))
+ ret = HELPER_SOFTWARE_RENDERING;
+ else
+ ret = HELPER_ACCEL;
+ }
+
+out:
+ return ret;
+}
diff --git a/tools/mate-session-check-accelerated.c b/tools/mate-session-check-accelerated.c
new file mode 100644
index 0000000..3c0d4bd
--- /dev/null
+++ b/tools/mate-session-check-accelerated.c
@@ -0,0 +1,312 @@
+/* -*- mode:c; c-basic-offset: 8; indent-tabs-mode: nil; -*- */
+/* Tool to set the property _GNOME_SESSION_ACCELERATED on the root window */
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author:
+ * Colin Walters <[email protected]>
+ */
+
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <gtk/gtk.h>
+#include <epoxy/gl.h>
+#include <gdk/gdkx.h>
+#include <X11/Xatom.h>
+#include <sys/wait.h>
+
+#include "mate-session-check-accelerated-common.h"
+
+/* Wait up to this long for a running check to finish */
+#define PROPERTY_CHANGE_TIMEOUT 5000
+
+/* Values used for the _GNOME_SESSION_ACCELERATED root window property */
+#define NO_ACCEL 0
+#define HAVE_ACCEL 1
+#define ACCEL_CHECK_RUNNING 2
+
+static Atom is_accelerated_atom;
+static Atom is_software_rendering_atom;
+static Atom renderer_atom;
+static gboolean property_changed;
+
+static gboolean
+on_property_notify_timeout (gpointer data)
+{
+ gtk_main_quit ();
+ return FALSE;
+}
+
+static GdkFilterReturn
+property_notify_filter (GdkXEvent *xevent,
+ GdkEvent *event,
+ gpointer data)
+{
+ XPropertyEvent *ev = xevent;
+
+ if (ev->type == PropertyNotify && ev->atom == is_accelerated_atom) {
+ property_changed = TRUE;
+ gtk_main_quit ();
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+static gboolean
+wait_for_property_notify (void)
+{
+ GdkDisplay *display;
+ GdkScreen *screen;
+ GdkWindow *root;
+ Window rootwin;
+
+ property_changed = FALSE;
+
+ display = gdk_display_get_default ();
+ screen = gdk_display_get_default_screen (display);
+ root = gdk_screen_get_root_window (screen);
+ rootwin = gdk_x11_window_get_xid (root);
+
+ XSelectInput (GDK_DISPLAY_XDISPLAY (display), rootwin, PropertyChangeMask);
+ gdk_window_add_filter (root, property_notify_filter, NULL);
+ g_timeout_add (PROPERTY_CHANGE_TIMEOUT, on_property_notify_timeout, NULL);
+
+ gtk_main ();
+
+ return property_changed;
+}
+
+static char *
+get_gtk_gles_renderer (void)
+{
+ GtkWidget *win;
+ GdkGLContext *context;
+ char *renderer = NULL;
+
+ win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_realize (win);
+ context = gdk_window_create_gl_context (gtk_widget_get_window (win), NULL);
+ if (!context)
+ return NULL;
+ gdk_gl_context_make_current (context);
+ renderer = g_strdup ((char *) glGetString (GL_RENDERER));
+ gdk_gl_context_clear_current ();
+ g_object_unref (context);
+
+ return renderer;
+}
+
+static gboolean
+is_discrete_gpu_check (void)
+{
+ const char *dri_prime;
+
+ dri_prime = g_getenv ("DRI_PRIME");
+ if (!dri_prime)
+ return FALSE;
+ if (*dri_prime != '1')
+ return FALSE;
+ return TRUE;
+}
+
+int
+main (int argc, char **argv)
+{
+ GdkDisplay *display = NULL;
+ int estatus;
+ char *gl_helper_argv[] = { LIBEXECDIR "/mate-session-check-accelerated-gl-helper", "--print-renderer", NULL };
+ char *gles_helper_argv[] = { LIBEXECDIR "/mate-session-check-accelerated-gles-helper", "--print-renderer", NULL };
+ char *renderer_string = NULL;
+ char *gl_renderer_string = NULL, *gles_renderer_string = NULL;
+ gboolean gl_software_rendering = FALSE, gles_software_rendering = FALSE;
+ Window rootwin;
+ glong is_accelerated, is_software_rendering;
+ GError *gl_error = NULL, *gles_error = NULL;
+
+ gtk_init (NULL, NULL);
+
+ /* mate-session-check-accelerated gets run before X is started in the wayland
+ * case, and it currently requires X. Until we have that working, just always
+ * assume wayland will work.
+ * Also make sure that we don't read cached information about the first GPU
+ * when requesting information about the second.
+ */
+ if (is_discrete_gpu_check () || g_strcmp0 (g_getenv ("XDG_SESSION_TYPE"), "x11") != 0) {
+ renderer_string = get_gtk_gles_renderer ();
+ if (renderer_string) {
+ g_print ("%s", renderer_string);
+ return 0;
+ }
+ return 1;
+ }
+
+ display = gdk_display_get_default ();
+ /* when running on X11 with a nested wayland GDK will default to wayland
+ * so looking for X11 atoms will not work (and crash).
+ */
+ if (!GDK_IS_X11_DISPLAY (display)) {
+ g_printerr ("mate-session-check-accelerated: no X11 display found\n");
+ return 1;
+ }
+
+ rootwin = gdk_x11_get_default_root_xwindow ();
+
+ is_accelerated_atom = gdk_x11_get_xatom_by_name_for_display (display, "_GNOME_SESSION_ACCELERATED");
+ is_software_rendering_atom = gdk_x11_get_xatom_by_name_for_display (display, "_GNOME_IS_SOFTWARE_RENDERING");
+ renderer_atom = gdk_x11_get_xatom_by_name_for_display (display, "_GNOME_SESSION_RENDERER");
+
+ {
+ Atom type;
+ gint format;
+ gulong nitems;
+ gulong bytes_after;
+ guchar *data;
+
+ read:
+ gdk_x11_display_error_trap_push (display);
+ XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), rootwin,
+ is_accelerated_atom,
+ 0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems,
+ &bytes_after, &data);
+ gdk_x11_display_error_trap_pop_ignored (display);
+
+ if (type == XA_CARDINAL) {
+ glong *is_accelerated_ptr = (glong*) data;
+
+ if (*is_accelerated_ptr == ACCEL_CHECK_RUNNING) {
+ /* Test in progress, wait */
+ if (wait_for_property_notify ())
+ goto read;
+ /* else fall through and do the check ourselves */
+
+ } else {
+ gdk_x11_display_error_trap_push (display);
+ XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), rootwin,
+ renderer_atom,
+ 0, G_MAXLONG, False, XA_STRING, &type, &format, &nitems,
+ &bytes_after, &data);
+ gdk_x11_display_error_trap_pop_ignored (display);
+
+ if (type == XA_STRING) {
+ g_print ("%s", data);
+ }
+
+ return (*is_accelerated_ptr == 0 ? 1 : 0);
+ }
+ }
+ }
+
+ /* We don't have the property or it's the wrong type.
+ * Try to compute it now.
+ */
+
+ /* First indicate that a test is in progress */
+ is_accelerated = ACCEL_CHECK_RUNNING;
+ is_software_rendering = FALSE;
+ estatus = 1;
+
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+ rootwin,
+ is_accelerated_atom,
+ XA_CARDINAL, 32, PropModeReplace, (guchar *) &is_accelerated, 1);
+
+ gdk_display_sync (display);
+
+ /* First, try the GL helper */
+ if (g_spawn_sync (NULL, (char **) gl_helper_argv, NULL, 0,
+ NULL, NULL, &gl_renderer_string, NULL, &estatus, &gl_error)) {
+ is_accelerated = (WEXITSTATUS(estatus) == HELPER_ACCEL);
+ gl_software_rendering = (WEXITSTATUS(estatus) == HELPER_SOFTWARE_RENDERING);
+ if (is_accelerated) {
+ renderer_string = gl_renderer_string;
+ goto finish;
+ }
+
+ g_printerr ("mate-session-check-accelerated: GL Helper exited with code %d\n", estatus);
+ }
+
+ /* Then, try the GLES helper */
+ if (g_spawn_sync (NULL, (char **) gles_helper_argv, NULL, 0,
+ NULL, NULL, &gles_renderer_string, NULL, &estatus, &gles_error)) {
+ is_accelerated = (WEXITSTATUS(estatus) == HELPER_ACCEL);
+ gles_software_rendering = (WEXITSTATUS(estatus) == HELPER_SOFTWARE_RENDERING);
+ if (is_accelerated) {
+ renderer_string = gles_renderer_string;
+ goto finish;
+ }
+
+ g_printerr ("mate-session-check-accelerated: GLES Helper exited with code %d\n", estatus);
+ }
+
+ /* If we got here, GL software rendering is our best bet */
+ if (gl_software_rendering || gles_software_rendering) {
+ is_software_rendering = TRUE;
+ is_accelerated = TRUE;
+
+ if (gl_software_rendering)
+ renderer_string = gl_renderer_string;
+ else if (gles_software_rendering)
+ renderer_string = gles_renderer_string;
+
+ goto finish;
+ }
+
+ /* Both helpers failed; print their error messages */
+ if (gl_error != NULL) {
+ g_printerr ("mate-session-check-accelerated: Failed to run GL helper: %s\n", gl_error->message);
+ g_clear_error (&gl_error);
+ }
+
+ if (gles_error != NULL) {
+ g_printerr ("mate-session-check-accelerated: Failed to run GLES helper: %s\n", gles_error->message);
+ g_clear_error (&gles_error);
+ }
+
+ finish:
+ if (is_accelerated) {
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+ rootwin,
+ is_accelerated_atom,
+ XA_CARDINAL, 32, PropModeReplace, (guchar *) &is_accelerated, 1);
+ }
+
+ if (is_software_rendering) {
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+ rootwin,
+ is_software_rendering_atom,
+ XA_CARDINAL, 32, PropModeReplace, (guchar *) &is_software_rendering, 1);
+ }
+
+ if (renderer_string != NULL) {
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+ rootwin,
+ renderer_atom,
+ XA_STRING, 8, PropModeReplace, (guchar *) renderer_string, strlen (renderer_string));
+
+ /* Print the renderer */
+ g_print ("%s", renderer_string);
+ }
+
+ gdk_display_sync (display);
+
+ g_free (gl_renderer_string);
+ g_free (gles_renderer_string);
+
+ return is_accelerated ? 0 : 1;
+}