Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Mon, 4 Jun 2018 16:49:43 +0000 (18:49 +0200)
committerCampbell Barton <ideasman42@gmail.com>
Mon, 4 Jun 2018 16:50:14 +0000 (18:50 +0200)
14 files changed:
1  2 
intern/ghost/CMakeLists.txt
intern/ghost/GHOST_C-api.h
intern/ghost/GHOST_ISystem.h
intern/ghost/intern/GHOST_C-api.cpp
intern/ghost/intern/GHOST_ContextWGL.h
intern/ghost/intern/GHOST_SystemCocoa.h
intern/ghost/intern/GHOST_SystemWin32.cpp
intern/ghost/intern/GHOST_SystemWin32.h
intern/ghost/intern/GHOST_SystemX11.cpp
intern/ghost/intern/GHOST_SystemX11.h
intern/ghost/intern/GHOST_WindowWin32.cpp
intern/ghost/intern/GHOST_WindowX11.cpp
intern/ghost/test/CMakeLists.txt
tests/python/view_layer/CMakeLists.txt

@@@ -54,7 -54,6 +54,7 @@@ set(SR
        intern/GHOST_WindowManager.cpp
  
        GHOST_C-api.h
 +      GHOST_IContext.h
        GHOST_IEvent.h
        GHOST_IEventConsumer.h
        GHOST_ISystem.h
@@@ -280,7 -279,7 +280,7 @@@ elseif(WIN32
        if(NOT WITH_GL_EGL)
                list(APPEND SRC
                        intern/GHOST_ContextWGL.cpp
-                               
                        intern/GHOST_ContextWGL.h
                )
        endif()
@@@ -307,7 -306,7 +307,7 @@@ endif(
  if(WITH_GL_EGL AND NOT (WITH_HEADLESS OR WITH_GHOST_SDL))
        list(APPEND SRC
                intern/GHOST_ContextEGL.cpp
-                       
                intern/GHOST_ContextEGL.h
        )
  endif()
@@@ -36,7 -36,7 +36,7 @@@
  #include "GHOST_Types.h"
  
  #ifdef __cplusplus
- extern "C" { 
+ extern "C" {
  #endif
  
  /**
@@@ -52,7 -52,6 +52,7 @@@ GHOST_DECLARE_HANDLE(GHOST_WindowHandle
  GHOST_DECLARE_HANDLE(GHOST_EventHandle);
  GHOST_DECLARE_HANDLE(GHOST_RectangleHandle);
  GHOST_DECLARE_HANDLE(GHOST_EventConsumerHandle);
 +GHOST_DECLARE_HANDLE(GHOST_ContextHandle);
  
  
  /**
@@@ -103,7 -102,7 +103,7 @@@ extern GHOST_TUns64 GHOST_GetMilliSecon
  
  /**
   * Installs a timer.
-  * Note that, on most operating systems, messages need to be processed in order 
+  * Note that, on most operating systems, messages need to be processed in order
   * for the timer callbacks to be invoked.
   * \param systemhandle The handle to the system
   * \param delay The time to wait for the first call to the timerProc (in milliseconds)
@@@ -165,7 -164,7 +165,7 @@@ extern void GHOST_GetAllDisplayDimensio
  
  /**
   * Create a new window.
-  * The new window is added to the list of windows managed. 
+  * The new window is added to the list of windows managed.
   * Never explicitly delete the window, use disposeWindow() instead.
   * \param systemhandle The handle to the system
   * \param title The name of the window (displayed in the title bar of the window if the OS supports it).
@@@ -189,23 -188,6 +189,23 @@@ extern GHOST_WindowHandle GHOST_CreateW
          GHOST_TDrawingContextType type,
          GHOST_GLSettings glSettings);
  
 +/**
 + * Create a new offscreen context.
 + * Never explicitly delete the context, use disposeContext() instead.
 + * \param systemhandle The handle to the system
 + * \return A handle to the new context ( == NULL if creation failed).
 + */
 +extern GHOST_ContextHandle GHOST_CreateOpenGLContext(GHOST_SystemHandle systemhandle);
 +
 +/**
 + * Dispose of a context.
 + * \param systemhandle The handle to the system
 + * \param contexthandle Handle to the context to be disposed.
 + * \return Indication of success.
 + */
 +extern GHOST_TSuccess GHOST_DisposeOpenGLContext(GHOST_SystemHandle systemhandle,
 +                                                    GHOST_ContextHandle contexthandle);
 +
  /**
   * Returns the window user data.
   * \param windowhandle The handle to the window
@@@ -218,7 -200,7 +218,7 @@@ extern GHOST_TUserDataPtr GHOST_GetWind
   * \param windowhandle The handle to the window
   * \param userdata The window user data.
   */
- extern void GHOST_SetWindowUserData(GHOST_WindowHandle windowhandle, 
+ extern void GHOST_SetWindowUserData(GHOST_WindowHandle windowhandle,
                                      GHOST_TUserDataPtr userdata);
  
  /**
@@@ -488,7 -470,7 +488,7 @@@ extern GHOST_TEventType GHOST_GetEventT
  extern GHOST_TUns64 GHOST_GetEventTime(GHOST_EventHandle eventhandle);
  
  /**
-  * Returns the window this event was generated on, 
+  * Returns the window this event was generated on,
   * or NULL if it is a 'system' event.
   * \param eventhandle The handle to the event
   * \return The generating window.
@@@ -566,7 -548,7 +566,7 @@@ extern void GHOST_SetTitle(GHOST_Window
  /**
   * Returns the title displayed in the title bar. The title
   * should be free'd with free().
-  * 
+  *
   * \param windowhandle The handle to the window
   * \return The title, free with free().
   */
@@@ -728,20 -710,6 +728,20 @@@ extern GHOST_TSuccess GHOST_ActivateWin
   */
  extern GHOST_TSuccess GHOST_InvalidateWindow(GHOST_WindowHandle windowhandle);
  
 +/**
 + * Activates the drawing context of this context.
 + * \param contexthandle The handle to the context
 + * \return A success indicator.
 + */
 +extern GHOST_TSuccess GHOST_ActivateOpenGLContext(GHOST_ContextHandle contexthandle);
 +
 +/**
 + * Release the drawing context bound to this thread.
 + * \param contexthandle The handle to the context
 + * \return A success indicator.
 + */
 +extern GHOST_TSuccess GHOST_ReleaseOpenGLContext(GHOST_ContextHandle contexthandle);
 +
  /**
   * Returns the status of the tablet
   * \param windowhandle The handle to the window
@@@ -36,7 -36,6 +36,7 @@@
  #define __GHOST_ISYSTEM_H__
  
  #include "GHOST_Types.h"
 +#include "GHOST_IContext.h"
  #include "GHOST_ITimerTask.h"
  #include "GHOST_IWindow.h"
  
@@@ -262,20 -261,6 +262,20 @@@ public
         */
        virtual GHOST_TSuccess disposeWindow(GHOST_IWindow *window) = 0;
  
 +      /**
 +       * Create a new offscreen context.
 +       * Never explicitly delete the context, use disposeContext() instead.
 +       * \return  The new context (or 0 if creation failed).
 +       */
 +      virtual GHOST_IContext *createOffscreenContext() = 0;
 +
 +      /**
 +       * Dispose of a context.
 +       * \param   context Pointer to the context to be disposed.
 +       * \return  Indication of success.
 +       */
 +      virtual GHOST_TSuccess disposeContext(GHOST_IContext *context) = 0;
 +
        /**
         * Returns whether a window is valid.
         * \param   window Pointer to the window to be checked.
         * \return The current status.
         */
        virtual bool getFullScreen(void) = 0;
-       
        /**
         * Native pixel size support (MacBook 'retina').
         */
@@@ -119,7 -119,7 +119,7 @@@ void GHOST_GetMainDisplayDimensions(GHO
                                      GHOST_TUns32 *height)
  {
        GHOST_ISystem *system = (GHOST_ISystem *) systemhandle;
-       
        system->getMainDisplayDimensions(*width, *height);
  }
  
@@@ -132,22 -132,6 +132,22 @@@ void GHOST_GetAllDisplayDimensions(GHOS
        system->getAllDisplayDimensions(*width, *height);
  }
  
 +GHOST_ContextHandle GHOST_CreateOpenGLContext(GHOST_SystemHandle systemhandle)
 +{
 +      GHOST_ISystem *system = (GHOST_ISystem *) systemhandle;
 +
 +      return (GHOST_ContextHandle) system->createOffscreenContext();
 +}
 +
 +GHOST_TSuccess GHOST_DisposeOpenGLContext(GHOST_SystemHandle systemhandle,
 +                                             GHOST_ContextHandle contexthandle)
 +{
 +      GHOST_ISystem *system = (GHOST_ISystem *) systemhandle;
 +      GHOST_IContext *context = (GHOST_IContext *) contexthandle;
 +
 +      return system->disposeContext(context);
 +}
 +
  GHOST_WindowHandle GHOST_CreateWindow(GHOST_SystemHandle systemhandle,
                                        const char *title,
                                        GHOST_TInt32 left,
@@@ -182,7 -166,7 +182,7 @@@ GHOST_TSuccess GHOST_DisposeWindow(GHOS
  {
        GHOST_ISystem *system = (GHOST_ISystem *) systemhandle;
        GHOST_IWindow *window = (GHOST_IWindow *) windowhandle;
-       
        return system->disposeWindow(window);
  }
  
@@@ -193,7 -177,7 +193,7 @@@ int GHOST_ValidWindow(GHOST_SystemHandl
  {
        GHOST_ISystem *system = (GHOST_ISystem *) systemhandle;
        GHOST_IWindow *window = (GHOST_IWindow *) windowhandle;
-       
        return (int) system->validWindow(window);
  }
  
@@@ -211,7 -195,7 +211,7 @@@ GHOST_WindowHandle GHOST_BeginFullScree
                bstereoVisual = true;
        else
                bstereoVisual = false;
-       
        system->beginFullScreen(*setting, &window, bstereoVisual);
  
        return (GHOST_WindowHandle)window;
@@@ -240,7 -224,7 +240,7 @@@ int GHOST_GetFullScreen(GHOST_SystemHan
  int GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, int waitForEvent)
  {
        GHOST_ISystem *system = (GHOST_ISystem *) systemhandle;
-       
        return (int) system->processEvents(waitForEvent ? true : false);
  }
  
  void GHOST_DispatchEvents(GHOST_SystemHandle systemhandle)
  {
        GHOST_ISystem *system = (GHOST_ISystem *) systemhandle;
-       
        system->dispatchEvents();
  }
  
  GHOST_TSuccess GHOST_AddEventConsumer(GHOST_SystemHandle systemhandle, GHOST_EventConsumerHandle consumerhandle)
  {
        GHOST_ISystem *system = (GHOST_ISystem *) systemhandle;
-       
        return system->addEventConsumer((GHOST_CallbackEventConsumer *)consumerhandle);
  }
  
@@@ -353,7 -337,7 +353,7 @@@ GHOST_TSuccess GHOST_GetCursorPosition(
                                         GHOST_TInt32 *y)
  {
        GHOST_ISystem *system = (GHOST_ISystem *) systemhandle;
-       
        return system->getCursorPosition(*x, *y);
  }
  
@@@ -364,7 -348,7 +364,7 @@@ GHOST_TSuccess GHOST_SetCursorPosition(
                                         GHOST_TInt32 y)
  {
        GHOST_ISystem *system = (GHOST_ISystem *) systemhandle;
-       
        return system->setCursorPosition(x, y);
  }
  
@@@ -398,7 -382,7 +398,7 @@@ GHOST_TSuccess GHOST_GetModifierKeyStat
        GHOST_ISystem *system = (GHOST_ISystem *) systemhandle;
        GHOST_TSuccess result;
        bool isdown = false;
-       
        result = system->getModifierKeyState(mask, isdown);
        *isDown = (int) isdown;
  
@@@ -414,7 -398,7 +414,7 @@@ GHOST_TSuccess GHOST_GetButtonState(GHO
        GHOST_ISystem *system = (GHOST_ISystem *) systemhandle;
        GHOST_TSuccess result;
        bool isdown = false;
-       
        result = system->getButtonState(mask, isdown);
        *isDown = (int) isdown;
  
@@@ -441,7 -425,7 +441,7 @@@ void GHOST_setAcceptDragOperation(GHOST
  GHOST_TEventType GHOST_GetEventType(GHOST_EventHandle eventhandle)
  {
        GHOST_IEvent *event = (GHOST_IEvent *) eventhandle;
-       
        return event->getType();
  }
  
@@@ -466,7 -450,7 +466,7 @@@ GHOST_WindowHandle GHOST_GetEventWindow
  GHOST_TEventDataPtr GHOST_GetEventData(GHOST_EventHandle eventhandle)
  {
        GHOST_IEvent *event = (GHOST_IEvent *) eventhandle;
-       
        return event->getData();
  }
  
  GHOST_TimerProcPtr GHOST_GetTimerProc(GHOST_TimerTaskHandle timertaskhandle)
  {
        GHOST_ITimerTask *timertask = (GHOST_ITimerTask *) timertaskhandle;
-       
        return timertask->getTimerProc();
  }
  
@@@ -485,7 -469,7 +485,7 @@@ void GHOST_SetTimerProc(GHOST_TimerTask
                          GHOST_TimerProcPtr timerproc)
  {
        GHOST_ITimerTask *timertask = (GHOST_ITimerTask *) timertaskhandle;
-       
        timertask->setTimerProc(timerproc);
  }
  
@@@ -504,13 -488,13 +504,13 @@@ void GHOST_SetTimerTaskUserData(GHOST_T
                                  GHOST_TUserDataPtr userdata)
  {
        GHOST_ITimerTask *timertask = (GHOST_ITimerTask *) timertaskhandle;
-       
        timertask->setUserData(userdata);
  }
  
  
  
- int GHOST_GetValid(GHOST_WindowHandle windowhandle) 
+ int GHOST_GetValid(GHOST_WindowHandle windowhandle)
  {
        GHOST_IWindow *window = (GHOST_IWindow *) windowhandle;
  
@@@ -542,7 -526,7 +542,7 @@@ void GHOST_SetTitle(GHOST_WindowHandle 
                      const char *title)
  {
        GHOST_IWindow *window = (GHOST_IWindow *) windowhandle;
-       
        window->setTitle(title);
  }
  
@@@ -567,7 -551,7 +567,7 @@@ char *GHOST_GetTitle(GHOST_WindowHandl
  
  
  
- GHOST_RectangleHandle GHOST_GetWindowBounds(GHOST_WindowHandle windowhandle) 
+ GHOST_RectangleHandle GHOST_GetWindowBounds(GHOST_WindowHandle windowhandle)
  {
        GHOST_IWindow *window = (GHOST_IWindow *) windowhandle;
        GHOST_Rect *rectangle = NULL;
  
  
  
- GHOST_RectangleHandle GHOST_GetClientBounds(GHOST_WindowHandle windowhandle) 
+ GHOST_RectangleHandle GHOST_GetClientBounds(GHOST_WindowHandle windowhandle)
  {
        GHOST_IWindow *window = (GHOST_IWindow *) windowhandle;
        GHOST_Rect *rectangle = NULL;
@@@ -678,16 -662,16 +678,16 @@@ GHOST_TSuccess GHOST_SetWindowState(GHO
  GHOST_TSuccess GHOST_SetWindowModifiedState(GHOST_WindowHandle windowhandle, GHOST_TUns8 isUnsavedChanges)
  {
        GHOST_IWindow *window = (GHOST_IWindow *) windowhandle;
-       
        return window->setModifiedState(isUnsavedChanges);
- }     
+ }
  
  
  GHOST_TSuccess GHOST_SetWindowOrder(GHOST_WindowHandle windowhandle,
                                      GHOST_TWindowOrder order)
  {
        GHOST_IWindow *window = (GHOST_IWindow *) windowhandle;
-       
        return window->setOrder(order);
  }
  
@@@ -725,23 -709,11 +725,23 @@@ GHOST_TUns16 GHOST_GetNumOfAASamples(GH
  GHOST_TSuccess GHOST_ActivateWindowDrawingContext(GHOST_WindowHandle windowhandle)
  {
        GHOST_IWindow *window = (GHOST_IWindow *) windowhandle;
-       
        return window->activateDrawingContext();
  }
  
 +GHOST_TSuccess GHOST_ActivateOpenGLContext(GHOST_ContextHandle contexthandle)
 +{
 +      GHOST_IContext *context = (GHOST_IContext *) contexthandle;
 +
 +      return context->activateDrawingContext();
 +}
 +
 +GHOST_TSuccess GHOST_ReleaseOpenGLContext(GHOST_ContextHandle contexthandle)
 +{
 +      GHOST_IContext *context = (GHOST_IContext *) contexthandle;
  
 +      return context->releaseDrawingContext();
 +}
  
  GHOST_TSuccess GHOST_InvalidateWindow(GHOST_WindowHandle windowhandle)
  {
  
  #include "GHOST_Context.h"
  
 -#ifdef WITH_GLEW_MX
 -#define wglewGetContext() wglewContext
 -#endif
 -
  #include <GL/wglew.h>
  
 -#ifdef WITH_GLEW_MX
 -extern "C" WGLEWContext *wglewContext;
 -#endif
 -
 -#ifndef GHOST_OPENGL_WGL_CONTEXT_FLAGS
 -#  ifdef WITH_GPU_DEBUG
 -#    define GHOST_OPENGL_WGL_CONTEXT_FLAGS WGL_CONTEXT_DEBUG_BIT_ARB
 -#  else
 -#    define GHOST_OPENGL_WGL_CONTEXT_FLAGS 0
 -#  endif
 -#endif
 -
  #ifndef GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY
  #define GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY 0
  #endif
@@@ -78,12 -94,6 +78,12 @@@ public
         */
        GHOST_TSuccess activateDrawingContext();
  
 +      /**
 +       * Release the drawing context of the calling thread.
 +       * \return  A boolean success indicator.
 +       */
 +      GHOST_TSuccess releaseDrawingContext();
 +
        /**
         * Call immediately after new to initialize.  If this fails then immediately delete the object.
         * \return Indication as to whether initialization has succeeded.
         */
        GHOST_TSuccess getSwapInterval(int &intervalOut);
  
 -protected:
 -      inline void activateWGLEW() const {
 -#ifdef WITH_GLEW_MX
 -              wglewContext = m_wglewContext;
 -#endif
 -      }
 -
  private:
        int choose_pixel_format(
                bool stereoVisual,
  
        void initContextWGLEW(PIXELFORMATDESCRIPTOR &preferredPFD);
  
 +      /* offscreen buffer with size of 1x1 pixel,
 +       * kept here to release the device constext when closing the program. */
 +      HPBUFFERARB m_dummyPbuffer;
 +
        HWND m_hWnd;
        HDC  m_hDC;
  
        const int m_contextResetNotificationStrategy;
  
        HGLRC m_hGLRC;
-       
 -#ifdef WITH_GLEW_MX
 -      WGLEWContext *m_wglewContext;
 -#endif
 -
  #ifndef NDEBUG
        const char *m_dummyVendor;
        const char *m_dummyRenderer;
  
        static HGLRC s_sharedHGLRC;
        static int   s_sharedCount;
 -
 -      static bool s_singleContextMode;
  };
  
  #endif  // __GHOST_CONTEXTWGL_H__
@@@ -88,7 -88,7 +88,7 @@@ public
         * \return The dimension of the main display.
         */
        void getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const;
-       
        /** Returns the combine dimensions of all monitors.
         * \return The dimension of the workspace.
         */
            const bool exclusive = false,
            const GHOST_TEmbedderWindowID parentWindow = 0
            );
-       
 +      /**
 +       * Create a new offscreen context.
 +       * Never explicitly delete the context, use disposeContext() instead.
 +       * \return  The new context (or 0 if creation failed).
 +       */
 +      GHOST_IContext *
 +      createOffscreenContext(
 +          );
 +
 +      /**
 +       * Dispose of a context.
 +       * \param   context Pointer to the context to be disposed.
 +       * \return  Indication of success.
 +       */
 +      GHOST_TSuccess
 +      disposeContext(
 +          GHOST_IContext *context
 +          );
 +
        /***************************************************************************************
         * Event management functionality
         ***************************************************************************************/
         * \return Indication of the presence of events.
         */
        bool processEvents(bool waitForEvent);
-       
        /**
         * Handle User request to quit, from Menu bar Quit, and Cmd+Q
         * Display alert panel if changes performed since last save
         */
        GHOST_TUns8 handleQuitRequest();
-       
        /**
         * Handle Cocoa openFile event
         * Display confirmation request panel if changes performed since last save
         */
        bool handleOpenDocumentRequest(void *filepathStr);
-       
        /**
         * Handles a drag'n'drop destination event. Called by GHOST_WindowCocoa window subclass
         * \param eventType The type of drag'n'drop event
         */
        GHOST_TSuccess handleDraggingEvent(GHOST_TEventType eventType, GHOST_TDragnDropTypes draggedObjectType,
                                           GHOST_WindowCocoa *window, int mouseX, int mouseY, void *data);
-       
        /***************************************************************************************
         * Cursor management functionality
         ***************************************************************************************/
         * \return                      Indication of success.
         */
        GHOST_TSuccess setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y);
-       
        /***************************************************************************************
         * Access to mouse button and keyboard states.
         ***************************************************************************************/
         * \return                              Returns the selected buffer
         */
        GHOST_TUns8 *getClipboard(bool selection) const;
-       
        /**
         * Puts buffer to system clipboard
         * \param buffer        The buffer to be copied
         * \return Indication whether the event was handled.
         */
        GHOST_TSuccess handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa *window);
-       
        /**
         * Handles the Cocoa event telling the application has become active (again)
         * \return Indication whether the event was handled.
        int toggleConsole(int action) {
                return 0;
        }
-       
        /**
         * Handles a tablet event.
         * \param eventPtr      An NSEvent pointer (casted to void* to enable compilation in standard C++)
         * \return Indication whether the event was handled.
         */
        GHOST_TSuccess handleKeyEvent(void *eventPtr);
-       
        /**
         * Informs if the system provides native dialogs (eg. confirm quit)
         */
@@@ -303,19 -284,19 +303,19 @@@ protected
  
        /** Start time at initialization. */
        GHOST_TUns64 m_start_time;
-       
        /** Event has been processed directly by Cocoa (or NDOF manager) and has sent a ghost event to be dispatched */
        bool m_outsideLoopEventProcessed;
-       
        /** Raised window is not yet known by the window manager, so delay application become active event handling */
        bool m_needDelayedApplicationBecomeActiveEventProcessing;
-       
        /** State of the modifiers. */
        GHOST_TUns32 m_modifierMask;
  
        /** Ignores window size messages (when window is dragged). */
        bool m_ignoreWindowSizedMessages;
-       
        /** Temporarily ignore momentum scroll events */
        bool m_ignoreMomentumScroll;
        /** Is the scroll wheel event generated by a multitouch trackpad or mouse? */
  #include "GHOST_WindowManager.h"
  #include "GHOST_WindowWin32.h"
  
 +#if defined(WITH_GL_EGL)
 +#  include "GHOST_ContextEGL.h"
 +#else
 +#  include "GHOST_ContextWGL.h"
 +#endif
 +
  #ifdef WITH_INPUT_NDOF
    #include "GHOST_NDOFManagerWin32.h"
  #endif
@@@ -240,7 -234,7 +240,7 @@@ GHOST_TUns64 GHOST_SystemWin32::getMill
        __int64 delta = 1000 * (count - m_start);
  
        GHOST_TUns64 t = (GHOST_TUns64)(delta / m_freq);
-       return t; 
+       return t;
  }
  
  
@@@ -305,109 -299,21 +305,109 @@@ GHOST_IWindow *GHOST_SystemWin32::creat
  }
  
  
 +/**
 + * Create a new offscreen context.
 + * Never explicitly delete the window, use disposeContext() instead.
 + * \return  The new context (or 0 if creation failed).
 + */
 +GHOST_IContext *GHOST_SystemWin32::createOffscreenContext()
 +{
 +      bool debug_context = false; /* TODO: inform as a parameter */
 +
 +      GHOST_Context *context;
 +
 +#if defined(WITH_GL_PROFILE_CORE)
 +      for (int minor = 5; minor >= 0; --minor) {
 +                      context = new GHOST_ContextWGL(
 +                          false, true, 0,
 +                          NULL, NULL,
 +                          WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
 +                          4, minor,
 +                          (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
 +                          GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
 +
 +                      if (context->initializeDrawingContext()) {
 +                              return context;
 +                      }
 +                      else {
 +                              delete context;
 +                      }
 +              }
 +
 +              context = new GHOST_ContextWGL(
 +                  false, true, 0,
 +                  NULL, NULL,
 +                  WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
 +                  3, 3,
 +                  (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
 +                  GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
 +
 +              if (context->initializeDrawingContext()) {
 +                      return context;
 +              }
 +              else {
 +                      MessageBox(
 +                              NULL,
 +                              "Blender requires a graphics driver with at least OpenGL 3.3 support.\n\n"
 +                              "The program will now close.",
 +                              "Blender - Unsupported Graphics Driver!",
 +                              MB_OK | MB_ICONERROR);
 +                      delete context;
 +                      exit();
 +              }
 +
 +#elif defined(WITH_GL_PROFILE_COMPAT)
 +              // ask for 2.1 context, driver gives any GL version >= 2.1 (hopefully the latest compatibility profile)
 +              // 2.1 ignores the profile bit & is incompatible with core profile
 +              context = new GHOST_ContextWGL(
 +                      false, true, 0,
 +                      NULL, NULL,
 +                      0, // no profile bit
 +                      2, 1,
 +                      (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
 +                      GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
 +
 +              if (context->initializeDrawingContext()) {
 +                      return context;
 +              }
 +              else {
 +                      delete context;
 +              }
 +#else
 +#  error // must specify either core or compat at build time
 +#endif
 +
 +      return NULL;
 +}
 +
 +/**
 + * Dispose of a context.
 + * \param   context Pointer to the context to be disposed.
 + * \return  Indication of success.
 + */
 +GHOST_TSuccess GHOST_SystemWin32::disposeContext(GHOST_IContext *context)
 +{
 +      delete context;
 +
 +      return GHOST_kSuccess;
 +}
 +
 +
  bool GHOST_SystemWin32::processEvents(bool waitForEvent)
  {
        MSG msg;
 -      bool anyProcessed = false;
 +      bool hasEventHandled = false;
  
        do {
                GHOST_TimerManager *timerMgr = getTimerManager();
  
 -              if (waitForEvent && !::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE)) {
 +              if (waitForEvent && !::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
  #if 1
                        ::Sleep(1);
  #else
                        GHOST_TUns64 next = timerMgr->nextFireTime();
                        GHOST_TInt64 maxSleep = next - getMilliSeconds();
-                       
                        if (next == GHOST_kFireTimeNever) {
                                ::WaitMessage();
                        }
                }
  
                if (timerMgr->fireTimers(getMilliSeconds())) {
 -                      anyProcessed = true;
 +                      hasEventHandled = true;
                }
  
                // Process all the events waiting for us
 -              while (::PeekMessageW(&msg, 0, 0, 0, PM_REMOVE) != 0) {
 +              while (::PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) != 0) {
                        // TranslateMessage doesn't alter the message, and doesn't change our raw keyboard data.
                        // Needed for MapVirtualKey or if we ever need to get chars from wm_ime_char or similar.
                        ::TranslateMessage(&msg);
                        ::DispatchMessageW(&msg);
 -                      anyProcessed = true;
                }
 -      } while (waitForEvent && !anyProcessed);
  
 -      return anyProcessed;
 +              if (hasEventHandled == false) {
 +                      // Check if we have events handled by the system
 +                      // (for example the `GHOST_kEventWindowClose`).
 +                      hasEventHandled = m_eventManager->getNumEvents() != 0;
 +              }
 +
 +      } while (waitForEvent && !hasEventHandled);
 +
 +      return hasEventHandled;
  }
  
  
@@@ -469,17 -369,17 +469,17 @@@ GHOST_TSuccess GHOST_SystemWin32::getMo
        keys.set(GHOST_kModifierKeyLeftShift, down);
        down = HIBYTE(::GetKeyState(VK_RSHIFT)) != 0;
        keys.set(GHOST_kModifierKeyRightShift, down);
-       
        down = HIBYTE(::GetKeyState(VK_LMENU)) != 0;
        keys.set(GHOST_kModifierKeyLeftAlt, down);
        down = HIBYTE(::GetKeyState(VK_RMENU)) != 0;
        keys.set(GHOST_kModifierKeyRightAlt, down);
-       
        down = HIBYTE(::GetKeyState(VK_LCONTROL)) != 0;
        keys.set(GHOST_kModifierKeyLeftControl, down);
        down = HIBYTE(::GetKeyState(VK_RCONTROL)) != 0;
        keys.set(GHOST_kModifierKeyRightControl, down);
-       
        bool lwindown = HIBYTE(::GetKeyState(VK_LWIN)) != 0;
        bool rwindown = HIBYTE(::GetKeyState(VK_RWIN)) != 0;
        if (lwindown || rwindown)
@@@ -512,7 -412,7 +512,7 @@@ GHOST_TSuccess GHOST_SystemWin32::getBu
  GHOST_TSuccess GHOST_SystemWin32::init()
  {
        GHOST_TSuccess success = GHOST_System::init();
-       
        /* Disable scaling on high DPI displays on Vista */
        HMODULE
            user32 = ::LoadLibraryA("user32.dll");
                wc.cbWndExtra = 0;
                wc.hInstance = ::GetModuleHandle(0);
                wc.hIcon = ::LoadIcon(wc.hInstance, "APPICON");
-               
                if (!wc.hIcon) {
                        ::LoadIcon(NULL, IDI_APPLICATION);
                }
                wc.hCursor = ::LoadCursor(0, IDC_ARROW);
-               wc.hbrBackground = 
+               wc.hbrBackground =
  #ifdef INW32_COMPISITING
                        (HBRUSH)CreateSolidBrush
  #endif
                        success = GHOST_kFailure;
                }
        }
-       
        return success;
  }
  
@@@ -583,7 -483,7 +583,7 @@@ GHOST_TKey GHOST_SystemWin32::hardKey(R
        *keyDown = !(raw.data.keyboard.Flags & RI_KEY_BREAK) && msg != WM_KEYUP && msg != WM_SYSKEYUP;
  
        key = this->convertKey(raw.data.keyboard.VKey, raw.data.keyboard.MakeCode, (raw.data.keyboard.Flags & (RI_KEY_E1 | RI_KEY_E0)));
-       
        // extra handling of modifier keys: don't send repeats out from GHOST
        if (key >= GHOST_kKeyLeftShift && key <= GHOST_kKeyRightAlt) {
                bool changed = false;
                        default:
                                break;
                }
-               
                if (changed) {
                        modifiers.set(modifier, (bool)*keyDown);
                        system->storeModifierKeys(modifiers);
                        key = GHOST_kKeyUnknown;
                }
        }
-       
  
        if (vk) *vk = raw.data.keyboard.VKey;
  
@@@ -682,7 -582,7 +682,7 @@@ GHOST_TKey GHOST_SystemWin32::convertKe
                switch (vKey) {
                        case VK_RETURN:
                                key = (extend) ? GHOST_kKeyNumpadEnter : GHOST_kKeyEnter; break;
-                       
                        case VK_BACK:     key = GHOST_kKeyBackSpace;        break;
                        case VK_TAB:      key = GHOST_kKeyTab;              break;
                        case VK_ESCAPE:   key = GHOST_kKeyEsc;              break;
                                break;
                }
        }
-       
        return key;
  }
  
@@@ -799,7 -699,7 +799,7 @@@ GHOST_EventCursor *GHOST_SystemWin32::p
  {
        GHOST_TInt32 x_screen, y_screen;
        GHOST_SystemWin32 *system = (GHOST_SystemWin32 *) getSystem();
-       
        system->getCursorPosition(x_screen, y_screen);
  
        /* TODO: CHECK IF THIS IS A TABLET EVENT */
@@@ -856,7 -756,7 +856,7 @@@ void GHOST_SystemWin32::processWheelEve
  
        int acc = system->m_wheelDeltaAccum;
        int delta = GET_WHEEL_DELTA_WPARAM(wParam);
-       
        if (acc * delta < 0) {
                // scroll direction reversed.
                acc = 0;
        acc += delta;
        int direction = (acc >= 0) ? 1 : -1;
        acc = abs(acc);
-       
        while (acc >= WHEEL_DELTA) {
                system->pushEvent(new GHOST_EventWheel(system->getMilliSeconds(), window, direction));
                acc -= WHEEL_DELTA;
@@@ -1178,10 -1078,10 +1178,10 @@@ LRESULT WINAPI GHOST_SystemWin32::s_wnd
                                 */
                                case WM_DEADCHAR:
                                        /* The WM_DEADCHAR message is posted to the window with the keyboard focus when a
-                                        * WM_KEYUP message is translated by the TranslateMessage function. WM_DEADCHAR 
-                                        * specifies a character code generated by a dead key. A dead key is a key that 
-                                        * generates a character, such as the umlaut (double-dot), that is combined with 
-                                        * another character to form a composite character. For example, the umlaut-O 
+                                        * WM_KEYUP message is translated by the TranslateMessage function. WM_DEADCHAR
+                                        * specifies a character code generated by a dead key. A dead key is a key that
+                                        * generates a character, such as the umlaut (double-dot), that is combined with
+                                        * another character to form a composite character. For example, the umlaut-O
                                         * character (Ö) is generated by typing the dead key for the umlaut character, and
                                         * then typing the O key.
                                         */
                                 * a dead key that is pressed while holding down the alt key.
                                 */
                                case WM_SYSCHAR:
-                                       /* The WM_SYSCHAR message is sent to the window with the keyboard focus when 
-                                        * a WM_SYSCHAR message is translated by the TranslateMessage function. 
-                                        * WM_SYSCHAR specifies the character code of a dead key - that is, 
+                                       /* The WM_SYSCHAR message is sent to the window with the keyboard focus when
+                                        * a WM_SYSCHAR message is translated by the TranslateMessage function.
+                                        * WM_SYSCHAR specifies the character code of a dead key - that is,
                                         * a dead key that is pressed while holding down the alt key.
                                         * To prevent the sound, DefWindowProc must be avoided by return
                                         */
                                        break;
                                case WM_SYSCOMMAND:
-                                       /* The WM_SYSCHAR message is sent to the window when system commands such as 
-                                        * maximize, minimize  or close the window are triggered. Also it is sent when ALT 
+                                       /* The WM_SYSCHAR message is sent to the window when system commands such as
+                                        * maximize, minimize  or close the window are triggered. Also it is sent when ALT
                                         * button is press for menu. To prevent this we must return preventing DefWindowProc.
                                         */
                                        if (wParam == SC_KEYMENU) {
                                        break;
                                case WM_MOUSEWHEEL:
                                {
-                                       /* The WM_MOUSEWHEEL message is sent to the focus window 
-                                        * when the mouse wheel is rotated. The DefWindowProc 
+                                       /* The WM_MOUSEWHEEL message is sent to the focus window
+                                        * when the mouse wheel is rotated. The DefWindowProc
                                         * function propagates the message to the window's parent.
-                                        * There should be no internal forwarding of the message, 
-                                        * since DefWindowProc propagates it up the parent chain 
+                                        * There should be no internal forwarding of the message,
+                                        * since DefWindowProc propagates it up the parent chain
                                         * until it finds a window that processes it.
                                         */
  
                                        POINT mouse_pos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
                                        HWND mouse_hwnd = ChildWindowFromPoint(HWND_DESKTOP, mouse_pos);
                                        GHOST_WindowWin32 *mouse_window = (GHOST_WindowWin32 *)::GetWindowLongPtr(mouse_hwnd, GWLP_USERDATA);
-                                       
                                        processWheelEvent(mouse_window ? mouse_window : window , wParam, lParam);
                                        eventHandled = true;
  #ifdef BROKEN_PEEK_TOUCHPAD
                                        /* The WM_SETCURSOR message is sent to a window if the mouse causes the cursor
                                         * to move within a window and mouse input is not captured.
                                         * This means we have to set the cursor shape every time the mouse moves!
-                                        * The DefWindowProc function uses this message to set the cursor to an 
+                                        * The DefWindowProc function uses this message to set the cursor to an
                                         * arrow if it is not in the client area.
                                         */
                                        if (LOWORD(lParam) == HTCLIENT) {
                                                window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
                                                // Bypass call to DefWindowProc
                                                return 0;
-                                       } 
+                                       }
                                        else {
                                                // Outside of client area show standard cursor
                                                window->loadCursor(true, GHOST_kStandardCursorDefault);
                                 * that contains the cursor. If a window has captured the mouse, this message is not posted.
                                 */
                                case WM_NCHITTEST:
-                                       /* The WM_NCHITTEST message is sent to a window when the cursor moves, or 
-                                        * when a mouse button is pressed or released. If the mouse is not captured, 
-                                        * the message is sent to the window beneath the cursor. Otherwise, the message 
-                                        * is sent to the window that has captured the mouse. 
+                                       /* The WM_NCHITTEST message is sent to a window when the cursor moves, or
+                                        * when a mouse button is pressed or released. If the mouse is not captured,
+                                        * the message is sent to the window beneath the cursor. Otherwise, the message
+                                        * is sent to the window that has captured the mouse.
                                         */
                                        break;
  
                                        event = processWindowEvent(GHOST_kEventWindowClose, window);
                                        break;
                                case WM_ACTIVATE:
-                                       /* The WM_ACTIVATE message is sent to both the window being activated and the window being 
-                                        * deactivated. If the windows use the same input queue, the message is sent synchronously, 
+                                       /* The WM_ACTIVATE message is sent to both the window being activated and the window being
+                                        * deactivated. If the windows use the same input queue, the message is sent synchronously,
                                         * first to the window procedure of the top-level window being deactivated, then to the window
                                         * procedure of the top-level window being activated. If the windows use different input queues,
-                                        * the message is sent asynchronously, so the window is activated immediately. 
+                                        * the message is sent asynchronously, so the window is activated immediately.
                                         */
                                {
                                        GHOST_ModifierKeys modifiers;
                                        break;
                                }
                                case WM_ENTERSIZEMOVE:
-                                       /* The WM_ENTERSIZEMOVE message is sent one time to a window after it enters the moving 
-                                        * or sizing modal loop. The window enters the moving or sizing modal loop when the user 
-                                        * clicks the window's title bar or sizing border, or when the window passes the 
-                                        * WM_SYSCOMMAND message to the DefWindowProc function and the wParam parameter of the 
-                                        * message specifies the SC_MOVE or SC_SIZE value. The operation is complete when 
-                                        * DefWindowProc returns. 
+                                       /* The WM_ENTERSIZEMOVE message is sent one time to a window after it enters the moving
+                                        * or sizing modal loop. The window enters the moving or sizing modal loop when the user
+                                        * clicks the window's title bar or sizing border, or when the window passes the
+                                        * WM_SYSCOMMAND message to the DefWindowProc function and the wParam parameter of the
+                                        * message specifies the SC_MOVE or SC_SIZE value. The operation is complete when
+                                        * DefWindowProc returns.
                                         */
                                        window->m_inLiveResize = 1;
                                        break;
                                        window->m_inLiveResize = 0;
                                        break;
                                case WM_PAINT:
-                                       /* An application sends the WM_PAINT message when the system or another application 
+                                       /* An application sends the WM_PAINT message when the system or another application
                                         * makes a request to paint a portion of an application's window. The message is sent
-                                        * when the UpdateWindow or RedrawWindow function is called, or by the DispatchMessage 
-                                        * function when the application obtains a WM_PAINT message by using the GetMessage or 
-                                        * PeekMessage function. 
+                                        * when the UpdateWindow or RedrawWindow function is called, or by the DispatchMessage
+                                        * function when the application obtains a WM_PAINT message by using the GetMessage or
+                                        * PeekMessage function.
                                         */
                                        if (!window->m_inLiveResize) {
                                                event = processWindowEvent(GHOST_kEventWindowUpdate, window);
                                        }
                                        break;
                                case WM_GETMINMAXINFO:
-                                       /* The WM_GETMINMAXINFO message is sent to a window when the size or 
-                                        * position of the window is about to change. An application can use 
-                                        * this message to override the window's default maximized size and 
-                                        * position, or its default minimum or maximum tracking size. 
+                                       /* The WM_GETMINMAXINFO message is sent to a window when the size or
+                                        * position of the window is about to change. An application can use
+                                        * this message to override the window's default maximized size and
+                                        * position, or its default minimum or maximum tracking size.
                                         */
                                        processMinMaxInfo((MINMAXINFO *) lParam);
                                        /* Let DefWindowProc handle it. */
                                case WM_SIZING:
                                case WM_SIZE:
                                        /* The WM_SIZE message is sent to a window after its size has changed.
-                                        * The WM_SIZE and WM_MOVE messages are not sent if an application handles the 
+                                        * The WM_SIZE and WM_MOVE messages are not sent if an application handles the
                                         * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
-                                        * to perform any move or size change processing during the WM_WINDOWPOSCHANGED 
+                                        * to perform any move or size change processing during the WM_WINDOWPOSCHANGED
                                         * message without calling DefWindowProc.
                                         */
                                        /* we get first WM_SIZE before we fully init. So, do not dispatch before we continiously resizng */
                                         * and, if needed, change its size or position.
                                         */
                                case WM_MOVE:
-                                       /* The WM_SIZE and WM_MOVE messages are not sent if an application handles the 
+                                       /* The WM_SIZE and WM_MOVE messages are not sent if an application handles the
                                         * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
-                                        * to perform any move or size change processing during the WM_WINDOWPOSCHANGED 
-                                        * message without calling DefWindowProc. 
+                                        * to perform any move or size change processing during the WM_WINDOWPOSCHANGED
+                                        * message without calling DefWindowProc.
                                         */
                                        /* see WM_SIZE comment*/
                                        if (window->m_inLiveResize) {
                                 * that all child windows still exist.
                                 */
                                case WM_NCDESTROY:
-                                       /* The WM_NCDESTROY message informs a window that its nonclient area is being destroyed. The 
+                                       /* The WM_NCDESTROY message informs a window that its nonclient area is being destroyed. The
                                         * DestroyWindow function sends the WM_NCDESTROY message to the window following the WM_DESTROY
-                                        * message. WM_DESTROY is used to free the allocated memory object associated with the window. 
+                                        * message. WM_DESTROY is used to free the allocated memory object associated with the window.
                                         */
                                        break;
                                case WM_KILLFOCUS:
-                                       /* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard focus. 
+                                       /* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard focus.
                                         * We want to prevent this if a window is still active and it loses focus to nowhere*/
                                        if (!wParam && hwnd == ::GetActiveWindow())
                                                ::SetFocus(hwnd);
                                         * when a timer expires. You can process the message by providing a WM_TIMER
                                         * case in the window procedure. Otherwise, the default window procedure will
                                         * call the TimerProc callback function specified in the call to the SetTimer
-                                        * function used to install the timer. 
+                                        * function used to install the timer.
                                         *
                                         * In GHOST, we let DefWindowProc call the timer callback.
                                         */
  GHOST_TUns8 *GHOST_SystemWin32::getClipboard(bool selection) const
  {
        char *temp_buff;
-       
        if (IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(NULL) ) {
                wchar_t *buffer;
                HANDLE hData = GetClipboardData(CF_UNICODETEXT);
                        CloseClipboard();
                        return NULL;
                }
-               
                temp_buff = alloc_utf_8_from_16(buffer, 0);
-               
                /* Buffer mustn't be accessed after CloseClipboard
                 * it would like accessing free-d memory */
                GlobalUnlock(hData);
                CloseClipboard();
-               
                return (GHOST_TUns8 *)temp_buff;
        }
        else if (IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(NULL) ) {
                        CloseClipboard();
                        return NULL;
                }
-               
                len = strlen(buffer);
                temp_buff = (char *) malloc(len + 1);
                strncpy(temp_buff, buffer, len);
                temp_buff[len] = '\0';
-               
                /* Buffer mustn't be accessed after CloseClipboard
                 * it would like accessing free-d memory */
                GlobalUnlock(hData);
                CloseClipboard();
-               
                return (GHOST_TUns8 *)temp_buff;
        }
        else {
@@@ -1626,11 -1526,11 +1626,11 @@@ void GHOST_SystemWin32::putClipboard(GH
        if (OpenClipboard(NULL)) {
                HLOCAL clipbuffer;
                wchar_t *data;
-               
                if (buffer) {
                        size_t len = count_utf_16_from_8(buffer);
                        EmptyClipboard();
-                       
                        clipbuffer = LocalAlloc(LMEM_FIXED, sizeof(wchar_t) * len);
                        data = (wchar_t *)GlobalLock(clipbuffer);
  
@@@ -1686,7 -1586,7 +1686,7 @@@ static bool getProcessName(int pid, cha
  static bool isStartedFromCommandPrompt()
  {
        HWND hwnd = GetConsoleWindow();
-       
        if (hwnd) {
                DWORD pid = (DWORD)-1;
                DWORD ppid = GetParentProcessID();
@@@ -110,7 -110,7 +110,7 @@@ public
  
        /**
         * Create a new window.
-        * The new window is added to the list of windows managed. 
+        * The new window is added to the list of windows managed.
         * Never explicitly delete the window, use disposeWindow() instead.
         * \param       title   The name of the window (displayed in the title bar of the window if the OS supports it).
         * \param       left    The coordinate of the left edge of the window.
            const bool exclusive = false,
            const GHOST_TEmbedderWindowID parentWindow = 0);
  
 +
 +      /**
 +       * Create a new offscreen context.
 +       * Never explicitly delete the window, use disposeContext() instead.
 +       * \return  The new context (or 0 if creation failed).
 +       */
 +      GHOST_IContext *createOffscreenContext();
 +
 +      /**
 +       * Dispose of a context.
 +       * \param   context Pointer to the context to be disposed.
 +       * \return  Indication of success.
 +       */
 +      GHOST_TSuccess disposeContext(GHOST_IContext *context);
 +
        /***************************************************************************************
         ** Event management functionality
         ***************************************************************************************/
         * \return Indication of the presence of events.
         */
        bool processEvents(bool waitForEvent);
-       
  
        /***************************************************************************************
         ** Cursor management functionality
         * \return                              Returns the Clipboard
         */
        GHOST_TUns8 *getClipboard(bool selection) const;
-       
        /**
         * Puts buffer to system clipboard
         * \param selection             Used by X11 only
        void putClipboard(GHOST_TInt8 *buffer, bool selection) const;
  
        /**
-        * Creates a drag'n'drop event and pushes it immediately onto the event queue. 
+        * Creates a drag'n'drop event and pushes it immediately onto the event queue.
         * Called by GHOST_DropTargetWin32 class.
         * \param eventType The type of drag'n'drop event
         * \param draggedObjectType The type object concerned (currently array of file names, string, ?bitmap)
         * \return Indication whether the event was handled.
         */
        static GHOST_TSuccess pushDragDropEvent(GHOST_TEventType eventType, GHOST_TDragnDropTypes draggedObjectType, GHOST_WindowWin32 *window, int mouseX, int mouseY, void *data);
-       
        /**
         * Confirms quitting he program when there is just one window left open
         * in the application
@@@ -242,7 -227,7 +242,7 @@@ protected
         * \return A success value.
         */
        GHOST_TSuccess exit();
-       
        /**
         * Converts raw WIN32 key codes from the wndproc to GHOST keys.
         * \param vKey          The virtual key from hardKey
         */
        GHOST_TKey processSpecialKey(short vKey, short scanCode) const;
  
-       /** 
+       /**
         * Creates a window event.
         * \param type          The type of event to create.
         * \param window        The window receiving the event (the active window).
         * param keys The new state of the modifier keys.
         */
        inline void storeModifierKeys(const GHOST_ModifierKeys& keys);
-       
        /**
         * Check current key layout for AltGr
         */
         * \return current status (1 -visible, 0 - hidden)
         */
        int toggleConsole(int action);
-       
        /** The current state of the modifier keys. */
        GHOST_ModifierKeys m_modifierKeys;
        /** State variable set at initialization. */
  
  #include "GHOST_Debug.h"
  
 +#if defined(WITH_GL_EGL)
 +#  include "GHOST_ContextEGL.h"
 +#else
 +#  include "GHOST_ContextGLX.h"
 +#endif
 +
  #ifdef WITH_XF86KEYSYM
  #include <X11/XF86keysym.h>
  #endif
@@@ -119,9 -113,8 +119,9 @@@ GHOST_SystemX11
      : GHOST_System(),
        m_start_time(0)
  {
 +      XInitThreads();
        m_display = XOpenDisplay(NULL);
-       
        if (!m_display) {
                std::cerr << "Unable to open a display" << std::endl;
                abort(); /* was return before, but this would just mean it will crash later */
        if (gettimeofday(&tv, NULL) == -1) {
                GHOST_ASSERT(false, "Could not instantiate timer!");
        }
-       
        /* Taking care not to overflow the tv.tv_sec * 1000 */
        m_start_time = GHOST_TUns64(tv.tv_sec) * 1000 + tv.tv_usec / 1000;
-       
-       
        /* use detectable autorepeate, mac and windows also do this */
        int use_xkb;
        int xkb_opcode, xkb_event, xkb_error;
        int xkb_major = XkbMajorVersion, xkb_minor = XkbMinorVersion;
-       
        use_xkb = XkbQueryExtension(m_display, &xkb_opcode, &xkb_event, &xkb_error, &xkb_major, &xkb_minor);
        if (use_xkb) {
                XkbSetDetectableAutoRepeat(m_display, true, NULL);
@@@ -244,7 -237,7 +244,7 @@@ GHOST_SystemX11:
        /* close tablet devices */
        if (m_xtablet.StylusDevice)
                XCloseDevice(m_display, m_xtablet.StylusDevice);
-       
        if (m_xtablet.EraserDevice)
                XCloseDevice(m_display, m_xtablet.EraserDevice);
  #endif /* WITH_X11_XINPUT */
@@@ -285,7 -278,7 +285,7 @@@ getMilliSeconds() cons
        /* Taking care not to overflow the tv.tv_sec * 1000 */
        return GHOST_TUns64(tv.tv_sec) * 1000 + tv.tv_usec / 1000 - m_start_time;
  }
-       
  GHOST_TUns8
  GHOST_SystemX11::
  getNumDisplays() const
@@@ -358,9 -351,9 +358,9 @@@ createWindow(const STR_String& title
                const GHOST_TEmbedderWindowID parentWindow)
  {
        GHOST_WindowX11 *window = NULL;
-       
        if (!m_display) return 0;
-       
        window = new GHOST_WindowX11(this, m_display, title,
                                     left, top, width, height,
                                     state, parentWindow, type,
        return window;
  }
  
- bool GHOST_SystemX11::supportsNativeDialogs(void) 
+ bool GHOST_SystemX11::supportsNativeDialogs(void)
  {
        return false;
  }
  
 +/**
 + * Create a new offscreen context.
 + * Never explicitly delete the context, use disposeContext() instead.
 + * \return  The new context (or 0 if creation failed).
 + */
 +GHOST_IContext *
 +GHOST_SystemX11::
 +createOffscreenContext()
 +{
 +      // During development:
 +      //   try 4.x compatibility profile
 +      //   try 3.3 compatibility profile
 +      //   fall back to 3.0 if needed
 +      //
 +      // Final Blender 2.8:
 +      //   try 4.x core profile
 +      //   try 3.3 core profile
 +      //   no fallbacks
 +
 +#if defined(WITH_GL_PROFILE_CORE)
 +      {
 +              const char *version_major = (char*)glewGetString(GLEW_VERSION_MAJOR);
 +              if (version_major != NULL && version_major[0] == '1') {
 +                      fprintf(stderr, "Error: GLEW version 2.0 and above is required.\n");
 +                      abort();
 +              }
 +      }
 +#endif
 +
 +      const int profile_mask =
 +#if defined(WITH_GL_PROFILE_CORE)
 +              GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
 +#elif defined(WITH_GL_PROFILE_COMPAT)
 +              GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
 +#else
 +#  error // must specify either core or compat at build time
 +#endif
 +
 +      GHOST_Context *context;
 +
 +      for (int minor = 5; minor >= 0; --minor) {
 +              context = new GHOST_ContextGLX(
 +                      false,
 +                      0,
 +                      (Window)NULL,
 +                      m_display,
 +                      (GLXFBConfig)NULL,
 +                      profile_mask,
 +                      4, minor,
 +                      GHOST_OPENGL_GLX_CONTEXT_FLAGS | (false ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
 +                      GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY);
 +
 +              if (context->initializeDrawingContext())
 +                      return context;
 +              else
 +                      delete context;
 +      }
 +
 +      context = new GHOST_ContextGLX(
 +              false,
 +              0,
 +              (Window)NULL,
 +              m_display,
 +              (GLXFBConfig)NULL,
 +              profile_mask,
 +              3, 3,
 +              GHOST_OPENGL_GLX_CONTEXT_FLAGS | (false ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
 +              GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY);
 +
 +      if (context->initializeDrawingContext())
 +              return context;
 +      else
 +              delete context;
 +
 +      return NULL;
 +}
 +
 +/**
 + * Dispose of a context.
 + * \param   context Pointer to the context to be disposed.
 + * \return  Indication of success.
 + */
 +GHOST_TSuccess
 +GHOST_SystemX11::
 +disposeContext(GHOST_IContext *context)
 +{
 +      delete context;
 +
 +      return GHOST_kSuccess;
 +}
 +
  #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
  static void destroyIMCallback(XIM /*xim*/, XPointer ptr, XPointer /*data*/)
  {
@@@ -516,7 -418,7 +516,7 @@@ GHOST_SystemX11:
  findGhostWindow(
                Window xwind) const
  {
-       
        if (xwind == 0) return NULL;
  
        /* It is not entirely safe to do this as the backptr may point
  
        vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
        vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end();
-       
        for (; win_it != win_end; ++win_it) {
                GHOST_WindowX11 *window = static_cast<GHOST_WindowX11 *>(*win_it);
                if (window->getXWindow() == xwind) {
                }
        }
        return NULL;
-       
  }
  
  static void SleepTillEvent(Display *display, GHOST_TInt64 maxSleep)
  {
        int fd = ConnectionNumber(display);
        fd_set fds;
-       
        FD_ZERO(&fds);
        FD_SET(fd, &fds);
  
  
                tv.tv_sec = maxSleep / 1000;
                tv.tv_usec = (maxSleep - tv.tv_sec * 1000) * 1000;
-       
                select(fd + 1, &fds, NULL, NULL, &tv);
        }
  }
@@@ -618,15 -520,15 +618,15 @@@ processEvents
  {
        /* Get all the current events -- translate them into
         * ghost events and call base class pushEvent() method. */
-       
        bool anyProcessed = false;
-       
        do {
                GHOST_TimerManager *timerMgr = getTimerManager();
-               
                if (waitForEvent && m_dirty_windows.empty() && !XPending(m_display)) {
                        GHOST_TUns64 next = timerMgr->nextFireTime();
-                       
                        if (next == GHOST_kFireTimeNever) {
                                SleepTillEvent(m_display, -1);
                        }
                                        SleepTillEvent(m_display, next - getMilliSeconds());
                        }
                }
-               
                if (timerMgr->fireTimers(getMilliSeconds())) {
                        anyProcessed = true;
                }
-               
                while (XPending(m_display)) {
                        XEvent xevent;
                        XNextEvent(m_display, &xevent);
  #endif  /* USE_UNITY_WORKAROUND */
  
                }
-               
                if (generateWindowExposeEvents()) {
                        anyProcessed = true;
                }
                        anyProcessed = true;
                }
  #endif
-               
        } while (waitForEvent && !anyProcessed);
-       
        return anyProcessed;
  }
  
@@@ -971,7 -873,7 +971,7 @@@ GHOST_SystemX11::processEvent(XEvent *x
  #else
                        char *utf8_buf = NULL;
  #endif
-                       
                        GHOST_TEventType type = (xke->type == KeyPress) ?  GHOST_kEventKeyDown : GHOST_kEventKeyUp;
  
                        GHOST_TKey gkey;
                        }
  
                        gkey = convertXKey(key_sym);
-                       
                        if (!XLookupString(xke, &ascii, 1, NULL, NULL)) {
                                ascii = '\0';
                        }
                        if (utf8_buf != utf8_array)
                                free(utf8_buf);
  #endif
-                       
                        break;
                }
  
                {
                        XButtonEvent & xbe = xe->xbutton;
                        GHOST_TButtonMask gbmask = GHOST_kButtonMaskLeft;
-                       GHOST_TEventType type = (xbe.type == ButtonPress) ? 
+                       GHOST_TEventType type = (xbe.type == ButtonPress) ?
                                                GHOST_kEventButtonDown : GHOST_kEventButtonUp;
  
                        /* process wheel mouse events and break, only pass on press events */
                                        g_event = new GHOST_EventWheel(getMilliSeconds(), window, -1);
                                break;
                        }
-                       
                        /* process rest of normal mouse buttons */
                        if (xbe.button == Button1)
                                gbmask = GHOST_kButtonMaskLeft;
                            );
                        break;
                }
-                       
                /* change of size, border, layer etc. */
                case ConfigureNotify:
                {
                        /* XConfigureEvent & xce = xe->xconfigure; */
  
-                       g_event = new 
+                       g_event = new
                                  GHOST_Event(
                            getMilliSeconds(),
                            GHOST_kEventWindowSize,
  
                        /* TODO: make sure this is the correct place for activate/deactivate */
                        // printf("X: focus %s for window %d\n", xfe.type == FocusIn ? "in" : "out", (int) xfe.window);
-               
                        /* May have to look at the type of event and filter some out. */
  
-                       GHOST_TEventType gtype = (xfe.type == FocusIn) ? 
+                       GHOST_TEventType gtype = (xfe.type == FocusIn) ?
                                                 GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate;
  
  #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
                        }
  #endif
  
-                       g_event = new 
+                       g_event = new
                                  GHOST_Event(
                            getMilliSeconds(),
                            gtype,
                        XClientMessageEvent & xcme = xe->xclient;
  
                        if (((Atom)xcme.data.l[0]) == m_atom.WM_DELETE_WINDOW) {
-                               g_event = new 
+                               g_event = new
                                          GHOST_Event(
                                    getMilliSeconds(),
                                    GHOST_kEventWindowClose,
  
                        break;
                }
-               
                case DestroyNotify:
                        ::exit(-1);
                /* We're not interested in the following things.(yet...) */
                case NoExpose:
                case GraphicsExpose:
                        break;
-               
                case EnterNotify:
                case LeaveNotify:
                {
                         */
                        XCrossingEvent &xce = xe->xcrossing;
                        if (xce.mode == NotifyNormal) {
-                               g_event = new 
+                               g_event = new
                                          GHOST_EventCursor(
                                    getMilliSeconds(),
                                    GHOST_kEventCursorMove,
                        XEvent nxe;
                        Atom target, utf8_string, string, compound_text, c_string;
                        XSelectionRequestEvent *xse = &xe->xselectionrequest;
-                       
                        target = XInternAtom(m_display, "TARGETS", False);
                        utf8_string = XInternAtom(m_display, "UTF8_STRING", False);
                        string = XInternAtom(m_display, "STRING", False);
                        compound_text = XInternAtom(m_display, "COMPOUND_TEXT", False);
                        c_string = XInternAtom(m_display, "C_STRING", False);
-                       
                        /* support obsolete clients */
                        if (xse->property == None) {
                                xse->property = xse->target;
                        }
-                       
                        nxe.xselection.type = SelectionNotify;
                        nxe.xselection.requestor = xse->requestor;
                        nxe.xselection.property = xse->property;
                        nxe.xselection.selection = xse->selection;
                        nxe.xselection.target = xse->target;
                        nxe.xselection.time = xse->time;
-                       
                        /* Check to see if the requestor is asking for String */
                        if (xse->target == utf8_string ||
                            xse->target == string ||
                                /* Change property to None because we do not support anything but STRING */
                                nxe.xselection.property = None;
                        }
-                       
                        /* Send the event to the client 0 0 == False, SelectionNotify */
                        XSendEvent(m_display, xse->requestor, 0, 0, &nxe);
                        XFlush(m_display);
                        break;
                }
-               
                default:
                {
  #ifdef WITH_X11_XINPUT
@@@ -1584,7 -1486,7 +1584,7 @@@ getButtons
        }
        else {
                return GHOST_kFailure;
-       }       
+       }
  
        return GHOST_kSuccess;
  }
@@@ -1688,7 -1590,7 +1688,7 @@@ setCursorPosition
  #endif
  
        XSync(m_display, 0); /* Sync to process all requests */
-       
        return GHOST_kSuccess;
  }
  
@@@ -1699,7 -1601,7 +1699,7 @@@ addDirtyWindow
                GHOST_WindowX11 *bad_wind)
  {
        GHOST_ASSERT((bad_wind != NULL), "addDirtyWindow() NULL ptr trapped (window)");
-       
        m_dirty_windows.push_back(bad_wind);
  }
  
@@@ -1711,7 -1613,7 +1711,7 @@@ generateWindowExposeEvents(
        vector<GHOST_WindowX11 *>::iterator w_start = m_dirty_windows.begin();
        vector<GHOST_WindowX11 *>::const_iterator w_end = m_dirty_windows.end();
        bool anyProcessed = false;
-       
        for (; w_start != w_end; ++w_start) {
                GHOST_Event *g_event = new
                                       GHOST_Event(
                    );
  
                (*w_start)->validate();
-               
                if (g_event) {
                        pushEvent(g_event);
                        anyProcessed = true;
@@@ -2110,12 -2012,12 +2110,12 @@@ GHOST_TUns8 *GHOST_SystemX11::getClipbo
                unsigned char *tmp_data = (unsigned char *) malloc(sel_len + 1);
                memcpy((char *)tmp_data, (char *)sel_buf, sel_len);
                tmp_data[sel_len] = '\0';
-               
                if (sseln == m_atom.STRING)
                        XFree(sel_buf);
                else
                        free(sel_buf);
-               
                return tmp_data;
        }
        return(NULL);
@@@ -2156,7 -2058,7 +2156,7 @@@ void GHOST_SystemX11::putClipboard(GHOS
  }
  
  #ifdef WITH_XDND
- GHOST_TSuccess GHOST_SystemX11::pushDragDropEvent(GHOST_TEventType eventType, 
+ GHOST_TSuccess GHOST_SystemX11::pushDragDropEvent(GHOST_TEventType eventType,
                GHOST_TDragnDropTypes draggedObjectType,
                GHOST_IWindow *window,
                int mouseX, int mouseY,
@@@ -2322,7 -2224,7 +2322,7 @@@ void GHOST_SystemX11::refreshXInputDevi
  
                        for (int i = 0; i < device_count; ++i) {
                                char *device_type = device_info[i].type ? XGetAtomName(m_display, device_info[i].type) : NULL;
-                               
  //                            printf("Tablet type:'%s', name:'%s', index:%d\n", device_type, device_info[i].name, i);
  
  
  
                                                                break;
                                                        }
-                                               
                                                        ici = (XAnyClassPtr)(((char *)ici) + ici->length);
                                                }
                                        }
@@@ -86,7 -86,7 +86,7 @@@ public
  
        GHOST_SystemX11(
            );
-       
        /**
         * Destructor.
         */
        GHOST_TUns64
        getMilliSeconds(
            ) const;
-       
  
        /**
         * Returns the number of displays on this system.
  
        /**
         * Create a new window.
-        * The new window is added to the list of windows managed. 
+        * The new window is added to the list of windows managed.
         * Never explicitly delete the window, use disposeWindow() instead.
         * \param       title   The name of the window (displayed in the title bar of the window if the OS supports it).
         * \param       left            The coordinate of the left edge of the window.
            const GHOST_TEmbedderWindowID parentWindow = 0
            );
  
 +
 +      /**
 +       * Create a new offscreen context.
 +       * Never explicitly delete the context, use disposeContext() instead.
 +       * \return  The new context (or 0 if creation failed).
 +       */
 +      GHOST_IContext *
 +      createOffscreenContext(
 +          );
 +
 +      /**
 +       * Dispose of a context.
 +       * \param   context Pointer to the context to be disposed.
 +       * \return  Indication of success.
 +       */
 +      GHOST_TSuccess
 +      disposeContext(
 +          GHOST_IContext *context
 +          );
 +
        /**
         * Retrieves events from the system and stores them in the queue.
         * \param waitForEvent Flag to wait for an event (or return immediately).
            GHOST_TInt32& x,
            GHOST_TInt32& y
            ) const;
-       
        GHOST_TSuccess
        setCursorPosition(
            GHOST_TInt32 x,
  
        /**
         * Flag a window as dirty. This will
-        * generate a GHOST window update event on a call to processEvents() 
+        * generate a GHOST window update event on a call to processEvents()
         */
  
        void
        addDirtyWindow(
            GHOST_WindowX11 *bad_wind
            );
-   
-  
        /**
         * return a pointer to the X11 display structure
         */
        getXDisplay(
                ) {
                return m_display;
-       }       
+       }
  
  #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
        XIM
         * \return                              Returns the Clipboard indicated by Flag
         */
        GHOST_TUns8 *getClipboard(bool selection) const;
-       
        /**
         * Puts buffer to system clipboard
         * \param buffer        The buffer to copy to the clipboard
  
  #ifdef WITH_XDND
        /**
-        * Creates a drag'n'drop event and pushes it immediately onto the event queue. 
+        * Creates a drag'n'drop event and pushes it immediately onto the event queue.
         * Called by GHOST_DropTargetX11 class.
         * \param eventType The type of drag'n'drop event
         * \param draggedObjectType The type object concerned (currently array of file names, string, ?bitmap)
         * \param mouseX x mouse coordinate (in window coordinates)
         * \param mouseY y mouse coordinate
         * \param window The window on which the event occurred
-        * \return Indication whether the event was handled. 
+        * \return Indication whether the event was handled.
         */
        static GHOST_TSuccess pushDragDropEvent(GHOST_TEventType eventType, GHOST_TDragnDropTypes draggedObjectType, GHOST_IWindow *window, int mouseX, int mouseY, void *data);
  #endif
@@@ -99,7 -99,7 +99,7 @@@ GHOST_WindowWin32::GHOST_WindowWin32(GH
        if (state != GHOST_kWindowStateFullScreen) {
                RECT rect;
                MONITORINFO monitor;
-               GHOST_TUns32 tw, th; 
+               GHOST_TUns32 tw, th;
  
  #ifndef _MSC_VER
                int cxsizeframe = GetSystemMetrics(SM_CXSIZEFRAME);
                        width = rect.right - rect.left;
                        height = rect.bottom - rect.top;
                }
-               
                wchar_t *title_16 = alloc_utf16_from_8((char *)(const char *)title, 0);
                m_hWnd = ::CreateWindowW(
                        s_windowClassName,          // pointer to registered class name
                        ::ShowWindow(m_hWnd, nCmdShow);
  #ifdef WIN32_COMPOSITING
                        if (alphaBackground && parentwindowhwnd == 0) {
-                               
                                HRESULT hr = S_OK;
  
                                // Create and populate the Blur Behind structure
@@@ -619,79 -619,101 +619,79 @@@ GHOST_TSuccess GHOST_WindowWin32::inval
  GHOST_Context *GHOST_WindowWin32::newDrawingContext(GHOST_TDrawingContextType type)
  {
        if (type == GHOST_kDrawingContextTypeOpenGL) {
 -#if !defined(WITH_GL_EGL)
 +              GHOST_Context *context;
  
  #if defined(WITH_GL_PROFILE_CORE)
 -              GHOST_Context *context = new GHOST_ContextWGL(
 -                      m_wantStereoVisual,
 -                      m_wantAlphaBackground,
 -                      m_wantNumOfAASamples,
 -                      m_hWnd,
 -                      m_hDC,
 -                      WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
 -                      3, 2,
 -                      GHOST_OPENGL_WGL_CONTEXT_FLAGS,
 -                      GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
 -#elif defined(WITH_GL_PROFILE_ES20)
 -              GHOST_Context *context = new GHOST_ContextWGL(
 -                      m_wantStereoVisual,
 -                      m_wantAlphaBackground,
 -                      m_wantNumOfAASamples,
 -                      m_hWnd,
 -                      m_hDC,
 -                      WGL_CONTEXT_ES2_PROFILE_BIT_EXT,
 -                      2, 0,
 -                      GHOST_OPENGL_WGL_CONTEXT_FLAGS,
 -                      GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
 +              /* - AMD and Intel give us exactly this version
 +               * - NVIDIA gives at least this version <-- desired behavior
 +               * So we ask for 4.5, 4.4 ... 3.3 in descending order to get the best version on the user's system. */
 +              for (int minor = 5; minor >= 0; --minor) {
 +                      context = new GHOST_ContextWGL(
 +                          m_wantStereoVisual,
 +                          m_wantAlphaBackground,
 +                          m_wantNumOfAASamples,
 +                          m_hWnd,
 +                          m_hDC,
 +                          WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
 +                          4, minor,
 +                          (m_debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
 +                          GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
 +
 +                      if (context->initializeDrawingContext()) {
 +                              return context;
 +                      }
 +                      else {
 +                              delete context;
 +                      }
 +              }
 +              context = new GHOST_ContextWGL(
 +                  m_wantStereoVisual,
 +                  m_wantAlphaBackground,
 +                  m_wantNumOfAASamples,
 +                  m_hWnd,
 +                  m_hDC,
 +                  WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
 +                  3, 3,
 +                  (m_debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
 +                  GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
 +
 +              if (context->initializeDrawingContext()) {
 +                      return context;
 +              }
 +              else {
 +                      MessageBox(
 +                              m_hWnd,
 +                              "Blender requires a graphics driver with at least OpenGL 3.3 support.\n\n"
 +                              "The program will now close.",
 +                              "Blender - Unsupported Graphics Driver!",
 +                              MB_OK | MB_ICONERROR);
 +                      delete context;
 +                      exit(0);
 +              }
 +
  #elif defined(WITH_GL_PROFILE_COMPAT)
 -              GHOST_Context *context = new GHOST_ContextWGL(
 +              // ask for 2.1 context, driver gives any GL version >= 2.1 (hopefully the latest compatibility profile)
 +              // 2.1 ignores the profile bit & is incompatible with core profile
 +              context = new GHOST_ContextWGL(
                        m_wantStereoVisual,
                        m_wantAlphaBackground,
                        m_wantNumOfAASamples,
                        m_hWnd,
                        m_hDC,
 -#if 1
 -                      0, // profile bit
 -                      2, 1, // GL version requested
 -#else
 -                      // switch to this for Blender 2.8 development
 -                      WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
 -                      3, 2,
 -#endif
 +                      0, // no profile bit
 +                      2, 1,
                        (m_debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
                        GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
 -#else
 -#  error
 -#endif
 -
 -#else
  
 -#if defined(WITH_GL_PROFILE_CORE)
 -              GHOST_Context *context = new GHOST_ContextEGL(
 -                      m_wantStereoVisual,
 -                      m_wantNumOfAASamples,
 -                      m_hWnd,
 -                      m_hDC,
 -                      EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
 -                      3, 2,
 -                      GHOST_OPENGL_EGL_CONTEXT_FLAGS,
 -                      GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
 -                      EGL_OPENGL_API);
 -#elif defined(WITH_GL_PROFILE_ES20)
 -              GHOST_Context *context = new GHOST_ContextEGL(
 -                      m_wantStereoVisual,
 -                      m_wantNumOfAASamples,
 -                      m_hWnd,
 -                      m_hDC,
 -                      0, // profile bit
 -                      2, 0,
 -                      GHOST_OPENGL_EGL_CONTEXT_FLAGS,
 -                      GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
 -                      EGL_OPENGL_ES_API);
 -#elif defined(WITH_GL_PROFILE_COMPAT)
 -              GHOST_Context *context = new GHOST_ContextEGL(
 -                      m_wantStereoVisual,
 -                      m_wantNumOfAASamples,
 -                      m_hWnd,
 -                      m_hDC,
 -#if 1
 -                      0, // profile bit
 -                      2, 1, // GL version requested
 -#else
 -                      // switch to this for Blender 2.8 development
 -                      EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT,
 -                      3, 2,
 -#endif
 -                      GHOST_OPENGL_EGL_CONTEXT_FLAGS,
 -                      GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
 -                      EGL_OPENGL_API);
 -#else
 -#  error
 -#endif
 -
 -#endif
 -              if (context->initializeDrawingContext())
 +              if (context->initializeDrawingContext()) {
                        return context;
 -              else
 +              }
 +              else {
                        delete context;
 +              }
 +#else
 +#  error // must specify either core or compat at build time
 +#endif
        }
  
        return NULL;
@@@ -817,7 -839,7 +817,7 @@@ GHOST_TSuccess GHOST_WindowWin32::setWi
                m_cursorGrabBounds.m_l = m_cursorGrabBounds.m_r = -1; /* disable */
                registerMouseClickEvent(3);
        }
-       
        return GHOST_kSuccess;
  }
  
@@@ -1041,7 -1063,7 +1041,7 @@@ GHOST_TSuccess GHOST_WindowWin32::setWi
  
  
  GHOST_TSuccess GHOST_WindowWin32::setProgressBar(float progress)
- {     
+ {
        /*SetProgressValue sets state to TBPF_NORMAL automaticly*/
        if (m_Bar && S_OK == m_Bar->SetProgressValue(m_hWnd, 10000 * progress, 10000))
                return GHOST_kSuccess;
@@@ -204,7 -204,7 +204,7 @@@ static XVisualInfo *x11_visualinfo_from
                numOfAASamples = 0;
                actualSamples = 0;
        }
-       
  #ifdef WITH_X11_ALPHA
        if (   needAlpha
            && glx_version >= 103
                /* legacy, don't use extension */
                for (;;) {
                        GHOST_X11_GL_GetAttributes(glx_attribs, 64, actualSamples, stereoVisual, needAlpha, false);
-                       
                        visual = glXChooseVisual(display, DefaultScreen(display), glx_attribs);
  
                        /* Any sample level or even zero, which means oversampling disabled, is good
@@@ -408,7 -408,7 +408,7 @@@ GHOST_WindowX11(GHOST_SystemX11 *system
                Window root_return;
                int x_return, y_return;
                unsigned int w_return, h_return, border_w_return, depth_return;
-               
                XGetGeometry(m_display, parentWindow, &root_return, &x_return, &y_return,
                             &w_return, &h_return, &border_w_return, &depth_return);
  
                        &xattributes);
  
                XSelectInput(m_display, parentWindow, SubstructureNotifyMask);
-               
-       }       
+       }
  
  #ifdef WITH_XDND
        /* initialize drop target for newly created window */
@@@ -717,12 -717,12 +717,12 @@@ getTitle
                STR_String& title) const
  {
        char *name = NULL;
-       
        XFetchName(m_display, m_window, &name);
        title = name ? name : "untitled";
        XFree(name);
  }
-       
  void
  GHOST_WindowX11::
  getWindowBounds(
@@@ -742,12 -742,12 +742,12 @@@ getClientBounds
        int x_return, y_return;
        unsigned int w_return, h_return, border_w_return, depth_return;
        GHOST_TInt32 screen_x, screen_y;
-       
        XGetGeometry(m_display, m_window, &root_return, &x_return, &y_return,
                     &w_return, &h_return, &border_w_return, &depth_return);
  
        clientToScreen(0, 0, screen_x, screen_y);
-       
        bounds.m_l = screen_x;
        bounds.m_r = bounds.m_l + w_return;
        bounds.m_t = screen_y;
@@@ -794,7 -794,7 +794,7 @@@ setClientSize
        XConfigureWindow(m_display, m_window, value_mask, &values);
        return GHOST_kSuccess;
  
- }     
+ }
  
  void
  GHOST_WindowX11::
@@@ -818,7 -818,7 +818,7 @@@ screenToClient
        outX = ax;
        outY = ay;
  }
-                
  void
  GHOST_WindowX11::
  clientToScreen(
@@@ -1159,7 -1159,7 +1159,7 @@@ setOrder
                GHOST_TWindowOrder order)
  {
        if (order == GHOST_kWindowOrderTop) {
-               XWindowAttributes attr;   
+               XWindowAttributes attr;
                Atom atom;
  
                /* We use both XRaiseWindow and _NET_ACTIVE_WINDOW, since some
        else {
                return GHOST_kFailure;
        }
-       
        return GHOST_kSuccess;
  }
  
@@@ -1233,8 -1233,8 +1233,8 @@@ invalidate(
        if (m_invalid_window == false) {
                m_system->addDirtyWindow(this);
                m_invalid_window = true;
-       } 
-  
+       }
        return GHOST_kSuccess;
  }
  
   * called by the X11 system implementation when expose events
   * for the window have been pushed onto the GHOST queue
   */
-  
  void
  GHOST_WindowX11::
  validate()
  {
        m_invalid_window = false;
- }     
-  
-  
+ }
  /**
   * Destructor.
   * Closes the window and disposes resources allocated.
@@@ -1289,7 -1289,7 +1289,7 @@@ GHOST_WindowX11:
                        XSetSelectionOwner(m_display, Clipboard_atom, None, CurrentTime);
                }
        }
-       
        if (m_visualInfo) {
                XFree(m_visualInfo);
        }
  GHOST_Context *GHOST_WindowX11::newDrawingContext(GHOST_TDrawingContextType type)
  {
        if (type == GHOST_kDrawingContextTypeOpenGL) {
 -#if !defined(WITH_GL_EGL)
 +
 +              // During development:
 +              //   try 4.x compatibility profile
 +              //   try 3.3 compatibility profile
 +              //   fall back to 3.0 if needed
 +              //
 +              // Final Blender 2.8:
 +              //   try 4.x core profile
 +              //   try 3.3 core profile
 +              //   no fallbacks
  
  #if defined(WITH_GL_PROFILE_CORE)
 -              GHOST_Context *context = new GHOST_ContextGLX(
 -                      m_wantStereoVisual,
 -                      m_wantNumOfAASamples,
 -                      m_window,
 -                      m_display,
 -                      m_visualInfo,
 -                      (GLXFBConfig)m_fbconfig,
 -                      GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
 -                      3, 2,
 -                      GHOST_OPENGL_GLX_CONTEXT_FLAGS | (m_is_debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
 -                      GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY);
 -#elif defined(WITH_GL_PROFILE_ES20)
 -              GHOST_Context *context = new GHOST_ContextGLX(
 -                      m_wantStereoVisual,
 -                      m_wantNumOfAASamples,
 -                      m_window,
 -                      m_display,
 -                      m_visualInfo,
 -                      (GLXFBConfig)m_fbconfig,
 -                      GLX_CONTEXT_ES2_PROFILE_BIT_EXT,
 -                      2, 0,
 -                      GHOST_OPENGL_GLX_CONTEXT_FLAGS | (m_is_debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
 -                      GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY);
 +              {
 +                      const char *version_major = (char*)glewGetString(GLEW_VERSION_MAJOR);
 +                      if (version_major != NULL && version_major[0] == '1') {
 +                              fprintf(stderr, "Error: GLEW version 2.0 and above is required.\n");
 +                              abort();
 +                      }
 +              }
 +#endif
 +
 +              const int profile_mask =
 +#if defined(WITH_GL_PROFILE_CORE)
 +                      GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
  #elif defined(WITH_GL_PROFILE_COMPAT)
 -              GHOST_Context *context = new GHOST_ContextGLX(
 -                      m_wantStereoVisual,
 -                      m_wantNumOfAASamples,
 -                      m_window,
 -                      m_display,
 -                      m_visualInfo,
 -                      (GLXFBConfig)m_fbconfig,
 -                      0, // profile bit
 -                      0, 0,
 -                      GHOST_OPENGL_GLX_CONTEXT_FLAGS | (m_is_debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
 -                      GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY);
 +                      GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
  #else
 -#  error
 +#  error // must specify either core or compat at build time
  #endif
  
 -#else
 +              GHOST_Context *context;
 +
 +              for (int minor = 5; minor >= 0; --minor) {
 +                      context = new GHOST_ContextGLX(
 +                              m_wantStereoVisual,
 +                              m_wantNumOfAASamples,
 +                              m_window,
 +                              m_display,
 +                              (GLXFBConfig)m_fbconfig,
 +                              profile_mask,
 +                              4, minor,
 +                              GHOST_OPENGL_GLX_CONTEXT_FLAGS | (m_is_debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
 +                              GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY);
 +
 +                      if (context->initializeDrawingContext())
 +                              return context;
 +                      else
 +                              delete context;
 +              }
  
 -#if defined(WITH_GL_PROFILE_CORE)
 -              GHOST_Context *context = new GHOST_ContextEGL(
 -                      m_wantStereoVisual,
 -                      m_wantNumOfAASamples,
 -                      m_window,
 -                      m_display,
 -                      EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
 -                      3, 2,
 -                      GHOST_OPENGL_EGL_CONTEXT_FLAGS,
 -                      GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
 -                      EGL_OPENGL_API);
 -#elif defined(WITH_GL_PROFILE_ES20)
 -              GHOST_Context *context = new GHOST_ContextEGL(
 +              context = new GHOST_ContextGLX(
                        m_wantStereoVisual,
                        m_wantNumOfAASamples,
                        m_window,
                        m_display,
 -                      0, // profile bit
 -                      2, 0,
 -                      GHOST_OPENGL_EGL_CONTEXT_FLAGS,
 -                      GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
 -                      EGL_OPENGL_ES_API);
 -#elif defined(WITH_GL_PROFILE_COMPAT)
 -              GHOST_Context *context = new GHOST_ContextEGL(
 -                      m_wantStereoVisual,
 -                      m_wantNumOfAASamples,
 -                      m_window,
 -                      m_display,
 -                      0, // profile bit
 -                      0, 0,
 -                      GHOST_OPENGL_EGL_CONTEXT_FLAGS,
 -                      GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
 -                      EGL_OPENGL_API);
 -#else
 -#  error
 -#endif
 +                      (GLXFBConfig)m_fbconfig,
 +                      profile_mask,
 +                      3, 3,
 +                      GHOST_OPENGL_GLX_CONTEXT_FLAGS | (m_is_debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
 +                      GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY);
  
 -#endif
                if (context->initializeDrawingContext())
                        return context;
                else
                        delete context;
 +
        }
  
        return NULL;
@@@ -1425,13 -1445,13 +1425,13 @@@ getStandardCursor
  
        if (xcursor_id) {
                Cursor xcursor = m_standard_cursors[xcursor_id];
-               
                if (!xcursor) {
                        xcursor = XCreateFontCursor(m_display, xcursor_id);
  
                        m_standard_cursors[xcursor_id] = xcursor;
                }
-               
                return xcursor;
        }
        else {
@@@ -1447,7 -1467,7 +1447,7 @@@ getEmptyCursor
                Pixmap blank;
                XColor dummy = {0};
                char data[1] = {0};
-                       
                /* make a blank cursor */
                blank = XCreateBitmapFromData(
                    m_display,
@@@ -1468,7 -1488,7 +1468,7 @@@ setWindowCursorVisibility
                bool visible)
  {
        Cursor xcursor;
-       
        if (visible) {
                if (m_visible_cursor)
                        xcursor = m_visible_cursor;
  
        XDefineCursor(m_display, m_window, xcursor);
        XFlush(m_display);
-       
        return GHOST_kSuccess;
  }
  
@@@ -1544,7 -1564,7 +1544,7 @@@ setWindowCursorGrab
        }
  
        XFlush(m_display);
-       
        return GHOST_kSuccess;
  }
  
@@@ -1556,7 -1576,7 +1556,7 @@@ setWindowCursorShape
        Cursor xcursor = getStandardCursor(shape);
  
        m_visible_cursor = xcursor;
-       
        XDefineCursor(m_display, m_window, xcursor);
        XFlush(m_display);
  
@@@ -1578,7 -1598,7 +1578,7 @@@ setWindowCustomCursorShape
  
  GHOST_TSuccess
  GHOST_WindowX11::
- setWindowCustomCursorShape(   
+ setWindowCustomCursorShape(
                GHOST_TUns8 *bitmap,
                GHOST_TUns8 *mask,
                int sizex,
        Colormap colormap = DefaultColormap(m_display, m_visualInfo->screen);
        Pixmap bitmap_pix, mask_pix;
        XColor fg, bg;
-       
        if (XAllocNamedColor(m_display, colormap, "White", &fg, &fg) == 0) return GHOST_kFailure;
        if (XAllocNamedColor(m_display, colormap, "Black", &bg, &bg) == 0) return GHOST_kFailure;
  
  
        bitmap_pix = XCreateBitmapFromData(m_display, m_window, (char *) bitmap, sizex, sizey);
        mask_pix = XCreateBitmapFromData(m_display, m_window, (char *) mask, sizex, sizey);
-               
        m_custom_cursor = XCreatePixmapCursor(m_display, bitmap_pix, mask_pix, &fg, &bg, hotX, hotY);
        XDefineCursor(m_display, m_window, m_custom_cursor);
        XFlush(m_display);
  
        m_visible_cursor = m_custom_cursor;
-       
        XFreePixmap(m_display, bitmap_pix);
        XFreePixmap(m_display, mask_pix);
  
@@@ -103,7 -103,7 +103,7 @@@ suffix_relpaths(SRC_NEW "${SRC}" "../..
  include_directories(${INC_NEW})
  add_library(guardedalloc_lib ${SRC_NEW})
  
- # blenfont 
+ # blenfont
  include(${CMAKE_SOURCE_DIR}/../../../source/blender/blenfont/CMakeLists.txt)
  suffix_relpaths(INC_NEW "${INC}" "../../../source/blender/blenfont/")
  suffix_relpaths(SRC_NEW "${SRC}" "../../../source/blender/blenfont/")
@@@ -128,7 -128,7 +128,7 @@@ add_library(glewmx_lib ${SRC_NEW}
  include_directories(
                "../../../source/blender/blenlib"
                )
- add_library(bli_lib 
+ add_library(bli_lib
                "../../../source/blender/blenlib/intern/fileops.c"
                "../../../source/blender/blenlib/intern/gsqueue.c"
                "../../../source/blender/blenlib/intern/rct.c"
@@@ -203,6 -203,7 +203,6 @@@ target_link_libraries(gears_
                glewmx_lib
                string_lib
                ${OPENGL_gl_LIBRARY}
 -              ${OPENGL_glu_LIBRARY}
                ${CMAKE_DL_LIBS}
                ${PLATFORM_LINKLIBS}
                )
@@@ -217,6 -218,7 +217,6 @@@ target_link_libraries(gears_cp
                glewmx_lib
                string_lib
                ${OPENGL_gl_LIBRARY}
 -              ${OPENGL_glu_LIBRARY}
                ${CMAKE_DL_LIBS}
                ${PLATFORM_LINKLIBS}
                )
@@@ -248,6 -250,7 +248,6 @@@ target_link_libraries(multitest_
                guardedalloc_lib
                wcwidth_lib
                ${OPENGL_gl_LIBRARY}
 -              ${OPENGL_glu_LIBRARY}
                ${FREETYPE_LIBRARY}
                ${ZLIB_LIBRARIES}
                ${CMAKE_DL_LIBS}
index 69f7af2,0000000..cec839a
mode 100644,000000..100644
--- /dev/null
@@@ -1,188 -1,0 +1,188 @@@
- # --env-system-scripts allows to run without the install target. 
 +# ***** BEGIN GPL LICENSE BLOCK *****
 +#
 +# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 +#
 +# Contributor(s): Jacques Beaurain, Dalai Felinto.
 +#
 +# ***** END GPL LICENSE BLOCK *****
 +
- # for testing with valgrind prefix: valgrind --track-origins=yes --error-limit=no 
++# --env-system-scripts allows to run without the install target.
 +
 +# Use '--write-blend=/tmp/test.blend' to view output
 +
 +set(TEST_SRC_DIR ${CMAKE_SOURCE_DIR}/../lib/tests)
 +set(TEST_OUT_DIR ${CMAKE_BINARY_DIR}/tests)
 +
 +# ugh, any better way to do this on testing only?
 +execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${TEST_OUT_DIR})
 +
 +#~    if(NOT IS_DIRECTORY ${TEST_SRC_DIR})
 +#~            message(FATAL_ERROR "CMake test directory not found!")
 +#~    endif()
 +
 +# all calls to blender use this
 +if(APPLE)
 +      if(${CMAKE_GENERATOR} MATCHES "Xcode")
 +              set(TEST_BLENDER_EXE ${EXECUTABLE_OUTPUT_PATH}/Debug/blender.app/Contents/MacOS/blender)
 +      else()
 +              set(TEST_BLENDER_EXE ${EXECUTABLE_OUTPUT_PATH}/blender.app/Contents/MacOS/blender)
 +      endif()
 +else()
 +      set(TEST_BLENDER_EXE ${EXECUTABLE_OUTPUT_PATH}/blender)
 +endif()
 +
++# for testing with valgrind prefix: valgrind --track-origins=yes --error-limit=no
 +set(TEST_BLENDER_EXE ${TEST_BLENDER_EXE} --background -noaudio --factory-startup --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts)
 +
 +
 +# ------------------------------------------------------------------------------
 +
 +macro(VIEW_LAYER_TEST test_name)
 +      # Adds ${CMAKE_CURRENT_LIST_DIR} to sys.path so that the tests can import
 +      # things from view_layer_common.py
 +      add_test(view_layer_${test_name} ${TEST_BLENDER_EXE}
 +              --python-expr "import sys; sys.path.append('${CMAKE_CURRENT_LIST_DIR}')"
 +              --python-exit-code 1
 +              --python ${CMAKE_CURRENT_LIST_DIR}/test_${test_name}.py --
 +              --testdir="${TEST_SRC_DIR}/layers"
 +      )
 +endmacro()
 +
 +VIEW_LAYER_TEST(active_collection)
 +VIEW_LAYER_TEST(background_set)
 +VIEW_LAYER_TEST(collection_new_sync)
 +VIEW_LAYER_TEST(collection_rename_a)
 +VIEW_LAYER_TEST(collection_rename_b)
 +#VIEW_LAYER_TEST(evaluation_render_settings_a)
 +#VIEW_LAYER_TEST(evaluation_render_settings_b)
 +#VIEW_LAYER_TEST(evaluation_render_settings_c)
 +#VIEW_LAYER_TEST(evaluation_render_settings_d)
 +#VIEW_LAYER_TEST(evaluation_render_settings_e)
 +#VIEW_LAYER_TEST(evaluation_render_settings_f)
 +#VIEW_LAYER_TEST(evaluation_render_settings_g)
 +#VIEW_LAYER_TEST(evaluation_render_settings_h)
 +#VIEW_LAYER_TEST(evaluation_render_settings_i)
 +VIEW_LAYER_TEST(evaluation_visibility_a)
 +VIEW_LAYER_TEST(evaluation_visibility_b)
 +VIEW_LAYER_TEST(evaluation_visibility_c)
 +VIEW_LAYER_TEST(evaluation_visibility_d)
 +VIEW_LAYER_TEST(evaluation_visibility_e)
 +VIEW_LAYER_TEST(evaluation_visibility_f)
 +VIEW_LAYER_TEST(evaluation_visibility_g)
 +VIEW_LAYER_TEST(evaluation_visibility_h)
 +VIEW_LAYER_TEST(evaluation_visibility_i)
 +VIEW_LAYER_TEST(evaluation_visibility_j)
 +VIEW_LAYER_TEST(evaluation_selectability_a)
 +VIEW_LAYER_TEST(evaluation_selectability_b)
 +VIEW_LAYER_TEST(evaluation_selectability_c)
 +VIEW_LAYER_TEST(evaluation_selectability_d)
 +VIEW_LAYER_TEST(evaluation_selectability_e)
 +VIEW_LAYER_TEST(evaluation_selectability_f)
 +VIEW_LAYER_TEST(group_a)
 +VIEW_LAYER_TEST(group_b)
 +VIEW_LAYER_TEST(group_c)
 +VIEW_LAYER_TEST(group_d)
 +VIEW_LAYER_TEST(group_e)
 +VIEW_LAYER_TEST(object_add_cylinder)
 +VIEW_LAYER_TEST(object_add_empty)
 +VIEW_LAYER_TEST(object_add_torus)
 +VIEW_LAYER_TEST(object_add_no_collection_cylinder)
 +VIEW_LAYER_TEST(object_add_no_collection_empty)
 +VIEW_LAYER_TEST(object_add_no_collection_torus)
 +VIEW_LAYER_TEST(object_copy)
 +VIEW_LAYER_TEST(object_delete_a)
 +VIEW_LAYER_TEST(object_delete_b)
 +VIEW_LAYER_TEST(object_link_a)
 +VIEW_LAYER_TEST(object_link_b)
 +VIEW_LAYER_TEST(object_link_c)
 +VIEW_LAYER_TEST(operator_context)
 +VIEW_LAYER_TEST(make_single_user)
 +VIEW_LAYER_TEST(move_above_below_scene_collection_a)
 +VIEW_LAYER_TEST(move_above_below_scene_collection_b)
 +VIEW_LAYER_TEST(move_above_below_scene_collection_c)
 +VIEW_LAYER_TEST(move_above_below_scene_collection_d)
 +VIEW_LAYER_TEST(move_above_below_scene_collection_e)
 +VIEW_LAYER_TEST(move_above_below_scene_collection_f)
 +VIEW_LAYER_TEST(move_above_below_scene_collection_g)
 +VIEW_LAYER_TEST(move_above_below_scene_collection_h)
 +VIEW_LAYER_TEST(move_above_below_scene_collection_i)
 +VIEW_LAYER_TEST(move_above_below_scene_collection_sync_a)
 +VIEW_LAYER_TEST(move_above_below_scene_collection_sync_b)
 +VIEW_LAYER_TEST(move_above_below_scene_collection_sync_c)
 +VIEW_LAYER_TEST(move_above_below_scene_collection_sync_d)
 +VIEW_LAYER_TEST(move_above_below_scene_collection_sync_e)
 +VIEW_LAYER_TEST(move_above_below_scene_collection_sync_f)
 +VIEW_LAYER_TEST(move_above_below_scene_collection_sync_g)
 +VIEW_LAYER_TEST(move_above_below_scene_collection_sync_h)
 +VIEW_LAYER_TEST(move_above_below_scene_collection_sync_i)
 +VIEW_LAYER_TEST(move_into_scene_collection_a)
 +VIEW_LAYER_TEST(move_into_scene_collection_b)
 +VIEW_LAYER_TEST(move_into_scene_collection_c)
 +VIEW_LAYER_TEST(move_into_scene_collection_d)
 +VIEW_LAYER_TEST(move_into_scene_collection_e)
 +VIEW_LAYER_TEST(move_into_scene_collection_f)
 +VIEW_LAYER_TEST(move_into_scene_collection_g)
 +VIEW_LAYER_TEST(move_into_scene_collection_h)
 +VIEW_LAYER_TEST(move_into_scene_collection_i)
 +VIEW_LAYER_TEST(move_into_scene_collection_j)
 +VIEW_LAYER_TEST(move_into_scene_collection_k)
 +VIEW_LAYER_TEST(move_into_scene_collection_l)
 +VIEW_LAYER_TEST(move_into_scene_collection_sync_a)
 +VIEW_LAYER_TEST(move_into_scene_collection_sync_b)
 +VIEW_LAYER_TEST(move_into_scene_collection_sync_c)
 +VIEW_LAYER_TEST(move_into_scene_collection_sync_d)
 +VIEW_LAYER_TEST(move_into_scene_collection_sync_e)
 +VIEW_LAYER_TEST(move_into_scene_collection_sync_f)
 +VIEW_LAYER_TEST(move_into_scene_collection_sync_g)
 +VIEW_LAYER_TEST(move_into_scene_collection_sync_h)
 +VIEW_LAYER_TEST(move_into_scene_collection_sync_i)
 +VIEW_LAYER_TEST(move_into_scene_collection_sync_j)
 +VIEW_LAYER_TEST(move_into_scene_collection_sync_k)
 +VIEW_LAYER_TEST(move_into_scene_collection_sync_l)
 +VIEW_LAYER_TEST(move_above_below_layer_collection_a)
 +VIEW_LAYER_TEST(move_above_below_layer_collection_b)
 +VIEW_LAYER_TEST(move_above_below_layer_collection_c)
 +VIEW_LAYER_TEST(move_above_below_layer_collection_d)
 +VIEW_LAYER_TEST(move_above_below_layer_collection_e)
 +VIEW_LAYER_TEST(move_above_below_layer_collection_f)
 +VIEW_LAYER_TEST(move_above_below_layer_collection_g)
 +VIEW_LAYER_TEST(move_above_below_layer_collection_h)
 +VIEW_LAYER_TEST(move_above_below_layer_collection_i)
 +VIEW_LAYER_TEST(move_above_below_layer_collection_j)
 +VIEW_LAYER_TEST(move_above_below_layer_collection_k)
 +VIEW_LAYER_TEST(move_above_below_layer_collection_l)
 +VIEW_LAYER_TEST(move_into_layer_collection_a)
 +VIEW_LAYER_TEST(move_into_layer_collection_b)
 +VIEW_LAYER_TEST(move_into_layer_collection_c)
 +VIEW_LAYER_TEST(move_into_layer_collection_d)
 +VIEW_LAYER_TEST(move_into_layer_collection_e)
 +VIEW_LAYER_TEST(move_into_layer_collection_f)
 +VIEW_LAYER_TEST(move_into_layer_collection_g)
 +VIEW_LAYER_TEST(move_into_layer_collection_h)
 +VIEW_LAYER_TEST(move_into_layer_collection_i)
 +VIEW_LAYER_TEST(move_into_layer_collection_j)
 +VIEW_LAYER_TEST(layer_linking)
 +VIEW_LAYER_TEST(layer_syncing)
 +VIEW_LAYER_TEST(scene_collection_delete)
 +VIEW_LAYER_TEST(scene_copy_a)
 +VIEW_LAYER_TEST(scene_copy_b)
 +VIEW_LAYER_TEST(scene_copy_c)
 +VIEW_LAYER_TEST(scene_copy_d)
 +VIEW_LAYER_TEST(scene_copy_e)
 +VIEW_LAYER_TEST(scene_copy_f)
 +VIEW_LAYER_TEST(scene_delete)
 +VIEW_LAYER_TEST(scene_objects)
 +VIEW_LAYER_TEST(scene_write_read)
 +VIEW_LAYER_TEST(view_layer_rename)