// Create a blank cursor (for locked mouse mode) // static Cursor createNULLCursor(void) { Pixmap cursormask; XGCValues xgc; GC gc; XColor col; Cursor cursor; _glfwGrabXErrorHandler(); cursormask = XCreatePixmap(_glfw.x11.display, _glfw.x11.root, 1, 1, 1); xgc.function = GXclear; gc = XCreateGC(_glfw.x11.display, cursormask, GCFunction, &xgc); XFillRectangle(_glfw.x11.display, cursormask, gc, 0, 0, 1, 1); col.pixel = 0; col.red = 0; col.flags = 4; cursor = XCreatePixmapCursor(_glfw.x11.display, cursormask, cursormask, &col, &col, 0, 0); XFreePixmap(_glfw.x11.display, cursormask); XFreeGC(_glfw.x11.display, gc); _glfwReleaseXErrorHandler(); if (cursor == None) { _glfwInputXError(GLFW_PLATFORM_ERROR, "X11: Failed to create null cursor"); } return cursor; }
// Check whether the running window manager is EWMH-compliant // static void detectEWMH(void) { Window* windowFromRoot = NULL; Window* windowFromChild = NULL; // First we need a couple of atoms, which should already be there Atom supportingWmCheck = XInternAtom(_glfw.x11.display, "_NET_SUPPORTING_WM_CHECK", True); Atom wmSupported = XInternAtom(_glfw.x11.display, "_NET_SUPPORTED", True); if (supportingWmCheck == None || wmSupported == None) return; // Then we look for the _NET_SUPPORTING_WM_CHECK property of the root window if (_glfwGetWindowProperty(_glfw.x11.root, supportingWmCheck, XA_WINDOW, (unsigned char**) &windowFromRoot) != 1) { if (windowFromRoot) XFree(windowFromRoot); return; } _glfwGrabXErrorHandler(); // It should be the ID of a child window (of the root) // Then we look for the same property on the child window if (_glfwGetWindowProperty(*windowFromRoot, supportingWmCheck, XA_WINDOW, (unsigned char**) &windowFromChild) != 1) { XFree(windowFromRoot); if (windowFromChild) XFree(windowFromChild); return; } _glfwReleaseXErrorHandler(); // It should be the ID of that same child window if (*windowFromRoot != *windowFromChild) { XFree(windowFromRoot); XFree(windowFromChild); return; } XFree(windowFromRoot); XFree(windowFromChild); // We are now fairly sure that an EWMH-compliant window manager is running Atom* supportedAtoms; unsigned long atomCount; // Now we need to check the _NET_SUPPORTED property of the root window // It should be a list of supported WM protocol and state atoms atomCount = _glfwGetWindowProperty(_glfw.x11.root, wmSupported, XA_ATOM, (unsigned char**) &supportedAtoms); // See which of the atoms we support that are supported by the WM _glfw.x11.NET_WM_STATE = getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE"); _glfw.x11.NET_WM_STATE_ABOVE = getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_ABOVE"); _glfw.x11.NET_WM_STATE_FULLSCREEN = getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_FULLSCREEN"); _glfw.x11.NET_WM_FULLSCREEN_MONITORS = getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_FULLSCREEN_MONITORS"); _glfw.x11.NET_WM_NAME = getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_NAME"); _glfw.x11.NET_WM_ICON_NAME = getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_ICON_NAME"); _glfw.x11.NET_WM_PID = getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_PID"); _glfw.x11.NET_WM_PING = getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_PING"); _glfw.x11.NET_ACTIVE_WINDOW = getSupportedAtom(supportedAtoms, atomCount, "_NET_ACTIVE_WINDOW"); _glfw.x11.NET_FRAME_EXTENTS = getSupportedAtom(supportedAtoms, atomCount, "_NET_FRAME_EXTENTS"); _glfw.x11.NET_REQUEST_FRAME_EXTENTS = getSupportedAtom(supportedAtoms, atomCount, "_NET_REQUEST_FRAME_EXTENTS"); _glfw.x11.NET_WM_BYPASS_COMPOSITOR = getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_BYPASS_COMPOSITOR"); XFree(supportedAtoms); }
// Create the X11 window (and its colormap) // static GLboolean createWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig) { unsigned long wamask; XSetWindowAttributes wa; XVisualInfo* visual = _GLFW_X11_CONTEXT_VISUAL; // Every window needs a colormap // Create one based on the visual used by the current context // TODO: Decouple this from context creation window->x11.colormap = XCreateColormap(_glfw.x11.display, _glfw.x11.root, visual->visual, AllocNone); // Create the actual window { wamask = CWBorderPixel | CWColormap | CWEventMask; wa.colormap = window->x11.colormap; wa.border_pixel = 0; wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask | ExposureMask | FocusChangeMask | VisibilityChangeMask | EnterWindowMask | LeaveWindowMask | PropertyChangeMask; if (wndconfig->monitor == NULL) { // HACK: This is a workaround for windows without a background pixel // not getting any decorations on certain older versions of // Compiz running on Intel hardware wa.background_pixel = BlackPixel(_glfw.x11.display, _glfw.x11.screen); wamask |= CWBackPixel; } _glfwGrabXErrorHandler(); window->x11.handle = XCreateWindow(_glfw.x11.display, _glfw.x11.root, 0, 0, wndconfig->width, wndconfig->height, 0, // Border width visual->depth, // Color depth InputOutput, visual->visual, wamask, &wa); _glfwReleaseXErrorHandler(); if (!window->x11.handle) { _glfwInputXError(GLFW_PLATFORM_ERROR, "X11: Failed to create window"); return GL_FALSE; } if (!wndconfig->decorated) { MotifWmHints hints; hints.flags = MWM_HINTS_DECORATIONS; hints.decorations = 0; XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.MOTIF_WM_HINTS, _glfw.x11.MOTIF_WM_HINTS, 32, PropModeReplace, (unsigned char*) &hints, sizeof(MotifWmHints) / sizeof(long)); } XSaveContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context, (XPointer) window); } if (window->monitor && !_glfw.x11.hasEWMH) { // This is the butcher's way of removing window decorations // Setting the override-redirect attribute on a window makes the window // manager ignore the window completely (ICCCM, section 4) // The good thing is that this makes undecorated fullscreen windows // easy to do; the bad thing is that we have to do everything manually // and some things (like iconify/restore) won't work at all, as those // are tasks usually performed by the window manager XSetWindowAttributes attributes; attributes.override_redirect = True; XChangeWindowAttributes(_glfw.x11.display, window->x11.handle, CWOverrideRedirect, &attributes); window->x11.overrideRedirect = GL_TRUE; } // Declare the WM protocols supported by GLFW { int count = 0; Atom protocols[2]; // The WM_DELETE_WINDOW ICCCM protocol // Basic window close notification protocol if (_glfw.x11.WM_DELETE_WINDOW) protocols[count++] = _glfw.x11.WM_DELETE_WINDOW; // The _NET_WM_PING EWMH protocol // Tells the WM to ping the GLFW window and flag the application as // unresponsive if the WM doesn't get a reply within a few seconds if (_glfw.x11.NET_WM_PING) protocols[count++] = _glfw.x11.NET_WM_PING; if (count > 0) { XSetWMProtocols(_glfw.x11.display, window->x11.handle, protocols, count); } } if (_glfw.x11.NET_WM_PID) { const pid_t pid = getpid(); XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_PID, XA_CARDINAL, 32, PropModeReplace, (unsigned char*) &pid, 1); } // Set ICCCM WM_HINTS property { XWMHints* hints = XAllocWMHints(); if (!hints) { _glfwInputError(GLFW_OUT_OF_MEMORY, "X11: Failed to allocate WM hints"); return GL_FALSE; } hints->flags = StateHint; hints->initial_state = NormalState; XSetWMHints(_glfw.x11.display, window->x11.handle, hints); XFree(hints); } // Set ICCCM WM_NORMAL_HINTS property (even if no parts are set) { XSizeHints* hints = XAllocSizeHints(); hints->flags = 0; if (wndconfig->monitor) { hints->flags |= PPosition; _glfwPlatformGetMonitorPos(wndconfig->monitor, &hints->x, &hints->y); } else { // HACK: Explicitly setting PPosition to any value causes some WMs, // notably Compiz and Metacity, to honor the position of // unmapped windows set by XMoveWindow hints->flags |= PPosition; hints->x = hints->y = 0; } if (!wndconfig->resizable) { hints->flags |= (PMinSize | PMaxSize); hints->min_width = hints->max_width = wndconfig->width; hints->min_height = hints->max_height = wndconfig->height; } XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints); XFree(hints); } // Set ICCCM WM_CLASS property // HACK: Until a mechanism for specifying the application name is added, the // initial window title is used as the window class name if (strlen(wndconfig->title)) { XClassHint* hint = XAllocClassHint(); hint->res_name = (char*) wndconfig->title; hint->res_class = (char*) wndconfig->title; XSetClassHint(_glfw.x11.display, window->x11.handle, hint); XFree(hint); } if (_glfw.x11.xi.available) { // Select for XInput2 events XIEventMask eventmask; unsigned char mask[] = { 0 }; eventmask.deviceid = 2; eventmask.mask_len = sizeof(mask); eventmask.mask = mask; XISetMask(mask, XI_Motion); XISelectEvents(_glfw.x11.display, window->x11.handle, &eventmask, 1); } if (_glfw.x11.XdndAware) { // Announce support for Xdnd (drag and drop) const Atom version = 5; XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*) &version, 1); } _glfwPlatformSetWindowTitle(window, wndconfig->title); XRRSelectInput(_glfw.x11.display, window->x11.handle, RRScreenChangeNotifyMask); _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos); _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height); return GL_TRUE; }
// Prepare for creation of the OpenGL context // int _glfwCreateContext(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWfbconfig* fbconfig) { int attribs[40]; GLXFBConfig native; GLXContext share = NULL; if (wndconfig->share) share = wndconfig->share->glx.context; if (!chooseFBConfig(fbconfig, &native)) { _glfwInputError(GLFW_PLATFORM_ERROR, "GLX: Failed to find a suitable GLXFBConfig"); return GL_FALSE; } // Retrieve the corresponding visual if (_glfw.glx.SGIX_fbconfig) { window->glx.visual = _glfw.glx.GetVisualFromFBConfigSGIX(_glfw.x11.display, native); } else window->glx.visual = glXGetVisualFromFBConfig(_glfw.x11.display, native); if (window->glx.visual == NULL) { _glfwInputError(GLFW_PLATFORM_ERROR, "GLX: Failed to retrieve visual for GLXFBConfig"); return GL_FALSE; } if (wndconfig->clientAPI == GLFW_OPENGL_ES_API) { if (!_glfw.glx.ARB_create_context || !_glfw.glx.ARB_create_context_profile || !_glfw.glx.EXT_create_context_es2_profile) { _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: OpenGL ES requested but " "GLX_EXT_create_context_es2_profile is unavailable"); return GL_FALSE; } } if (wndconfig->glForward) { if (!_glfw.glx.ARB_create_context) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "GLX: Forward compatibility requested but " "GLX_ARB_create_context_profile is unavailable"); return GL_FALSE; } } if (wndconfig->glProfile) { if (!_glfw.glx.ARB_create_context || !_glfw.glx.ARB_create_context_profile) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "GLX: An OpenGL profile requested but " "GLX_ARB_create_context_profile is unavailable"); return GL_FALSE; } } _glfwGrabXErrorHandler(); if (_glfw.glx.ARB_create_context) { int index = 0, mask = 0, flags = 0, strategy = 0; if (wndconfig->clientAPI == GLFW_OPENGL_API) { if (wndconfig->glForward) flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; if (wndconfig->glDebug) flags |= GLX_CONTEXT_DEBUG_BIT_ARB; if (wndconfig->glProfile) { if (wndconfig->glProfile == GLFW_OPENGL_CORE_PROFILE) mask |= GLX_CONTEXT_CORE_PROFILE_BIT_ARB; else if (wndconfig->glProfile == GLFW_OPENGL_COMPAT_PROFILE) mask |= GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; } } else mask |= GLX_CONTEXT_ES2_PROFILE_BIT_EXT; if (wndconfig->glRobustness != GLFW_NO_ROBUSTNESS) { if (_glfw.glx.ARB_create_context_robustness) { if (wndconfig->glRobustness == GLFW_NO_RESET_NOTIFICATION) strategy = GLX_NO_RESET_NOTIFICATION_ARB; else if (wndconfig->glRobustness == GLFW_LOSE_CONTEXT_ON_RESET) strategy = GLX_LOSE_CONTEXT_ON_RESET_ARB; flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB; } } if (wndconfig->glMajor != 1 || wndconfig->glMinor != 0) { // NOTE: Only request an explicitly versioned context when // necessary, as explicitly requesting version 1.0 does not // always return the highest available version setGLXattrib(GLX_CONTEXT_MAJOR_VERSION_ARB, wndconfig->glMajor); setGLXattrib(GLX_CONTEXT_MINOR_VERSION_ARB, wndconfig->glMinor); } if (mask) setGLXattrib(GLX_CONTEXT_PROFILE_MASK_ARB, mask); if (flags) setGLXattrib(GLX_CONTEXT_FLAGS_ARB, flags); if (strategy) setGLXattrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, strategy); setGLXattrib(None, None); window->glx.context = _glfw.glx.CreateContextAttribsARB(_glfw.x11.display, native, share, True, attribs); if (window->glx.context == NULL) { // HACK: This is a fallback for the broken Mesa implementation of // GLX_ARB_create_context_profile, which fails default 1.0 // context creation with a GLXBadProfileARB error in violation // of the extension spec if (_glfw.x11.errorCode == _glfw.glx.errorBase + GLXBadProfileARB && wndconfig->clientAPI == GLFW_OPENGL_API && wndconfig->glProfile == GLFW_OPENGL_ANY_PROFILE && wndconfig->glForward == GL_FALSE) { window->glx.context = createLegacyContext(window, native, share); } } } else window->glx.context = createLegacyContext(window, native, share); _glfwReleaseXErrorHandler(); if (window->glx.context == NULL) { _glfwInputXError(GLFW_PLATFORM_ERROR, "GLX: Failed to create context"); return GL_FALSE; } return GL_TRUE; }
// Create the OpenGL or OpenGL ES context // int _glfwCreateContext(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { int attribs[40]; GLXFBConfig native = NULL; GLXContext share = NULL; if (ctxconfig->share) share = ctxconfig->share->glx.context; if (!chooseFBConfig(fbconfig, &native)) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "GLX: Failed to find a suitable GLXFBConfig"); return GL_FALSE; } window->glx.visual = _glfw_glXGetVisualFromFBConfig(_glfw.x11.display, native); if (!window->glx.visual) { _glfwInputError(GLFW_PLATFORM_ERROR, "GLX: Failed to retrieve visual for GLXFBConfig"); return GL_FALSE; } if (ctxconfig->api == GLFW_OPENGL_ES_API) { if (!_glfw.glx.ARB_create_context || !_glfw.glx.ARB_create_context_profile || !_glfw.glx.EXT_create_context_es2_profile) { _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: OpenGL ES requested but GLX_EXT_create_context_es2_profile is unavailable"); return GL_FALSE; } } if (ctxconfig->forward) { if (!_glfw.glx.ARB_create_context) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "GLX: Forward compatibility requested but GLX_ARB_create_context_profile is unavailable"); return GL_FALSE; } } if (ctxconfig->profile) { if (!_glfw.glx.ARB_create_context || !_glfw.glx.ARB_create_context_profile) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "GLX: An OpenGL profile requested but GLX_ARB_create_context_profile is unavailable"); return GL_FALSE; } } _glfwGrabXErrorHandler(); if (_glfw.glx.ARB_create_context) { int index = 0, mask = 0, flags = 0; if (ctxconfig->api == GLFW_OPENGL_API) { if (ctxconfig->forward) flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) mask |= GLX_CONTEXT_CORE_PROFILE_BIT_ARB; else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) mask |= GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; } else mask |= GLX_CONTEXT_ES2_PROFILE_BIT_EXT; if (ctxconfig->debug) flags |= GLX_CONTEXT_DEBUG_BIT_ARB; if (ctxconfig->robustness) { if (_glfw.glx.ARB_create_context_robustness) { if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) { setGLXattrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, GLX_NO_RESET_NOTIFICATION_ARB); } else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) { setGLXattrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, GLX_LOSE_CONTEXT_ON_RESET_ARB); } flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB; } } if (ctxconfig->release) { if (_glfw.glx.ARB_context_flush_control) { if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) { setGLXattrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); } else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) { setGLXattrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); } } } // NOTE: Only request an explicitly versioned context when necessary, as // explicitly requesting version 1.0 does not always return the // highest version supported by the driver if (ctxconfig->major != 1 || ctxconfig->minor != 0) { setGLXattrib(GLX_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); setGLXattrib(GLX_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); } if (mask) setGLXattrib(GLX_CONTEXT_PROFILE_MASK_ARB, mask); if (flags) setGLXattrib(GLX_CONTEXT_FLAGS_ARB, flags); setGLXattrib(None, None); window->glx.context = _glfw.glx.CreateContextAttribsARB(_glfw.x11.display, native, share, True, attribs); // HACK: This is a fallback for broken versions of the Mesa // implementation of GLX_ARB_create_context_profile that fail // default 1.0 context creation with a GLXBadProfileARB error in // violation of the extension spec if (!window->glx.context) { if (_glfw.x11.errorCode == _glfw.glx.errorBase + GLXBadProfileARB && ctxconfig->api == GLFW_OPENGL_API && ctxconfig->profile == GLFW_OPENGL_ANY_PROFILE && ctxconfig->forward == GL_FALSE) { window->glx.context = createLegacyContext(window, native, share); } } } else window->glx.context = createLegacyContext(window, native, share); _glfwReleaseXErrorHandler(); if (!window->glx.context) { _glfwInputXError(GLFW_VERSION_UNAVAILABLE, "GLX: Failed to create context"); return GL_FALSE; } return GL_TRUE; }