/* GDK - The GIMP Drawing Kit * * gdkglcontext-x11.c: X11 specific OpenGL wrappers * * Copyright © 2014 Emmanuele Bassi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library. If not, see . */ #include "config.h" #include "gdkglcontext-x11.h" #include "gdkdisplay-x11.h" #include "gdkscreen-x11.h" #include "gdkx11display.h" #include "gdkx11glcontext.h" #include "gdkx11screen.h" #include "gdkx11window.h" #include "gdkx11visual.h" #include "gdkinternals.h" #include "gdkintl.h" #include #include G_DEFINE_TYPE (GdkX11GLContext, gdk_x11_gl_context, GDK_TYPE_GL_CONTEXT) typedef struct { GdkDisplay *display; GLXDrawable glx_drawable; Window dummy_xwin; GLXWindow dummy_glx; guint32 last_frame_counter; } DrawableInfo; static void drawable_info_free (gpointer data_) { DrawableInfo *data = data_; Display *dpy; gdk_x11_display_error_trap_push (data->display); dpy = gdk_x11_display_get_xdisplay (data->display); if (data->glx_drawable) glXDestroyWindow (dpy, data->glx_drawable); if (data->dummy_glx) glXDestroyWindow (dpy, data->dummy_glx); if (data->dummy_xwin) XDestroyWindow (dpy, data->dummy_xwin); gdk_x11_display_error_trap_pop_ignored (data->display); g_slice_free (DrawableInfo, data); } static DrawableInfo * get_glx_drawable_info (GdkWindow *window) { return g_object_get_data (G_OBJECT (window), "-gdk-x11-window-glx-info"); } static void set_glx_drawable_info (GdkWindow *window, DrawableInfo *info) { g_object_set_data_full (G_OBJECT (window), "-gdk-x11-window-glx-info", info, drawable_info_free); } static void gdk_x11_gl_context_update (GdkGLContext *context) { GdkWindow *window = gdk_gl_context_get_window (context); int width, height; gdk_gl_context_make_current (context); width = gdk_window_get_width (window); height = gdk_window_get_height (window); GDK_NOTE (OPENGL, g_print ("Updating GL viewport size to { %d, %d } for window %lu (context: %p)\n", width, height, (unsigned long) gdk_x11_window_get_xid (window), context)); glViewport (0, 0, width, height); } static void maybe_wait_for_vblank (GdkDisplay *display, GLXDrawable drawable) { GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); Display *dpy = gdk_x11_display_get_xdisplay (display); if (display_x11->has_glx_sync_control) { gint64 ust, msc, sbc; glXGetSyncValuesOML (dpy, drawable, &ust, &msc, &sbc); glXWaitForMscOML (dpy, drawable, 0, 2, (msc + 1) % 2, &ust, &msc, &sbc); } else if (display_x11->has_glx_video_sync) { guint32 current_count; glXGetVideoSyncSGI (¤t_count); glXWaitVideoSyncSGI (2, (current_count + 1) % 2, ¤t_count); } } void gdk_x11_window_invalidate_for_new_frame (GdkWindow *window, cairo_region_t *update_area) { cairo_rectangle_int_t window_rect; GdkDisplay *display = gdk_window_get_display (window); GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); Display *dpy = gdk_x11_display_get_xdisplay (display); GdkX11GLContext *context_x11; unsigned int buffer_age; gboolean invalidate_all; /* Minimal update is ok if we're not drawing with gl */ if (window->gl_paint_context == NULL) return; context_x11 = GDK_X11_GL_CONTEXT (window->gl_paint_context); buffer_age = 0; if (display_x11->has_glx_buffer_age) { gdk_gl_context_make_current (window->gl_paint_context); glXQueryDrawable(dpy, context_x11->drawable, GLX_BACK_BUFFER_AGE_EXT, &buffer_age); } invalidate_all = FALSE; if (buffer_age == 0 || buffer_age >= 4) invalidate_all = TRUE; else { if (buffer_age >= 2) { if (window->old_updated_area[0]) cairo_region_union (update_area, window->old_updated_area[0]); else invalidate_all = TRUE; } if (buffer_age >= 3) { if (window->old_updated_area[1]) cairo_region_union (update_area, window->old_updated_area[1]); else invalidate_all = TRUE; } } if (invalidate_all) { window_rect.x = 0; window_rect.y = 0; window_rect.width = gdk_window_get_width (window); window_rect.height = gdk_window_get_height (window); /* If nothing else is known, repaint everything so that the back buffer is fully up-to-date for the swapbuffer */ cairo_region_union_rectangle (update_area, &window_rect); } } static void gdk_x11_gl_context_flush_buffer (GdkGLContext *context, cairo_region_t *painted, cairo_region_t *damage) { GdkX11GLContext *context_x11 = GDK_X11_GL_CONTEXT (context); GdkWindow *window = gdk_gl_context_get_window (context); GdkDisplay *display = gdk_window_get_display (window); Display *dpy = gdk_x11_display_get_xdisplay (display); GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); DrawableInfo *info; GLXDrawable drawable; gdk_gl_context_make_current (context); info = get_glx_drawable_info (window); drawable = context_x11->drawable; GDK_NOTE (OPENGL, g_print ("Flushing GLX buffers for drawable %lu (window: %lu), frame sync: %s\n", (unsigned long) drawable, (unsigned long) gdk_x11_window_get_xid (window), context_x11->do_frame_sync ? "yes" : "no")); /* if we are going to wait for the vertical refresh manually * we need to flush pending redraws, and we also need to wait * for that to finish, otherwise we are going to tear. * * obviously, this condition should not be hit if we have * GLX_SGI_swap_control, and we ask the driver to do the right * thing. */ if (context_x11->do_frame_sync) { guint32 end_frame_counter = 0; gboolean has_counter = display_x11->has_glx_video_sync; gboolean can_wait = display_x11->has_glx_video_sync || display_x11->has_glx_sync_control; if (display_x11->has_glx_video_sync) glXGetVideoSyncSGI (&end_frame_counter); if (context_x11->do_frame_sync && !display_x11->has_glx_swap_interval) { glFinish (); if (has_counter && can_wait) { guint32 last_counter = info != NULL ? info->last_frame_counter : 0; if (last_counter == end_frame_counter) maybe_wait_for_vblank (display, drawable); } else if (can_wait) maybe_wait_for_vblank (display, drawable); } } glXSwapBuffers (dpy, drawable); if (context_x11->do_frame_sync && info != NULL && display_x11->has_glx_video_sync) glXGetVideoSyncSGI (&info->last_frame_counter); } typedef struct { Display *display; GLXDrawable drawable; gboolean y_inverted; } GdkGLXPixmap; static void glx_pixmap_destroy (void *data) { GdkGLXPixmap *glx_pixmap = data; glXDestroyPixmap (glx_pixmap->display, glx_pixmap->drawable); g_slice_free (GdkGLXPixmap, glx_pixmap); } static GdkGLXPixmap * glx_pixmap_get (cairo_surface_t *surface) { Display *display = cairo_xlib_surface_get_display (surface); Screen *screen = cairo_xlib_surface_get_screen (surface); Visual *visual = cairo_xlib_surface_get_visual (surface);; GdkGLXPixmap *glx_pixmap; GLXFBConfig *fbconfigs; int nfbconfigs; XVisualInfo *visinfo; int i, value; gboolean y_inverted; const int pixmap_attributes[] = { GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_RECTANGLE_EXT, GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT, None }; y_inverted = FALSE; fbconfigs = glXGetFBConfigs (display, XScreenNumberOfScreen (screen), &nfbconfigs); for (i = 0; i < nfbconfigs; i++) { visinfo = glXGetVisualFromFBConfig (display, fbconfigs[i]); if (!visinfo || visinfo->visualid != XVisualIDFromVisual (visual)) continue; glXGetFBConfigAttrib (display, fbconfigs[i], GLX_DRAWABLE_TYPE, &value); if (!(value & GLX_PIXMAP_BIT)) continue; glXGetFBConfigAttrib (display, fbconfigs[i], GLX_BIND_TO_TEXTURE_TARGETS_EXT, &value); if (!(value & GLX_TEXTURE_RECTANGLE_BIT_EXT)) continue; glXGetFBConfigAttrib (display, fbconfigs[i], GLX_BIND_TO_TEXTURE_RGBA_EXT, &value); if (value == FALSE) { glXGetFBConfigAttrib (display, fbconfigs[i], GLX_BIND_TO_TEXTURE_RGB_EXT, &value); if (value == FALSE) continue; } glXGetFBConfigAttrib (display, fbconfigs[i], GLX_Y_INVERTED_EXT, &value); if (value == TRUE) y_inverted = TRUE; break; } if (i == nfbconfigs) return NULL; glx_pixmap = g_slice_new0 (GdkGLXPixmap); glx_pixmap->y_inverted = y_inverted; glx_pixmap->display = display; glx_pixmap->drawable = glXCreatePixmap (display, fbconfigs[i], cairo_xlib_surface_get_drawable (surface), pixmap_attributes); return glx_pixmap; } static gboolean gdk_x11_gl_context_texture_from_surface (GdkGLContext *context, cairo_surface_t *surface, cairo_region_t *region) { GdkGLXPixmap *glx_pixmap; double device_x_offset, device_y_offset; cairo_rectangle_int_t rect; int n_rects, i; GdkWindow *window; int window_height; int window_scale; unsigned int texture_id; double sx, sy; if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_XLIB) return FALSE; glx_pixmap = glx_pixmap_get (surface); if (glx_pixmap == NULL) return FALSE; window = gdk_gl_context_get_window (gdk_gl_context_get_current ()); window_scale = gdk_window_get_scale_factor (window); window_height = gdk_window_get_height (window); sx = sy = 1; #ifdef HAVE_CAIRO_SURFACE_SET_DEVICE_SCALE cairo_surface_get_device_scale (window->current_paint.surface, &sx, &sy); #endif cairo_surface_get_device_offset (surface, &device_x_offset, &device_y_offset); /* Ensure all the X stuff are synced before we read it back via texture-from-pixmap */ glXWaitX(); glGenTextures (1, &texture_id); glBindTexture (GL_TEXTURE_RECTANGLE_ARB, texture_id); glEnable (GL_TEXTURE_RECTANGLE_ARB); glXBindTexImageEXT (glx_pixmap->display, glx_pixmap->drawable, GLX_FRONT_LEFT_EXT, NULL); n_rects = cairo_region_num_rectangles (region); for (i = 0; i < n_rects; i++) { int src_x, src_y, src_height, src_width; cairo_region_get_rectangle (region, i, &rect); glScissor (rect.x * window_scale, (window_height - rect.y - rect.height) * window_scale, rect.width * window_scale, rect.height * window_scale); src_x = rect.x * sx + device_x_offset; src_y = rect.y * sy + device_y_offset; src_width = rect.width * sx; src_height = rect.height * sy; #define FLIP_Y(_y) (window_height - (_y)) glBegin (GL_QUADS); glTexCoord2f (src_x, src_y + src_height); glVertex2f (rect.x * window_scale, FLIP_Y(rect.y + rect.height) * window_scale); glTexCoord2f (src_x + src_width, src_y + src_height); glVertex2f ((rect.x + rect.width) * window_scale, FLIP_Y(rect.y + rect.height) * window_scale); glTexCoord2f (src_x + src_width, src_y); glVertex2f ((rect.x + rect.width) * window_scale, FLIP_Y(rect.y) * window_scale); glTexCoord2f (src_x, src_y); glVertex2f (rect.x * window_scale, FLIP_Y(rect.y) * window_scale); glEnd(); } glXReleaseTexImageEXT (glx_pixmap->display, glx_pixmap->drawable, GLX_FRONT_LEFT_EXT); glDisable (GL_TEXTURE_RECTANGLE_ARB); glDeleteTextures (1, &texture_id); glx_pixmap_destroy(glx_pixmap); return TRUE; } static void gdk_x11_gl_context_class_init (GdkX11GLContextClass *klass) { GdkGLContextClass *context_class = GDK_GL_CONTEXT_CLASS (klass); context_class->update = gdk_x11_gl_context_update; context_class->flush_buffer = gdk_x11_gl_context_flush_buffer; context_class->texture_from_surface = gdk_x11_gl_context_texture_from_surface; } static void gdk_x11_gl_context_init (GdkX11GLContext *self) { } gboolean gdk_x11_display_init_gl (GdkDisplay *display) { GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); GdkScreen *screen; Display *dpy; int error_base, event_base; int screen_num; if (display_x11->have_glx) return TRUE; dpy = gdk_x11_display_get_xdisplay (display); if (!glXQueryExtension (dpy, &error_base, &event_base)) return FALSE; screen = gdk_display_get_default_screen (display); screen_num = GDK_X11_SCREEN (screen)->screen_num; display_x11->have_glx = TRUE; display_x11->glx_version = epoxy_glx_version (dpy, screen_num); display_x11->glx_error_base = error_base; display_x11->glx_event_base = event_base; display_x11->has_glx_create_context = epoxy_has_glx_extension (dpy, screen_num, "GLX_ARB_create_context_profile"); display_x11->has_glx_swap_interval = epoxy_has_glx_extension (dpy, screen_num, "GLX_SGI_swap_control"); display_x11->has_glx_texture_from_pixmap = epoxy_has_glx_extension (dpy, screen_num, "GLX_EXT_texture_from_pixmap"); display_x11->has_glx_video_sync = epoxy_has_glx_extension (dpy, screen_num, "GLX_SGI_video_sync"); display_x11->has_glx_buffer_age = epoxy_has_glx_extension (dpy, screen_num, "GLX_EXT_buffer_age"); display_x11->has_glx_sync_control = epoxy_has_glx_extension (dpy, screen_num, "GLX_OML_sync_control"); GDK_NOTE (OPENGL, g_print ("GLX version %d.%d found\n" " - Vendor: %s\n" " - Checked extensions:\n" "\t* GLX_ARB_create_context_profile: %s\n" "\t* GLX_SGI_swap_control: %s\n" "\t* GLX_EXT_texture_from_pixmap: %s\n" "\t* GLX_SGI_video_sync: %s\n" "\t* GLX_EXT_buffer_age: %s\n" "\t* GLX_OML_sync_control: %s\n", display_x11->glx_version / 10, display_x11->glx_version % 10, glXGetClientString (dpy, GLX_VENDOR), display_x11->has_glx_create_context ? "yes" : "no", display_x11->has_glx_swap_interval ? "yes" : "no", display_x11->has_glx_texture_from_pixmap ? "yes" : "no", display_x11->has_glx_video_sync ? "yes" : "no", display_x11->has_glx_buffer_age ? "yes" : "no", display_x11->has_glx_sync_control ? "yes" : "no")); return TRUE; } #define MAX_GLX_ATTRS 30 static gboolean find_fbconfig_for_visual (GdkDisplay *display, GdkVisual *visual, GLXFBConfig *fb_config_out, XVisualInfo **visinfo_out, GError **error) { static int attrs[MAX_GLX_ATTRS]; Display *dpy = gdk_x11_display_get_xdisplay (display); GLXFBConfig *configs; int n_configs, i; gboolean use_rgba; gboolean retval = FALSE; i = 0; attrs[i++] = GLX_DRAWABLE_TYPE; attrs[i++] = GLX_WINDOW_BIT; attrs[i++] = GLX_RENDER_TYPE; attrs[i++] = GLX_RGBA_BIT; attrs[i++] = GLX_DOUBLEBUFFER; attrs[i++] = GL_TRUE; attrs[i++] = GLX_RED_SIZE; attrs[i++] = gdk_visual_get_bits_per_rgb (visual); attrs[i++] = GLX_GREEN_SIZE; attrs[i++] = gdk_visual_get_bits_per_rgb (visual);; attrs[i++] = GLX_BLUE_SIZE; attrs[i++] = gdk_visual_get_bits_per_rgb (visual);; use_rgba = (visual == gdk_screen_get_rgba_visual (gdk_display_get_default_screen (display))); if (use_rgba) { attrs[i++] = GLX_ALPHA_SIZE; attrs[i++] = 1; } else { attrs[i++] = GLX_ALPHA_SIZE; attrs[i++] = GLX_DONT_CARE; } attrs[i++] = None; g_assert (i < MAX_GLX_ATTRS); configs = glXChooseFBConfig (dpy, DefaultScreen (dpy), attrs, &n_configs); if (configs == NULL || n_configs == 0) { g_set_error_literal (error, GDK_GL_ERROR, GDK_GL_ERROR_UNSUPPORTED_FORMAT, _("No available configurations for the given pixel format")); return FALSE; } /* if we don't care about an alpha channel, then the first * valid configuration is the one we give back */ if (!use_rgba) { if (fb_config_out != NULL) *fb_config_out = configs[0]; if (visinfo_out != NULL) *visinfo_out = glXGetVisualFromFBConfig (dpy, configs[0]); retval = TRUE; goto out; } for (i = 0; i < n_configs; i++) { XVisualInfo *visinfo; unsigned long mask; visinfo = glXGetVisualFromFBConfig (dpy, configs[i]); if (visinfo == NULL) continue; mask = visinfo->red_mask | visinfo->green_mask | visinfo->blue_mask; if (visinfo->depth == 32 && mask != 0xffffffff) { if (fb_config_out != NULL) *fb_config_out = configs[i]; if (visinfo_out != NULL) *visinfo_out = visinfo; retval = TRUE; goto out; } XFree (visinfo); } g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_UNSUPPORTED_FORMAT, _("No available configurations for the given RGBA pixel format")); out: XFree (configs); return retval; } static GLXContext create_gl3_context (GdkDisplay *display, GLXFBConfig config, GdkGLContext *share) { static const int attrib_list[] = { GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MAJOR_VERSION_ARB, 2, None, }; GdkX11GLContext *context_x11 = NULL; if (share != NULL) context_x11 = GDK_X11_GL_CONTEXT (share); return glXCreateContextAttribsARB (gdk_x11_display_get_xdisplay (display), config, context_x11 != NULL ? context_x11->glx_context : NULL, True, attrib_list); } static GLXContext create_gl_context (GdkDisplay *display, GLXFBConfig config, GdkGLContext *share) { GdkX11GLContext *context_x11 = NULL; if (share != NULL) context_x11 = GDK_X11_GL_CONTEXT (share); return glXCreateNewContext (gdk_x11_display_get_xdisplay (display), config, GLX_RGBA_TYPE, context_x11 != NULL ? context_x11->glx_context : NULL, True); } GdkGLContext * gdk_x11_window_create_gl_context (GdkWindow *window, gboolean attached, GdkGLProfile profile, GdkGLContext *share, GError **error) { GdkDisplay *display; GdkX11GLContext *context; GdkVisual *visual; GdkVisual *gdk_visual; GLXFBConfig config; GLXContext glx_context; GLXWindow drawable; gboolean is_direct; XVisualInfo *xvisinfo; Display *dpy; DrawableInfo *info; display = gdk_window_get_display (window); if (!gdk_x11_display_init_gl (display)) { g_set_error_literal (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE, _("No GL implementation is available")); return NULL; } if (profile == GDK_GL_PROFILE_3_2_CORE && !GDK_X11_DISPLAY (display)->has_glx_create_context) { g_set_error_literal (error, GDK_GL_ERROR, GDK_GL_ERROR_UNSUPPORTED_PROFILE, _("The GLX_ARB_create_context_profile extension " "needed to create 3.2 core profiles is not " "available")); return NULL; } visual = gdk_window_get_visual (window); if (!find_fbconfig_for_visual (display, visual, &config, &xvisinfo, error)) return NULL; dpy = gdk_x11_display_get_xdisplay (display); /* we check for the GLX_ARB_create_context_profile extension * while validating the PixelFormat. */ if (profile == GDK_GL_PROFILE_3_2_CORE) glx_context = create_gl3_context (display, config, share); else { /* GDK_GL_PROFILE_DEFAULT is currently * equivalent to the LEGACY profile */ glx_context = create_gl_context (display, config, share); } if (glx_context == NULL) { g_set_error_literal (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE, _("Unable to create a GL context")); return NULL; } is_direct = glXIsDirect (dpy, glx_context); info = get_glx_drawable_info (window->impl_window); if (info == NULL) { XSetWindowAttributes attrs; unsigned long mask; gdk_x11_display_error_trap_push (display); info = g_slice_new0 (DrawableInfo); info->display = display; info->last_frame_counter = 0; attrs.override_redirect = True; attrs.colormap = XCreateColormap (dpy, DefaultRootWindow (dpy), xvisinfo->visual, AllocNone); attrs.border_pixel = 0; mask = CWOverrideRedirect | CWColormap | CWBorderPixel; info->dummy_xwin = XCreateWindow (dpy, DefaultRootWindow (dpy), -100, -100, 1, 1, 0, xvisinfo->depth, CopyFromParent, xvisinfo->visual, mask, &attrs); XMapWindow(dpy, info->dummy_xwin); if (GDK_X11_DISPLAY (display)->glx_version >= 13) { info->glx_drawable = glXCreateWindow (dpy, config, gdk_x11_window_get_xid (window->impl_window), NULL); info->dummy_glx = glXCreateWindow (dpy, config, info->dummy_xwin, NULL); } if (gdk_x11_display_error_trap_pop (display)) { g_set_error_literal (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE, _("Unable to create a GL context")); drawable_info_free (info); glXDestroyContext (dpy, glx_context); return NULL; } set_glx_drawable_info (window->impl_window, info); } gdk_visual = gdk_x11_screen_lookup_visual (gdk_display_get_default_screen (display), xvisinfo->visualid); XFree (xvisinfo); if (attached) drawable = info->glx_drawable ? info->glx_drawable : gdk_x11_window_get_xid (window->impl_window); else drawable = info->dummy_glx ? info->dummy_glx : info->dummy_xwin; GDK_NOTE (OPENGL, g_print ("Created GLX context[%p], %s\n", glx_context, is_direct ? "direct" : "indirect")); context = g_object_new (GDK_X11_TYPE_GL_CONTEXT, "window", window, "visual", gdk_visual, NULL); context->profile = profile; context->glx_config = config; context->glx_context = glx_context; context->drawable = drawable; context->is_attached = attached; context->is_direct = is_direct; return GDK_GL_CONTEXT (context); } void gdk_x11_display_destroy_gl_context (GdkDisplay *display, GdkGLContext *context) { GdkX11GLContext *context_x11 = GDK_X11_GL_CONTEXT (context); Display *dpy = gdk_x11_display_get_xdisplay (display); if (context_x11->glx_context != NULL) { if (glXGetCurrentContext () == context_x11->glx_context) glXMakeContextCurrent (dpy, None, None, NULL); GDK_NOTE (OPENGL, g_print ("Destroying GLX context\n")); glXDestroyContext (dpy, context_x11->glx_context); context_x11->glx_context = NULL; } } void gdk_x11_display_make_gl_context_current (GdkDisplay *display, GdkGLContext *context) { GdkX11GLContext *context_x11; Display *dpy = gdk_x11_display_get_xdisplay (display); GdkWindow *window; GdkScreen *screen; gboolean do_frame_sync = FALSE; if (context == NULL) { glXMakeContextCurrent (dpy, None, None, NULL); return; } context_x11 = GDK_X11_GL_CONTEXT (context); window = gdk_gl_context_get_window (context); // If the WM is compositing there is no particular need to delay // the swap when drawing on the offscreen, rendering to the screen // happens later anyway, and its up to the compositor to sync that // to the vblank. screen = gdk_window_get_screen (window); do_frame_sync = ! gdk_screen_is_composited (screen); context_x11->do_frame_sync = do_frame_sync; GDK_NOTE (OPENGL, g_print ("Making GLX context current to drawable %lu\n", (unsigned long) context_x11->drawable)); glXMakeContextCurrent (dpy, context_x11->drawable, context_x11->drawable, context_x11->glx_context); if (context_x11->is_attached && GDK_X11_DISPLAY (display)->has_glx_swap_interval) { if (context_x11->do_frame_sync) glXSwapIntervalSGI (1); else glXSwapIntervalSGI (0); } } /** * gdk_x11_display_get_glx_version: * @display: a #GdkDisplay * @major: (out): return location for the GLX major version * @minor: (out): return location for the GLX minor version * * Retrieves the version of the GLX implementation. * * Returns: %TRUE if GLX is available * * Since: 3.14 */ gboolean gdk_x11_display_get_glx_version (GdkDisplay *display, int *major, int *minor) { g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE); if (!GDK_IS_X11_DISPLAY (display)) return FALSE; if (!gdk_x11_display_init_gl (display)) return FALSE; if (major != NULL) *major = GDK_X11_DISPLAY (display)->glx_version / 10; if (minor != NULL) *minor = GDK_X11_DISPLAY (display)->glx_version % 10; return TRUE; }