diff options
Diffstat (limited to 'intern/ghost/intern')
71 files changed, 6385 insertions, 2118 deletions
diff --git a/intern/ghost/intern/GHOST_Buttons.h b/intern/ghost/intern/GHOST_Buttons.h index 6b0d74e7eb9..0aa93a2fad0 100644 --- a/intern/ghost/intern/GHOST_Buttons.h +++ b/intern/ghost/intern/GHOST_Buttons.h @@ -48,26 +48,26 @@ struct GHOST_Buttons { */ GHOST_Buttons(); - virtual ~GHOST_Buttons(); + ~GHOST_Buttons(); /** * Returns the state of a single button. * \param mask Key button to return. * \return The state of the button (pressed == true). */ - virtual bool get(GHOST_TButtonMask mask) const; + bool get(GHOST_TButtonMask mask) const; /** * Updates the state of a single button. * \param mask Button state to update. * \param down The new state of the button. */ - virtual void set(GHOST_TButtonMask mask, bool down); + void set(GHOST_TButtonMask mask, bool down); /** * Sets the state of all buttons to up. */ - virtual void clear(); + void clear(); GHOST_TUns8 m_ButtonLeft : 1; GHOST_TUns8 m_ButtonMiddle : 1; diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp index eaa59f096aa..af992bf5a3c 100644 --- a/intern/ghost/intern/GHOST_C-api.cpp +++ b/intern/ghost/intern/GHOST_C-api.cpp @@ -140,20 +140,12 @@ GHOST_WindowHandle GHOST_CreateWindow(GHOST_SystemHandle systemhandle, GHOST_TUns32 height, GHOST_TWindowState state, GHOST_TDrawingContextType type, - const int stereoVisual, - const GHOST_TUns16 numOfAASamples) + GHOST_GLSettings glSettings) { GHOST_ISystem *system = (GHOST_ISystem *) systemhandle; - bool bstereoVisual; - - if (stereoVisual) - bstereoVisual = true; - else - bstereoVisual = false; return (GHOST_WindowHandle) system->createWindow(title, left, top, width, height, - state, type, bstereoVisual, false, - numOfAASamples); + state, type, glSettings, false); } GHOST_TUserDataPtr GHOST_GetWindowUserData(GHOST_WindowHandle windowhandle) @@ -421,6 +413,13 @@ GHOST_TSuccess GHOST_GetButtonState(GHOST_SystemHandle systemhandle, } +void GHOST_setNDOFDeadZone(float deadzone) +{ + GHOST_ISystem *system = GHOST_ISystem::getSystem(); + system->setNDOFDeadZone(deadzone); +} + + void GHOST_setAcceptDragOperation(GHOST_WindowHandle windowhandle, GHOST_TInt8 canAccept) { GHOST_IWindow *window = (GHOST_IWindow *) windowhandle; @@ -698,14 +697,21 @@ GHOST_TSuccess GHOST_SetSwapInterval(GHOST_WindowHandle windowhandle, int interv return window->setSwapInterval(interval); } -int GHOST_GetSwapInterval(GHOST_WindowHandle windowhandle) +GHOST_TSuccess GHOST_GetSwapInterval(GHOST_WindowHandle windowhandle, int* intervalOut) { GHOST_IWindow *window = (GHOST_IWindow *) windowhandle; - return window->getSwapInterval(); + return window->getSwapInterval(*intervalOut); } +GHOST_TUns16 GHOST_GetNumOfAASamples(GHOST_WindowHandle windowhandle) +{ + GHOST_IWindow *window = (GHOST_IWindow *) windowhandle; + + return window->getNumOfAASamples(); +} + GHOST_TSuccess GHOST_ActivateWindowDrawingContext(GHOST_WindowHandle windowhandle) { GHOST_IWindow *window = (GHOST_IWindow *) windowhandle; @@ -914,3 +920,21 @@ float GHOST_GetNativePixelSize(GHOST_WindowHandle windowhandle) return 1.0f; } +#ifdef WITH_INPUT_IME + +void GHOST_BeginIME(GHOST_WindowHandle windowhandle, + GHOST_TInt32 x, GHOST_TInt32 y, + GHOST_TInt32 w, GHOST_TInt32 h, + int complete) +{ + GHOST_IWindow *window = (GHOST_IWindow *) windowhandle; + window->beginIME(x, y, w, h, complete); +} + +void GHOST_EndIME(GHOST_WindowHandle windowhandle) +{ + GHOST_IWindow *window = (GHOST_IWindow *) windowhandle; + window->endIME(); +} + +#endif /* WITH_INPUT_IME */ diff --git a/intern/ghost/intern/GHOST_CallbackEventConsumer.h b/intern/ghost/intern/GHOST_CallbackEventConsumer.h index 770ebdc8b78..3505d651fa3 100644 --- a/intern/ghost/intern/GHOST_CallbackEventConsumer.h +++ b/intern/ghost/intern/GHOST_CallbackEventConsumer.h @@ -57,7 +57,7 @@ public: /** * Destructor. */ - virtual ~GHOST_CallbackEventConsumer(void) + ~GHOST_CallbackEventConsumer(void) { } @@ -66,7 +66,7 @@ public: * \param event The event that can be handled or ignored. * \return Indication as to whether the event was handled. */ - virtual bool processEvent(GHOST_IEvent *event); + bool processEvent(GHOST_IEvent *event); protected: /** The call-back routine invoked. */ diff --git a/intern/ghost/intern/GHOST_Context.cpp b/intern/ghost/intern/GHOST_Context.cpp new file mode 100644 index 00000000000..f69f2181ef7 --- /dev/null +++ b/intern/ghost/intern/GHOST_Context.cpp @@ -0,0 +1,159 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2013 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Jason Wilkins + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file ghost/intern/GHOST_Context.cpp + * \ingroup GHOST + * + * Definition of GHOST_Context class. + */ + +#include "GHOST_Context.h" + +#ifdef _WIN32 +# include <GL/wglew.h> // only for symbolic constants, do not use API functions +# include <tchar.h> +# +# ifndef ERROR_PROFILE_DOES_NOT_MATCH_DEVICE +# define ERROR_PROFILE_DOES_NOT_MATCH_DEVICE 0x7E7 // Mingw64 headers may have had this +# endif +#endif + +#include <cstdio> +#include <cstring> + + +#ifdef _WIN32 + +bool win32_chk(bool result, const char *file, int line, const char *text) +{ + if (!result) { + LPTSTR formattedMsg = NULL; + + DWORD error = GetLastError(); + + const char *msg; + + DWORD count = 0; + + switch (error) { + case ERROR_INVALID_VERSION_ARB: + msg = "The specified OpenGL version and feature set are either invalid or not supported.\n"; + break; + + case ERROR_INVALID_PROFILE_ARB: + msg = "The specified OpenGL profile and feature set are either invalid or not supported.\n"; + break; + + case ERROR_INVALID_PIXEL_TYPE_ARB: + msg = "The specified pixel type is invalid.\n"; + break; + + case ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB: + msg = ("The device contexts specified are not compatible. " + "This can occur if the device contexts are managed by " + "different drivers or possibly on different graphics adapters.\n"); + break; + +#ifdef WITH_GLEW_ES + case ERROR_INCOMPATIBLE_AFFINITY_MASKS_NV: + msg = "The device context(s) and rendering context have non-matching affinity masks.\n"; + break; + + case ERROR_MISSING_AFFINITY_MASK_NV: + msg = "The rendering context does not have an affinity mask set.\n"; + break; +#endif + + case ERROR_PROFILE_DOES_NOT_MATCH_DEVICE: + msg = ("The specified profile is intended for a device of a " + "different type than the specified device.\n"); + break; + + default: + { + count = + FormatMessage( + (FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS), + NULL, + error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)(&formattedMsg), + 0, + NULL); + + msg = count > 0 ? formattedMsg : "<no system message>\n"; + break; + } + } + +#ifndef NDEBUG + _ftprintf( + stderr, + "%s(%d):[%s] -> Win32 Error# (%lu): %s", + file, + line, + text, + (unsigned long)error, + msg); +#else + _ftprintf( + stderr, + "Win32 Error# (%lu): %s", + (unsigned long)error, + msg); +#endif + + SetLastError(NO_ERROR); + + if (count != 0) + LocalFree(formattedMsg); + } + + return result; +} + +#endif // _WIN32 + + +void GHOST_Context::initContextGLEW() +{ + mxDestroyContext(m_mxContext); // no-op if m_mxContext is NULL + + mxMakeCurrentContext(mxCreateContext()); + + m_mxContext = mxGetCurrentContext(); +} + + +void GHOST_Context::initClearGL() +{ + glClearColor(0.447, 0.447, 0.447, 0.000); + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(0.000, 0.000, 0.000, 0.000); +} diff --git a/intern/ghost/intern/GHOST_Context.h b/intern/ghost/intern/GHOST_Context.h new file mode 100644 index 00000000000..18d36c40e9c --- /dev/null +++ b/intern/ghost/intern/GHOST_Context.h @@ -0,0 +1,161 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2013 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Jason Wilkins + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file ghost/intern/GHOST_Context.h + * \ingroup GHOST + * Declaration of GHOST_Context class. + */ + +#ifndef __GHOST_CONTEXT_H__ +#define __GHOST_CONTEXT_H__ + +#include "GHOST_Types.h" + +#include "glew-mx.h" + +#include <cstdlib> // for NULL + + +class GHOST_Context +{ +public: + /** + * Constructor. + * \param stereoVisual Stereo visual for quad buffered stereo. + * \param numOfAASamples Number of samples used for AA (zero if no AA) + */ + GHOST_Context(bool stereoVisual, GHOST_TUns16 numOfAASamples) + : m_stereoVisual(stereoVisual), + m_numOfAASamples(numOfAASamples), + m_mxContext(NULL) + {} + + /** + * Destructor. + */ + virtual ~GHOST_Context() { + mxDestroyContext(m_mxContext); + } + + /** + * Swaps front and back buffers of a window. + * \return A boolean success indicator. + */ + virtual GHOST_TSuccess swapBuffers() = 0; + + /** + * Activates the drawing context of this window. + * \return A boolean success indicator. + */ + virtual GHOST_TSuccess activateDrawingContext() = 0; + + /** + * Call immediately after new to initialize. If this fails then immediately delete the object. + * \return Indication as to whether initialization has succeeded. + */ + virtual GHOST_TSuccess initializeDrawingContext() = 0; + + /** + * Updates the drawing context of this window. Needed + * whenever the window is changed. + * \return Indication of success. + */ + virtual GHOST_TSuccess updateDrawingContext() { + return GHOST_kFailure; + } + + /** + * Checks if it is OK for a remove the native display + * \return Indication as to whether removal has succeeded. + */ + virtual GHOST_TSuccess releaseNativeHandles() = 0; + + /** + * Sets the swap interval for swapBuffers. + * \param interval The swap interval to use. + * \return A boolean success indicator. + */ + virtual GHOST_TSuccess setSwapInterval(int /*interval*/) { + return GHOST_kFailure; + } + + /** + * Gets the current swap interval for swapBuffers. + * \param intervalOut Variable to store the swap interval if it can be read. + * \return Whether the swap interval can be read. + */ + virtual GHOST_TSuccess getSwapInterval(int&) { + return GHOST_kFailure; + } + + /** Stereo visual created. Only necessary for 'real' stereo support, + * ie quad buffered stereo. This is not always possible, depends on + * the graphics h/w + */ + inline bool isStereoVisual() const { + return m_stereoVisual; + } + + /** Number of samples used in anti-aliasing, set to 0 if no AA **/ + inline GHOST_TUns16 getNumOfAASamples() const { + return m_numOfAASamples; + } + +protected: + void initContextGLEW(); + + inline void activateGLEW() const { + mxMakeCurrentContext(m_mxContext); + } + + bool m_stereoVisual; + + GHOST_TUns16 m_numOfAASamples; + + static void initClearGL(); + +private: + MXContext *m_mxContext; + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_Context") +#endif +}; + + +#ifdef _WIN32 +bool win32_chk(bool result, const char *file = NULL, int line = 0, const char *text = NULL); + +# ifndef NDEBUG +# define WIN32_CHK(x) win32_chk((x), __FILE__, __LINE__, #x) +# else +# define WIN32_CHK(x) win32_chk(x) +# endif +#endif /* _WIN32 */ + + +#endif // __GHOST_CONTEXT_H__ diff --git a/intern/ghost/intern/GHOST_ContextCGL.h b/intern/ghost/intern/GHOST_ContextCGL.h new file mode 100644 index 00000000000..92fdbfc53de --- /dev/null +++ b/intern/ghost/intern/GHOST_ContextCGL.h @@ -0,0 +1,153 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2014 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Jason Wilkins + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file ghost/intern/GHOST_ContextCGL.h + * \ingroup GHOST + */ + +#ifndef __GHOST_CONTEXTCGL_H__ +#define __GHOST_CONTEXTCGL_H__ + +#include "GHOST_Context.h" + +//#define cglewGetContext() cglewContext +//#include <GL/cglew.h> +//extern "C" CGLEWContext *cglewContext; + +#ifndef GHOST_OPENGL_CGL_CONTEXT_FLAGS +#define GHOST_OPENGL_CGL_CONTEXT_FLAGS 0 +#endif + +#ifndef GHOST_OPENGL_CGL_RESET_NOTIFICATION_STRATEGY +#define GHOST_OPENGL_CGL_RESET_NOTIFICATION_STRATEGY 0 +#endif + + +@class NSWindow; +@class NSOpenGLView; +@class NSOpenGLContext; + + +class GHOST_ContextCGL : public GHOST_Context +{ +public: + /** + * Constructor. + */ + GHOST_ContextCGL( + bool stereoVisual, + GHOST_TUns16 numOfAASamples, + NSWindow *window, + NSOpenGLView *openGLView, + int contextProfileMask, + int contextMajorVersion, + int contextMinorVersion, + int contextFlags, + int contextResetNotificationStrategy); + + /** + * Destructor. + */ + ~GHOST_ContextCGL(); + + /** + * Swaps front and back buffers of a window. + * \return A boolean success indicator. + */ + GHOST_TSuccess swapBuffers(); + + /** + * Activates the drawing context of this window. + * \return A boolean success indicator. + */ + GHOST_TSuccess activateDrawingContext(); + + /** + * Call immediately after new to initialize. If this fails then immediately delete the object. + * \return Indication as to whether initialization has succeeded. + */ + GHOST_TSuccess initializeDrawingContext(); + + /** + * Removes references to native handles from this context and then returns + * \return GHOST_kSuccess if it is OK for the parent to release the handles and + * GHOST_kFailure if releasing the handles will interfere with sharing + */ + GHOST_TSuccess releaseNativeHandles(); + + /** + * Sets the swap interval for swapBuffers. + * \param interval The swap interval to use. + * \return A boolean success indicator. + */ + GHOST_TSuccess setSwapInterval(int interval); + + /** + * Gets the current swap interval for swapBuffers. + * \param intervalOut Variable to store the swap interval if it can be read. + * \return Whether the swap interval can be read. + */ + GHOST_TSuccess getSwapInterval(int&); + + /** + * Updates the drawing context of this window. + * Needed whenever the window is changed. + * \return Indication of success. + */ + GHOST_TSuccess updateDrawingContext(); + +//protected: +// inline void activateCGLEW() const { +// cglewContext = m_cglewContext; +// } + +private: + //void initContextCGLEW() + + /** The window containing the OpenGL view */ + NSWindow *m_window; + + /** The openGL view */ + NSOpenGLView *m_openGLView; + + const int m_contextProfileMask; + const int m_contextMajorVersion; + const int m_contextMinorVersion; + const int m_contextFlags; + const int m_contextResetNotificationStrategy; + + /** The OpenGL drawing context */ + NSOpenGLContext *m_openGLContext; + + //static CGLEWContext *s_cglewContext; + + /** The first created OpenGL context (for sharing display lists) */ + static NSOpenGLContext *s_sharedOpenGLContext; + static int s_sharedCount; +}; + +#endif // __GHOST_CONTEXTCGL_H__ diff --git a/intern/ghost/intern/GHOST_ContextCGL.mm b/intern/ghost/intern/GHOST_ContextCGL.mm new file mode 100644 index 00000000000..51895e5fa1a --- /dev/null +++ b/intern/ghost/intern/GHOST_ContextCGL.mm @@ -0,0 +1,354 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2013 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Jason Wilkins + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file ghost/intern/GHOST_ContextCGL.mm + * \ingroup GHOST + * + * Definition of GHOST_ContextCGL class. + */ + +#include "GHOST_ContextCGL.h" + +#include <Cocoa/Cocoa.h> + +#ifdef GHOST_MULTITHREADED_OPENGL +#include <OpenGL/OpenGL.h> +#endif + +#include <vector> +#include <cassert> + + +NSOpenGLContext *GHOST_ContextCGL::s_sharedOpenGLContext = nil; +int GHOST_ContextCGL::s_sharedCount = 0; + + +GHOST_ContextCGL::GHOST_ContextCGL( + bool stereoVisual, + GHOST_TUns16 numOfAASamples, + NSWindow *window, + NSOpenGLView *openGLView, + int contextProfileMask, + int contextMajorVersion, + int contextMinorVersion, + int contextFlags, + int contextResetNotificationStrategy) + : GHOST_Context(stereoVisual, numOfAASamples), + m_window(window), + m_openGLView(openGLView), + m_contextProfileMask(contextProfileMask), + m_contextMajorVersion(contextMajorVersion), + m_contextMinorVersion(contextMinorVersion), + m_contextFlags(contextFlags), + m_contextResetNotificationStrategy(contextResetNotificationStrategy), + m_openGLContext(nil) +{ + assert(window != nil); + assert(openGLView != nil); +} + + +GHOST_ContextCGL::~GHOST_ContextCGL() +{ + if (m_openGLContext != nil) { + if (m_openGLContext == [NSOpenGLContext currentContext]) { + [NSOpenGLContext clearCurrentContext]; + [m_openGLView clearGLContext]; + } + + if (m_openGLContext != s_sharedOpenGLContext || s_sharedCount == 1) { + assert(s_sharedCount > 0); + + s_sharedCount--; + + if (s_sharedCount == 0) + s_sharedOpenGLContext = nil; + + [m_openGLContext release]; + } + } +} + + +GHOST_TSuccess GHOST_ContextCGL::swapBuffers() +{ + if (m_openGLContext != nil) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [m_openGLContext flushBuffer]; + [pool drain]; + return GHOST_kSuccess; + } + else { + return GHOST_kFailure; + } +} + + +GHOST_TSuccess GHOST_ContextCGL::setSwapInterval(int interval) +{ + if (m_openGLContext != nil) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [m_openGLContext setValues:&interval forParameter:NSOpenGLCPSwapInterval]; + [pool drain]; + return GHOST_kSuccess; + } + else { + return GHOST_kFailure; + } +} + + +GHOST_TSuccess GHOST_ContextCGL::getSwapInterval(int &intervalOut) +{ + if (m_openGLContext != nil) { + GLint interval; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + [m_openGLContext setValues:&interval forParameter:NSOpenGLCPSwapInterval]; + + [pool drain]; + + intervalOut = static_cast<int>(interval); + + return GHOST_kSuccess; + } + else { + return GHOST_kFailure; + } +} + + +GHOST_TSuccess GHOST_ContextCGL::activateDrawingContext() +{ + if (m_openGLContext != nil) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [m_openGLContext makeCurrentContext]; + + activateGLEW(); + + [pool drain]; + return GHOST_kSuccess; + } + else { + return GHOST_kFailure; + } +} + + +GHOST_TSuccess GHOST_ContextCGL::updateDrawingContext() +{ + if (m_openGLContext != nil) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [m_openGLContext update]; + [pool drain]; + return GHOST_kSuccess; + } + else { + return GHOST_kFailure; + } +} + + +static void makeAttribList( + std::vector<NSOpenGLPixelFormatAttribute>& attribs, + bool stereoVisual, + int numOfAASamples, + bool needAlpha, + bool needStencil) +{ + // Pixel Format Attributes for the windowed NSOpenGLContext + attribs.push_back(NSOpenGLPFADoubleBuffer); + + // Guarantees the back buffer contents to be valid after a call to NSOpenGLContext object's flushBuffer + // needed for 'Draw Overlap' drawing method + attribs.push_back(NSOpenGLPFABackingStore); + + // Force software OpenGL, for debugging + /* XXX jwilkins: fixed this to work on Intel macs? useful feature for Windows and Linux too? + * Maybe a command line flag is better... */ + if (getenv("BLENDER_SOFTWAREGL")) { + attribs.push_back(NSOpenGLPFARendererID); + attribs.push_back(kCGLRendererGenericFloatID); + } + else { + attribs.push_back(NSOpenGLPFAAccelerated); + } + + /* Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway */ + //attribs.push_back(NSOpenGLPFAAllowOfflineRenderers); + + attribs.push_back(NSOpenGLPFADepthSize); + attribs.push_back((NSOpenGLPixelFormatAttribute) 32); + + attribs.push_back(NSOpenGLPFAAccumSize); + attribs.push_back((NSOpenGLPixelFormatAttribute) 32); + + if (stereoVisual) + attribs.push_back(NSOpenGLPFAStereo); + + if (needAlpha) { + attribs.push_back(NSOpenGLPFAAlphaSize); + attribs.push_back((NSOpenGLPixelFormatAttribute) 8); + } + + if (needStencil) { + attribs.push_back(NSOpenGLPFAStencilSize); + attribs.push_back((NSOpenGLPixelFormatAttribute) 8); + } + + if (numOfAASamples > 0) { + // Multisample anti-aliasing + attribs.push_back(NSOpenGLPFAMultisample); + + attribs.push_back(NSOpenGLPFASampleBuffers); + attribs.push_back((NSOpenGLPixelFormatAttribute) 1); + + attribs.push_back(NSOpenGLPFASamples); + attribs.push_back((NSOpenGLPixelFormatAttribute) numOfAASamples); + + attribs.push_back(NSOpenGLPFANoRecovery); + } + + attribs.push_back((NSOpenGLPixelFormatAttribute) 0); +} + + +GHOST_TSuccess GHOST_ContextCGL::initializeDrawingContext() +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + std::vector<NSOpenGLPixelFormatAttribute> attribs; + attribs.reserve(40); + + NSOpenGLContext *prev_openGLContext = [m_openGLView openGLContext]; + +#ifdef GHOST_OPENGL_ALPHA + static const bool needAlpha = true; +#else + static const bool needAlpha = false; +#endif + +#ifdef GHOST_OPENGL_STENCIL + static const bool needStencil = true; +#else + static const bool needStencil = false; +#endif + + makeAttribList(attribs, m_stereoVisual, m_numOfAASamples, needAlpha, needStencil); + + NSOpenGLPixelFormat *pixelFormat; + + pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:&attribs[0]]; + + // Fall back to no multisampling if Antialiasing init failed + if (m_numOfAASamples > 0 && pixelFormat == nil) { + // XXX jwilkins: Does CGL only succeed when it makes an exact match on the number of samples? + // Does this need to explicitly try for a lesser match before giving up? + // (Now that I think about it, does WGL really require the code that it has for finding a lesser match?) + + attribs.clear(); + makeAttribList(attribs, m_stereoVisual, 0, needAlpha, needStencil); + pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:&attribs[0]]; + } + + if (pixelFormat == nil) + goto error; + + if (m_numOfAASamples > 0) { //Set m_numOfAASamples to the actual value + GLint actualSamples; + [pixelFormat getValues:&actualSamples forAttribute:NSOpenGLPFASamples forVirtualScreen:0]; + + if (m_numOfAASamples != (GHOST_TUns16)actualSamples) { + fprintf(stderr, + "Warning! Unable to find a multisample pixel format that supports exactly %d samples. " + "Substituting one that uses %d samples.\n", + m_numOfAASamples, actualSamples); + + m_numOfAASamples = (GHOST_TUns16)actualSamples; + } + } + + [m_openGLView setPixelFormat:pixelFormat]; + + m_openGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:s_sharedOpenGLContext]; // +1 refCount to pixelFormat + + if (m_openGLContext == nil) + goto error; + + if (s_sharedCount == 0) + s_sharedOpenGLContext = m_openGLContext; + + [pixelFormat release]; // -1 refCount to pixelFormat + + s_sharedCount++; + +#ifdef GHOST_MULTITHREADED_OPENGL + //Switch openGL to multhreaded mode + CGLContextObj cglCtx = (CGLContextObj)[tmpOpenGLContext CGLContextObj]; + if (CGLEnable(cglCtx, kCGLCEMPEngine) == kCGLNoError) + printf("\nSwitched openGL to multithreaded mode\n"); +#endif + +#ifdef GHOST_WAIT_FOR_VSYNC + { + GLint swapInt = 1; + /* wait for vsync, to avoid tearing artifacts */ + [m_openGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; + } +#endif + + [m_openGLView setOpenGLContext:m_openGLContext]; + [m_openGLContext setView:m_openGLView]; + + initContextGLEW(); + + initClearGL(); + [m_openGLContext flushBuffer]; + + [pool drain]; + + return GHOST_kSuccess; + +error: + + [m_openGLView setOpenGLContext:prev_openGLContext]; + [pixelFormat release]; + + [pool drain]; + + return GHOST_kFailure; +} + + +GHOST_TSuccess GHOST_ContextCGL::releaseNativeHandles() +{ + m_openGLContext = NULL; + m_openGLView = NULL; + + return GHOST_kSuccess; +} diff --git a/intern/ghost/intern/GHOST_ContextEGL.cpp b/intern/ghost/intern/GHOST_ContextEGL.cpp new file mode 100644 index 00000000000..520aa0fffb2 --- /dev/null +++ b/intern/ghost/intern/GHOST_ContextEGL.cpp @@ -0,0 +1,640 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2013 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Jason Wilkins + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file ghost/intern/GHOST_ContextEGL.cpp + * \ingroup GHOST + * + * Definition of GHOST_ContextEGL class. + */ + +#include "GHOST_ContextEGL.h" + +#include <set> +#include <sstream> +#include <vector> + +#include <cassert> +#include <cstdio> +#include <cstring> + + +#ifdef WITH_GLEW_MX +EGLEWContext *eglewContext = NULL; +#endif + + +#define CASE_CODE_RETURN_STR(code) case code: return #code; + +static const char *get_egl_error_enum_string(EGLenum error) +{ + switch (error) { + CASE_CODE_RETURN_STR(EGL_SUCCESS) + CASE_CODE_RETURN_STR(EGL_NOT_INITIALIZED) + CASE_CODE_RETURN_STR(EGL_BAD_ACCESS) + CASE_CODE_RETURN_STR(EGL_BAD_ALLOC) + CASE_CODE_RETURN_STR(EGL_BAD_ATTRIBUTE) + CASE_CODE_RETURN_STR(EGL_BAD_CONTEXT) + CASE_CODE_RETURN_STR(EGL_BAD_CONFIG) + CASE_CODE_RETURN_STR(EGL_BAD_CURRENT_SURFACE) + CASE_CODE_RETURN_STR(EGL_BAD_DISPLAY) + CASE_CODE_RETURN_STR(EGL_BAD_SURFACE) + CASE_CODE_RETURN_STR(EGL_BAD_MATCH) + CASE_CODE_RETURN_STR(EGL_BAD_PARAMETER) + CASE_CODE_RETURN_STR(EGL_BAD_NATIVE_PIXMAP) + CASE_CODE_RETURN_STR(EGL_BAD_NATIVE_WINDOW) + CASE_CODE_RETURN_STR(EGL_CONTEXT_LOST) + default: + return NULL; + } +} + +static const char *get_egl_error_message_string(EGLenum error) +{ + switch (error) { + case EGL_SUCCESS: + return "The last function succeeded without error."; + + case EGL_NOT_INITIALIZED: + return ("EGL is not initialized, or could not be initialized, " + "for the specified EGL display connection."); + + case EGL_BAD_ACCESS: + return ("EGL cannot access a requested resource " + "(for example a context is bound in another thread)."); + + case EGL_BAD_ALLOC: + return "EGL failed to allocate resources for the requested operation."; + + case EGL_BAD_ATTRIBUTE: + return "An unrecognized attribute or attribute value was passed in the attribute list."; + + case EGL_BAD_CONTEXT: + return "An EGLContext argument does not name a valid EGL rendering context."; + + case EGL_BAD_CONFIG: + return "An EGLConfig argument does not name a valid EGL frame buffer configuration."; + + case EGL_BAD_CURRENT_SURFACE: + return ("The current surface of the calling thread is a window, " + "pixel buffer or pixmap that is no longer valid."); + + case EGL_BAD_DISPLAY: + return "An EGLDisplay argument does not name a valid EGL display connection."; + + case EGL_BAD_SURFACE: + return ("An EGLSurface argument does not name a valid surface " + "(window, pixel buffer or pixmap) configured for GL rendering."); + + case EGL_BAD_MATCH: + return ("Arguments are inconsistent " + "(for example, a valid context requires buffers not supplied by a valid surface)."); + + case EGL_BAD_PARAMETER: + return "One or more argument values are invalid."; + + case EGL_BAD_NATIVE_PIXMAP: + return "A NativePixmapType argument does not refer to a valid native pixmap."; + + case EGL_BAD_NATIVE_WINDOW: + return "A NativeWindowType argument does not refer to a valid native window."; + + case EGL_CONTEXT_LOST: + return ("A power management event has occurred. " + "The application must destroy all contexts and reinitialise OpenGL ES state " + "and objects to continue rendering."); + + default: + return NULL; + } +} + + +static bool egl_chk(bool result, const char *file = NULL, int line = 0, const char *text = NULL) +{ + if (!result) { + EGLenum error = eglGetError(); + + const char *code = get_egl_error_enum_string(error); + const char *msg = get_egl_error_message_string(error); + +#ifndef NDEBUG + fprintf(stderr, + "%s(%d):[%s] -> EGL Error (0x%04X): %s: %s\n", + file, line, text, error, + code ? code : "<Unknown>", + msg ? msg : "<Unknown>"); +#else + fprintf(stderr, + "EGL Error (0x%04X): %s: %s\n", + error, + code ? code : "<Unknown>", + msg ? msg : "<Unknown>"); +#endif + } + + return result; +} + +#ifndef NDEBUG +#define EGL_CHK(x) egl_chk((x), __FILE__, __LINE__, #x) +#else +#define EGL_CHK(x) egl_chk(x) +#endif + + +static inline bool bindAPI(EGLenum api) +{ +#ifdef WITH_GLEW_MX + if (eglewContext != NULL) +#endif + { + if (EGLEW_VERSION_1_2) { + return (EGL_CHK(eglBindAPI(api)) == EGL_TRUE); + } + } + + return false; +} + + +#ifdef WITH_GL_ANGLE +HMODULE GHOST_ContextEGL::s_d3dcompiler = NULL; +#endif + + +EGLContext GHOST_ContextEGL::s_gl_sharedContext = EGL_NO_CONTEXT; +EGLint GHOST_ContextEGL::s_gl_sharedCount = 0; + +EGLContext GHOST_ContextEGL::s_gles_sharedContext = EGL_NO_CONTEXT; +EGLint GHOST_ContextEGL::s_gles_sharedCount = 0; + +EGLContext GHOST_ContextEGL::s_vg_sharedContext = EGL_NO_CONTEXT; +EGLint GHOST_ContextEGL::s_vg_sharedCount = 0; + + +#pragma warning(disable : 4715) + +template <typename T> +T &choose_api(EGLenum api, T &a, T &b, T &c) +{ + switch (api) { + case EGL_OPENGL_API: + return a; + case EGL_OPENGL_ES_API: + return b; + case EGL_OPENVG_API: + return c; + default: + abort(); + } +} + + +GHOST_ContextEGL::GHOST_ContextEGL( + bool stereoVisual, + GHOST_TUns16 numOfAASamples, + EGLNativeWindowType nativeWindow, + EGLNativeDisplayType nativeDisplay, + EGLint contextProfileMask, + EGLint contextMajorVersion, + EGLint contextMinorVersion, + EGLint contextFlags, + EGLint contextResetNotificationStrategy, + EGLenum api) + : GHOST_Context(stereoVisual, numOfAASamples), + m_nativeDisplay(nativeDisplay), + m_nativeWindow (nativeWindow), + m_contextProfileMask(contextProfileMask), + m_contextMajorVersion(contextMajorVersion), + m_contextMinorVersion(contextMinorVersion), + m_contextFlags(contextFlags), + m_contextResetNotificationStrategy(contextResetNotificationStrategy), + m_api(api), + m_context(EGL_NO_CONTEXT), + m_surface(EGL_NO_SURFACE), + m_display(EGL_NO_DISPLAY), + m_swap_interval(1), +#ifdef WITH_GLEW_MX + m_eglewContext(NULL), +#endif + m_sharedContext(choose_api(api, s_gl_sharedContext, s_gles_sharedContext, s_vg_sharedContext)), + m_sharedCount (choose_api(api, s_gl_sharedCount, s_gles_sharedCount, s_vg_sharedCount)) +{ + assert(m_nativeWindow != 0); + assert(m_nativeDisplay != NULL); +} + + +GHOST_ContextEGL::~GHOST_ContextEGL() +{ + if (m_display != EGL_NO_DISPLAY) { + activateEGLEW(); + + bindAPI(m_api); + + if (m_context != EGL_NO_CONTEXT) { + if (m_context == ::eglGetCurrentContext()) + EGL_CHK(::eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); + + if (m_context != m_sharedContext || m_sharedCount == 1) { + assert(m_sharedCount > 0); + + m_sharedCount--; + + if (m_sharedCount == 0) + m_sharedContext = EGL_NO_CONTEXT; + + EGL_CHK(::eglDestroyContext(m_display, m_context)); + } + } + + if (m_surface != EGL_NO_SURFACE) + EGL_CHK(::eglDestroySurface(m_display, m_surface)); + + EGL_CHK(::eglTerminate(m_display)); + +#ifdef WITH_GLEW_MX + delete m_eglewContext; +#endif + } +} + + +GHOST_TSuccess GHOST_ContextEGL::swapBuffers() +{ + return EGL_CHK(::eglSwapBuffers(m_display, m_surface)) ? GHOST_kSuccess : GHOST_kFailure; +} + + +GHOST_TSuccess GHOST_ContextEGL::setSwapInterval(int interval) +{ + if (EGLEW_VERSION_1_1) { + if (EGL_CHK(::eglSwapInterval(m_display, interval))) { + m_swap_interval = interval; + + return GHOST_kSuccess; + } + else { + return GHOST_kFailure; + } + } + else { + return GHOST_kFailure; + } +} + + +GHOST_TSuccess GHOST_ContextEGL::getSwapInterval(int &intervalOut) +{ + // This is a bit of a kludge because there does not seem to + // be a way to query the swap interval with EGL. + intervalOut = m_swap_interval; + + return GHOST_kSuccess; +} + + +GHOST_TSuccess GHOST_ContextEGL::activateDrawingContext() +{ + if (m_display) { + activateEGLEW(); + activateGLEW(); + + bindAPI(m_api); + + return EGL_CHK(::eglMakeCurrent(m_display, m_surface, m_surface, m_context)) ? GHOST_kSuccess : GHOST_kFailure; + } + else { + return GHOST_kFailure; + } +} + + +void GHOST_ContextEGL::initContextEGLEW() +{ +#ifdef WITH_GLEW_MX + eglewContext = new EGLEWContext; + memset(eglewContext, 0, sizeof(EGLEWContext)); + + delete m_eglewContext; + m_eglewContext = eglewContext; +#endif + + if (GLEW_CHK(eglewInit(m_display)) != GLEW_OK) + fprintf(stderr, "Warning! EGLEW failed to initialize properly.\n"); +} + + +static const std::string &api_string(EGLenum api) +{ + static const std::string a("OpenGL"); + static const std::string b("OpenGL ES"); + static const std::string c("OpenVG"); + + return choose_api(api, a, b, c); +} + +GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext() +{ + // objects have to be declared here due to the use of goto + std::vector<EGLint> attrib_list; + EGLint num_config = 0; + + if (m_stereoVisual) + fprintf(stderr, "Warning! Stereo OpenGL ES contexts are not supported.\n"); + + m_stereoVisual = false; // It doesn't matter what the Window wants. + +#ifdef WITH_GL_ANGLE + // d3dcompiler_XX.dll needs to be loaded before ANGLE will work + if (s_d3dcompiler == NULL) { + s_d3dcompiler = LoadLibrary(D3DCOMPILER); + + WIN32_CHK(s_d3dcompiler != NULL); + + if (s_d3dcompiler == NULL) { + fprintf(stderr, "LoadLibrary(\"" D3DCOMPILER "\") failed!\n"); + return GHOST_kFailure; + } + } +#endif + + EGLDisplay prev_display = eglGetCurrentDisplay(); + EGLSurface prev_draw = eglGetCurrentSurface(EGL_DRAW); + EGLSurface prev_read = eglGetCurrentSurface(EGL_READ); + EGLContext prev_context = eglGetCurrentContext(); + + m_display = ::eglGetDisplay(m_nativeDisplay); + + if (!EGL_CHK(m_display != EGL_NO_DISPLAY)) + return GHOST_kFailure; + + EGLint egl_major, egl_minor; + + if (!EGL_CHK(::eglInitialize(m_display, &egl_major, &egl_minor))) + goto error; + + fprintf(stderr, "EGL Version %d.%d\n", egl_major, egl_minor); + + if (!EGL_CHK(::eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))) + goto error; + + initContextEGLEW(); + + if (!bindAPI(m_api)) + goto error; + + + // build attribute list + + attrib_list.reserve(20); + + if (m_api == EGL_OPENGL_ES_API && EGLEW_VERSION_1_2) { + // According to the spec it seems that you are required to set EGL_RENDERABLE_TYPE, + // but some implementations (ANGLE) do not seem to care. + + if (m_contextMajorVersion == 1) { + attrib_list.push_back(EGL_RENDERABLE_TYPE); + attrib_list.push_back(EGL_OPENGL_ES_BIT); + } + else if (m_contextMajorVersion == 2) { + attrib_list.push_back(EGL_RENDERABLE_TYPE); + attrib_list.push_back(EGL_OPENGL_ES2_BIT); + } + else if (m_contextMajorVersion == 3) { + attrib_list.push_back(EGL_RENDERABLE_TYPE); + attrib_list.push_back(EGL_OPENGL_ES3_BIT_KHR); + } + else { + fprintf(stderr, + "Warning! Unable to request an ES context of version %d.%d\n", + m_contextMajorVersion, m_contextMinorVersion); + } + + if (!((m_contextMajorVersion == 1) || + (m_contextMajorVersion == 2 && EGLEW_VERSION_1_3) || + (m_contextMajorVersion == 3 && /*EGLEW_VERSION_1_4 &&*/ EGLEW_KHR_create_context) || + (m_contextMajorVersion == 3 && EGLEW_VERSION_1_5))) + { + fprintf(stderr, + "Warning! May not be able to create a version %d.%d ES context with version %d.%d of EGL\n", + m_contextMajorVersion, m_contextMinorVersion, egl_major, egl_minor); + } + } + + attrib_list.push_back(EGL_RED_SIZE); + attrib_list.push_back(8); + + attrib_list.push_back(EGL_GREEN_SIZE); + attrib_list.push_back(8); + + attrib_list.push_back(EGL_BLUE_SIZE); + attrib_list.push_back(8); + +#ifdef GHOST_OPENGL_ALPHA + attrib_list.push_back(EGL_ALPHA_SIZE); + attrib_list.push_back(8); +#endif + + attrib_list.push_back(EGL_DEPTH_SIZE); + attrib_list.push_back(24); + +#ifdef GHOST_OPENGL_STENCIL + attrib_list.push_back(EGL_STENCIL_SIZE); + attrib_list.push_back(8); +#endif + + if (m_numOfAASamples > 0) { + attrib_list.push_back(EGL_SAMPLE_BUFFERS); + attrib_list.push_back(1); + + attrib_list.push_back(EGL_SAMPLES); + attrib_list.push_back(m_numOfAASamples); + } + + attrib_list.push_back(EGL_NONE); + + EGLConfig config; + + if (!EGL_CHK(::eglChooseConfig(m_display, &(attrib_list[0]), &config, 1, &num_config))) + goto error; + + // A common error is to assume that ChooseConfig worked because it returned EGL_TRUE + if (num_config != 1) // num_config should be exactly 1 + goto error; + + if (m_numOfAASamples > 0) { + EGLint actualSamples; + + if (!EGL_CHK(::eglGetConfigAttrib(m_display, config, EGL_SAMPLE_BUFFERS, &actualSamples))) + goto error; + + if (m_numOfAASamples != actualSamples) { + fprintf(stderr, + "Warning! Unable to find a multisample pixel format that supports exactly %d samples. " + "Substituting one that uses %d samples.\n", + m_numOfAASamples, + actualSamples); + + m_numOfAASamples = (GHOST_TUns16)actualSamples; + } + } + + m_surface = ::eglCreateWindowSurface(m_display, config, m_nativeWindow, NULL); + + if (!EGL_CHK(m_surface != EGL_NO_SURFACE)) + goto error; + + attrib_list.clear(); + + if (EGLEW_VERSION_1_5 || EGLEW_KHR_create_context) { + if (m_api == EGL_OPENGL_API || m_api == EGL_OPENGL_ES_API) { + if (m_contextMajorVersion != 0) { + attrib_list.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR); + attrib_list.push_back(m_contextMajorVersion); + } + + if (m_contextMinorVersion != 0) { + attrib_list.push_back(EGL_CONTEXT_MINOR_VERSION_KHR); + attrib_list.push_back(m_contextMinorVersion); + } + + if (m_contextFlags != 0) { + attrib_list.push_back(EGL_CONTEXT_FLAGS_KHR); + attrib_list.push_back(m_contextFlags); + } + } + else { + if (m_contextMajorVersion != 0 || m_contextMinorVersion != 0) { + fprintf(stderr, + "Warning! Cannot request specific versions of %s contexts.", + api_string(m_api).c_str()); + } + + if (m_contextFlags != 0) { + fprintf(stderr, + "Warning! Flags cannot be set on %s contexts.", + api_string(m_api).c_str()); + } + } + + if (m_api == EGL_OPENGL_API) { + if (m_contextProfileMask != 0) { + attrib_list.push_back(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR); + attrib_list.push_back(m_contextProfileMask); + } + } + else { + if (m_contextProfileMask != 0) + fprintf(stderr, + "Warning! Cannot select profile for %s contexts.", + api_string(m_api).c_str()); + } + + if (m_api == EGL_OPENGL_API || EGLEW_VERSION_1_5) { + if (m_contextResetNotificationStrategy != 0) { + attrib_list.push_back(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR); + attrib_list.push_back(m_contextResetNotificationStrategy); + } + } + else { + if (m_contextResetNotificationStrategy != 0) { + fprintf(stderr, + "Warning! EGL %d.%d cannot set the reset notification strategy on %s contexts.", + egl_major, egl_minor, api_string(m_api).c_str()); + } + } + } + else { + if (m_api == EGL_OPENGL_ES_API) { + if (m_contextMajorVersion != 0) { + attrib_list.push_back(EGL_CONTEXT_CLIENT_VERSION); + attrib_list.push_back(m_contextMajorVersion); + } + } + else { + if (m_contextMajorVersion != 0 || m_contextMinorVersion != 0) { + fprintf(stderr, + "Warning! EGL %d.%d is unable to select between versions of %s.", + egl_major, egl_minor, api_string(m_api).c_str()); + } + } + + if (m_contextFlags != 0) { + fprintf(stderr, + "Warning! EGL %d.%d is unable to set context flags.", + egl_major, egl_minor); + } + if (m_contextProfileMask != 0) { + fprintf(stderr, + "Warning! EGL %d.%d is unable to select between profiles.", + egl_major, egl_minor); + } + if (m_contextResetNotificationStrategy != 0) { + fprintf(stderr, + "Warning! EGL %d.%d is unable to set the reset notification strategies.", + egl_major, egl_minor); + } + } + + attrib_list.push_back(EGL_NONE); + + m_context = ::eglCreateContext(m_display, config, m_sharedContext, &(attrib_list[0])); + + if (!EGL_CHK(m_context != EGL_NO_CONTEXT)) + goto error; + + if (m_sharedContext == EGL_NO_CONTEXT) + m_sharedContext = m_context; + + m_sharedCount++; + + if (!EGL_CHK(::eglMakeCurrent(m_display, m_surface, m_surface, m_context))) + goto error; + + initContextGLEW(); + + initClearGL(); + ::eglSwapBuffers(m_display, m_surface); + + return GHOST_kSuccess; + +error: + if (prev_display != EGL_NO_DISPLAY) + EGL_CHK(eglMakeCurrent(prev_display, prev_draw, prev_read, prev_context)); + + return GHOST_kFailure; +} + + +GHOST_TSuccess GHOST_ContextEGL::releaseNativeHandles() +{ + m_nativeWindow = 0; + m_nativeDisplay = NULL; + + return GHOST_kSuccess; +} diff --git a/intern/ghost/intern/GHOST_ContextEGL.h b/intern/ghost/intern/GHOST_ContextEGL.h new file mode 100644 index 00000000000..70c26c940fc --- /dev/null +++ b/intern/ghost/intern/GHOST_ContextEGL.h @@ -0,0 +1,167 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2013 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Jason Wilkins + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file ghost/intern/GHOST_ContextEGL.h + * \ingroup GHOST + */ + +#ifndef __GHOST_CONTEXTEGL_H__ +#define __GHOST_CONTEXTEGL_H__ + +#include "GHOST_Context.h" + +#ifdef WITH_GLEW_MX +# define eglewGetContext() eglewContext +#endif + +#include <GL/eglew.h> + +#ifdef WITH_GLEW_MX +extern "C" EGLEWContext *eglewContext; +#endif + + +#ifndef GHOST_OPENGL_EGL_CONTEXT_FLAGS +#define GHOST_OPENGL_EGL_CONTEXT_FLAGS 0 +#endif + +#ifndef GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY +#define GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY 0 +#endif + + +class GHOST_ContextEGL : public GHOST_Context +{ +public: + /** + * Constructor. + */ + GHOST_ContextEGL( + bool stereoVisual, + GHOST_TUns16 numOfAASamples, + EGLNativeWindowType nativeWindow, + EGLNativeDisplayType nativeDisplay, + EGLint contextProfileMask, + EGLint contextMajorVersion, + EGLint contextMinorVersion, + EGLint contextFlags, + EGLint contextResetNotificationStrategy, + EGLenum api); + + /** + * Destructor. + */ + ~GHOST_ContextEGL(); + + /** + * Swaps front and back buffers of a window. + * \return A boolean success indicator. + */ + GHOST_TSuccess swapBuffers(); + + /** + * Activates the drawing context of this window. + * \return A boolean success indicator. + */ + GHOST_TSuccess activateDrawingContext(); + + /** + * Call immediately after new to initialize. If this fails then immediately delete the object. + * \return Indication as to whether initialization has succeeded. + */ + GHOST_TSuccess initializeDrawingContext(); + + /** + * Removes references to native handles from this context and then returns + * \return GHOST_kSuccess if it is OK for the parent to release the handles and + * GHOST_kFailure if releasing the handles will interfere with sharing + */ + GHOST_TSuccess releaseNativeHandles(); + + /** + * Sets the swap interval for swapBuffers. + * \param interval The swap interval to use. + * \return A boolean success indicator. + */ + GHOST_TSuccess setSwapInterval(int interval); + + /** + * Gets the current swap interval for swapBuffers. + * \param intervalOut Variable to store the swap interval if it can be read. + * \return Whether the swap interval can be read. + */ + GHOST_TSuccess getSwapInterval(int &intervalOut); + +protected: + inline void activateEGLEW() const { +#ifdef WITH_GLEW_MX + eglewContext = m_eglewContext; +#endif + } + +private: + void initContextEGLEW(); + + EGLNativeDisplayType m_nativeDisplay; + EGLNativeWindowType m_nativeWindow; + + const EGLint m_contextProfileMask; + const EGLint m_contextMajorVersion; + const EGLint m_contextMinorVersion; + const EGLint m_contextFlags; + const EGLint m_contextResetNotificationStrategy; + + const EGLenum m_api; + + EGLContext m_context; + EGLSurface m_surface; + EGLDisplay m_display; + + EGLint m_swap_interval; + +#ifdef WITH_GLEW_MX + EGLEWContext *m_eglewContext; +#endif + + EGLContext &m_sharedContext; + EGLint &m_sharedCount; + + static EGLContext s_gl_sharedContext; + static EGLint s_gl_sharedCount; + + static EGLContext s_gles_sharedContext; + static EGLint s_gles_sharedCount; + + static EGLContext s_vg_sharedContext; + static EGLint s_vg_sharedCount; + +#ifdef WITH_GL_ANGLE + static HMODULE s_d3dcompiler; +#endif +}; + +#endif // __GHOST_CONTEXTEGL_H__ diff --git a/intern/ghost/intern/GHOST_ContextGLX.cpp b/intern/ghost/intern/GHOST_ContextGLX.cpp new file mode 100644 index 00000000000..02b43abec6c --- /dev/null +++ b/intern/ghost/intern/GHOST_ContextGLX.cpp @@ -0,0 +1,411 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2014 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Jason Wilkins + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file ghost/intern/GHOST_ContextGLX.cpp + * \ingroup GHOST + * + * Definition of GHOST_ContextGLX class. + */ + +#include "GHOST_ContextGLX.h" +#include "GHOST_SystemX11.h" + +#include <vector> + +#include <cassert> +#include <cstdio> +#include <cstring> + + +#ifdef WITH_GLEW_MX +GLXEWContext *glxewContext = NULL; +#endif + +GLXContext GHOST_ContextGLX::s_sharedContext = None; +int GHOST_ContextGLX::s_sharedCount = 0; + + +GHOST_ContextGLX::GHOST_ContextGLX( + bool stereoVisual, + GHOST_TUns16 numOfAASamples, + Window window, + Display *display, + XVisualInfo *visualInfo, + int contextProfileMask, + int contextMajorVersion, + int contextMinorVersion, + int contextFlags, + int contextResetNotificationStrategy) + : GHOST_Context(stereoVisual, numOfAASamples), + m_display(display), + m_visualInfo(visualInfo), + m_window(window), + m_contextProfileMask(contextProfileMask), + m_contextMajorVersion(contextMajorVersion), + m_contextMinorVersion(contextMinorVersion), + m_contextFlags(contextFlags), + m_contextResetNotificationStrategy(contextResetNotificationStrategy), + m_context(None) +#ifdef WITH_GLEW_MX + , + m_glxewContext(NULL) +#endif +{ + assert(m_window != 0); + assert(m_display != NULL); +} + + +GHOST_ContextGLX::~GHOST_ContextGLX() +{ + if (m_display != NULL) { + activateGLXEW(); + + if (m_context != None) { + if (m_window != 0 && m_context == ::glXGetCurrentContext()) + ::glXMakeCurrent(m_display, m_window, NULL); + + if (m_context != s_sharedContext || s_sharedCount == 1) { + assert(s_sharedCount > 0); + + s_sharedCount--; + + if (s_sharedCount == 0) + s_sharedContext = NULL; + + ::glXDestroyContext(m_display, m_context); + } + } + +#ifdef WITH_GLEW_MX + if (m_glxewContext) + delete m_glxewContext; +#endif + } +} + + +GHOST_TSuccess GHOST_ContextGLX::swapBuffers() +{ + ::glXSwapBuffers(m_display, m_window); + + return GHOST_kSuccess; +} + + +GHOST_TSuccess GHOST_ContextGLX::activateDrawingContext() +{ + if (m_display) { + activateGLXEW(); + activateGLEW(); + + return ::glXMakeCurrent(m_display, m_window, m_context) ? GHOST_kSuccess : GHOST_kFailure; + } + else { + return GHOST_kFailure; + } +} + +void GHOST_ContextGLX::initContextGLXEW() +{ +#ifdef WITH_GLEW_MX + glxewContext = new GLXEWContext; + memset(glxewContext, 0, sizeof(GLXEWContext)); + + if (m_glxewContext) + delete m_glxewContext; + m_glxewContext = glxewContext; +#endif + + initContextGLEW(); +} + +GHOST_TSuccess GHOST_ContextGLX::initializeDrawingContext() +{ +#ifdef WITH_X11_XINPUT + /* use our own event handlers to avoid exiting blender, + * this would happen for eg: + * if you open blender, unplug a tablet, then open a new window. */ + XErrorHandler old_handler = XSetErrorHandler (GHOST_X11_ApplicationErrorHandler ); + XIOErrorHandler old_handler_io = XSetIOErrorHandler(GHOST_X11_ApplicationIOErrorHandler); +#endif + + /* needed so 'GLXEW_ARB_create_context' is valid */ + mxIgnoreNoVersion(1); + initContextGLXEW(); + mxIgnoreNoVersion(0); + + if (GLXEW_ARB_create_context) { + int profileBitCore = m_contextProfileMask & GLX_CONTEXT_CORE_PROFILE_BIT_ARB; + int profileBitCompat = m_contextProfileMask & GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; + +#ifdef WITH_GLEW_ES + int profileBitES = m_contextProfileMask & GLX_CONTEXT_ES_PROFILE_BIT_EXT; +#endif + + if (!GLXEW_ARB_create_context_profile && profileBitCore) + fprintf(stderr, "Warning! OpenGL core profile not available.\n"); + + if (!GLXEW_ARB_create_context_profile && profileBitCompat) + fprintf(stderr, "Warning! OpenGL compatibility profile not available.\n"); + +#ifdef WITH_GLEW_ES + if (!GLXEW_EXT_create_context_es_profile && profileBitES && m_contextMajorVersion == 1) + fprintf(stderr, "Warning! OpenGL ES profile not available.\n"); + + if (!GLXEW_EXT_create_context_es2_profile && profileBitES && m_contextMajorVersion == 2) + fprintf(stderr, "Warning! OpenGL ES2 profile not available.\n"); +#endif + + int profileMask = 0; + + if (GLXEW_ARB_create_context_profile && profileBitCore) + profileMask |= profileBitCore; + + if (GLXEW_ARB_create_context_profile && profileBitCompat) + profileMask |= profileBitCompat; + +#ifdef WITH_GLEW_ES + if (GLXEW_EXT_create_context_es_profile && profileBitES) + profileMask |= profileBitES; +#endif + + if (profileMask != m_contextProfileMask) + fprintf(stderr, "Warning! Ignoring untested OpenGL context profile mask bits."); + + + /* max 10 attributes plus terminator */ + int attribs[11]; + int i = 0; + + if (profileMask) { + attribs[i++] = GLX_CONTEXT_PROFILE_MASK_ARB; + attribs[i++] = profileMask; + } + + if (m_contextMajorVersion != 0) { + attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB; + attribs[i++] = m_contextMajorVersion; + } + + if (m_contextMinorVersion != 0) { + attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB; + attribs[i++] = m_contextMinorVersion; + } + + if (m_contextFlags != 0) { + attribs[i++] = GLX_CONTEXT_FLAGS_ARB; + attribs[i++] = m_contextFlags; + } + + if (m_contextResetNotificationStrategy != 0) { + if (GLXEW_ARB_create_context_robustness) { + attribs[i++] = GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB; + attribs[i++] = m_contextResetNotificationStrategy; + } + else { + fprintf(stderr, "Warning! Cannot set the reset notification strategy."); + } + } + attribs[i++] = 0; + + /* Create a GL 3.x context */ + GLXFBConfig *framebuffer_config = NULL; + { + int glx_attribs[64]; + int fbcount = 0; + + GHOST_X11_GL_GetAttributes(glx_attribs, 64, m_numOfAASamples, m_stereoVisual, true); + + framebuffer_config = glXChooseFBConfig(m_display, DefaultScreen(m_display), glx_attribs, &fbcount); + } + + if (framebuffer_config) { + m_context = glXCreateContextAttribsARB(m_display, framebuffer_config[0], s_sharedContext, True, attribs); + XFree(framebuffer_config); + } + } + else { + /* Create legacy context */ + m_context = glXCreateContext(m_display, m_visualInfo, s_sharedContext, True); + } + + GHOST_TSuccess success; + + if (m_context != NULL) { + if (!s_sharedContext) + s_sharedContext = m_context; + + s_sharedCount++; + + glXMakeCurrent(m_display, m_window, m_context); + + initClearGL(); + ::glXSwapBuffers(m_display, m_window); + + /* re initialize to get the extensions properly */ + initContextGLXEW(); + + success = GHOST_kSuccess; + } + else { + /* freeing well clean up the context initialized above */ + success = GHOST_kFailure; + } + +#ifdef WITH_X11_XINPUT + /* Restore handler */ + XSetErrorHandler (old_handler); + XSetIOErrorHandler(old_handler_io); +#endif + + return success; +} + + +GHOST_TSuccess GHOST_ContextGLX::releaseNativeHandles() +{ + m_window = 0; + + return GHOST_kSuccess; +} + + +GHOST_TSuccess GHOST_ContextGLX::setSwapInterval(int interval) +{ + if (GLXEW_EXT_swap_control) { + ::glXSwapIntervalEXT(m_display, m_window, interval); + + return GHOST_kSuccess; + } + else { + return GHOST_kFailure; + } +} + + +GHOST_TSuccess GHOST_ContextGLX::getSwapInterval(int &intervalOut) +{ + if (GLXEW_EXT_swap_control) { + unsigned int interval = 0; + + ::glXQueryDrawable(m_display, m_window, GLX_SWAP_INTERVAL_EXT, &interval); + + intervalOut = static_cast<int>(interval); + + return GHOST_kSuccess; + } + else { + return GHOST_kFailure; + } +} + +/** + * Utility function to get GLX attributes. + * + * \param for_fb_config: There are some small differences in + * #glXChooseVisual and #glXChooseFBConfig's attribute encoding. + * + * \note Similar to SDL's 'X11_GL_GetAttributes' + */ +int GHOST_X11_GL_GetAttributes( + int *attribs, int attribs_max, + int samples, bool is_stereo_visual, + bool for_fb_config) +{ + int i = 0; + +#ifdef GHOST_OPENGL_ALPHA + const bool need_alpha = true; +#else + const bool need_alpha = false; +#endif + +#ifdef GHOST_OPENGL_STENCIL + const bool need_stencil = true; +#else + const bool need_stencil = false; +#endif + + if (is_stereo_visual) { + attribs[i++] = GLX_STEREO; + if (for_fb_config) { + attribs[i++] = True; + } + } + + if (for_fb_config) { + attribs[i++] = GLX_RENDER_TYPE; + attribs[i++] = GLX_RGBA_BIT; + } + else { + attribs[i++] = GLX_RGBA; + } + + attribs[i++] = GLX_DOUBLEBUFFER; + if (for_fb_config) { + attribs[i++] = True; + } + + attribs[i++] = GLX_RED_SIZE; + attribs[i++] = True; + + attribs[i++] = GLX_BLUE_SIZE; + attribs[i++] = True; + + attribs[i++] = GLX_GREEN_SIZE; + attribs[i++] = True; + + attribs[i++] = GLX_DEPTH_SIZE; + attribs[i++] = True; + + if (need_alpha) { + attribs[i++] = GLX_ALPHA_SIZE; + attribs[i++] = True; + } + + if (need_stencil) { + attribs[i++] = GLX_STENCIL_SIZE; + attribs[i++] = True; + } + + if (samples) { + attribs[i++] = GLX_SAMPLE_BUFFERS_ARB; + attribs[i++] = True; + + attribs[i++] = GLX_SAMPLES_ARB; + attribs[i++] = samples; + } + + attribs[i++] = 0; + + GHOST_ASSERT(i <= attribs_max, "attribute size too small"); + + (void)attribs_max; + + return i; +} diff --git a/intern/ghost/intern/GHOST_ContextGLX.h b/intern/ghost/intern/GHOST_ContextGLX.h new file mode 100644 index 00000000000..8c2231a0b01 --- /dev/null +++ b/intern/ghost/intern/GHOST_ContextGLX.h @@ -0,0 +1,156 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2014 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Jason Wilkins + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file ghost/intern/GHOST_ContextGLX.h + * \ingroup GHOST + */ + +#ifndef __GHOST_CONTEXTGLX_H__ +#define __GHOST_CONTEXTGLX_H__ + +#include "GHOST_Context.h" + +#ifdef WITH_GLEW_MX +# define glxewGetContext() glxewContext +#endif + +#include <GL/glxew.h> + +#ifdef WITH_GLEW_MX +extern "C" GLXEWContext *glxewContext; +#endif + + +#ifndef GHOST_OPENGL_GLX_CONTEXT_FLAGS +/* leave as convenience define for the future */ +#define GHOST_OPENGL_GLX_CONTEXT_FLAGS 0 +#endif + +#ifndef GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY +#define GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY 0 +#endif + +class GHOST_ContextGLX : public GHOST_Context +{ +public: + /** + * Constructor. + */ + GHOST_ContextGLX( + bool stereoVisual, + GHOST_TUns16 numOfAASamples, + Window window, + Display *display, + XVisualInfo *visualInfo, + int contextProfileMask, + int contextMajorVersion, + int contextMinorVersion, + int contextFlags, + int contextResetNotificationStrategy); + + /** + * Destructor. + */ + ~GHOST_ContextGLX(); + + /** + * Swaps front and back buffers of a window. + * \return A boolean success indicator. + */ + GHOST_TSuccess swapBuffers(); + + /** + * Activates the drawing context of this window. + * \return A boolean success indicator. + */ + GHOST_TSuccess activateDrawingContext(); + + /** + * Call immediately after new to initialize. If this fails then immediately delete the object. + * \return Indication as to whether initialization has succeeded. + */ + GHOST_TSuccess initializeDrawingContext(); + + /** + * Removes references to native handles from this context and then returns + * \return GHOST_kSuccess if it is OK for the parent to release the handles and + * GHOST_kFailure if releasing the handles will interfere with sharing + */ + GHOST_TSuccess releaseNativeHandles(); + + /** + * Sets the swap interval for swapBuffers. + * \param interval The swap interval to use. + * \return A boolean success indicator. + */ + GHOST_TSuccess setSwapInterval(int interval); + + /** + * Gets the current swap interval for swapBuffers. + * \param intervalOut Variable to store the swap interval if it can be read. + * \return Whether the swap interval can be read. + */ + GHOST_TSuccess getSwapInterval(int &intervalOut); + +protected: + inline void activateGLXEW() const { +#ifdef WITH_GLEW_MX + glxewContext = m_glxewContext; +#endif + } + +private: + void initContextGLXEW(); + + Display *m_display; + XVisualInfo *m_visualInfo; + Window m_window; + + const int m_contextProfileMask; + const int m_contextMajorVersion; + const int m_contextMinorVersion; + const int m_contextFlags; + const int m_contextResetNotificationStrategy; + + GLXContext m_context; + +#ifdef WITH_GLEW_MX + GLXEWContext *m_glxewContext; +#endif + + /** The first created OpenGL context (for sharing display lists) */ + static GLXContext s_sharedContext; + static int s_sharedCount; +}; + +/* used to get GLX info */ +int GHOST_X11_GL_GetAttributes( + int *attribs, int attribs_max, + int samples, bool is_stereo_visual, + bool for_fb_config); + +#endif // __GHOST_CONTEXTGLX_H__ diff --git a/intern/ghost/intern/GHOST_ContextNone.cpp b/intern/ghost/intern/GHOST_ContextNone.cpp new file mode 100644 index 00000000000..380ab532f7a --- /dev/null +++ b/intern/ghost/intern/GHOST_ContextNone.cpp @@ -0,0 +1,79 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2013 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Jason Wilkins + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file ghost/intern/GHOST_ContextNone.cpp + * \ingroup GHOST + * + * Definition of GHOST_ContextNone class. + */ + +#include "GHOST_ContextNone.h" + + +GHOST_TSuccess GHOST_ContextNone::swapBuffers() +{ + return GHOST_kSuccess; +} + + +GHOST_TSuccess GHOST_ContextNone::activateDrawingContext() +{ + return GHOST_kSuccess; +} + + +GHOST_TSuccess GHOST_ContextNone::updateDrawingContext() +{ + return GHOST_kSuccess; +} + + +GHOST_TSuccess GHOST_ContextNone::initializeDrawingContext() +{ + return GHOST_kSuccess; +} + + +GHOST_TSuccess GHOST_ContextNone::releaseNativeHandles() +{ + return GHOST_kSuccess; +} + + +GHOST_TSuccess GHOST_ContextNone::setSwapInterval(int interval) +{ + m_swapInterval = interval; + + return GHOST_kSuccess; +} + + +GHOST_TSuccess GHOST_ContextNone::getSwapInterval(int &intervalOut) +{ + intervalOut = m_swapInterval; + return GHOST_kSuccess; +} diff --git a/intern/ghost/intern/GHOST_ContextNone.h b/intern/ghost/intern/GHOST_ContextNone.h new file mode 100644 index 00000000000..80cce76190d --- /dev/null +++ b/intern/ghost/intern/GHOST_ContextNone.h @@ -0,0 +1,97 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2013 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Jason Wilkins + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file ghost/intern/GHOST_ContextNone.h + * \ingroup GHOST + * + * Declaration of GHOST_Context class. + */ + +#ifndef __GHOST_CONTEXTNONE_H__ +#define __GHOST_CONTEXTNONE_H__ + +#include "GHOST_Context.h" + +class GHOST_ContextNone : public GHOST_Context +{ +public: + + GHOST_ContextNone( + bool stereoVisual, + GHOST_TUns16 numOfAASamples) + : GHOST_Context(stereoVisual, numOfAASamples), + m_swapInterval(1) + {} + + /** + * Dummy function + * \return Always succeeds + */ + GHOST_TSuccess swapBuffers(); + + /** + * Dummy function + * \return Always succeeds + */ + GHOST_TSuccess activateDrawingContext(); + + /** + * Dummy function + * \return Always succeeds + */ + GHOST_TSuccess updateDrawingContext(); + + /** + * Dummy function + * \return Always succeeds + */ + GHOST_TSuccess initializeDrawingContext(); + + /** + * Dummy function + * \return Always succeeds + */ + GHOST_TSuccess releaseNativeHandles(); + + /** + * Dummy function + * \return Always succeeds + */ + GHOST_TSuccess setSwapInterval(int interval); + + /** + * Dummy function + * \param intervalOut Gets whatever was set by setSwapInterval + * \return Always succeeds + */ + GHOST_TSuccess getSwapInterval(int &intervalOut); + +private: + int m_swapInterval; +}; + +#endif // __GHOST_CONTEXTNONE_H__ diff --git a/intern/ghost/intern/GHOST_ContextSDL.cpp b/intern/ghost/intern/GHOST_ContextSDL.cpp new file mode 100644 index 00000000000..d80a638818c --- /dev/null +++ b/intern/ghost/intern/GHOST_ContextSDL.cpp @@ -0,0 +1,204 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2014 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Jason Wilkins + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file ghost/intern/GHOST_ContextSDL.cpp + * \ingroup GHOST + * + * Definition of GHOST_ContextSDL class. + */ + +#include "GHOST_ContextSDL.h" + +#include <vector> + +#include <cassert> +#include <cstdio> +#include <cstring> + + +SDL_GLContext GHOST_ContextSDL::s_sharedContext = NULL; +int GHOST_ContextSDL::s_sharedCount = 0; + + +GHOST_ContextSDL::GHOST_ContextSDL( + bool stereoVisual, + GHOST_TUns16 numOfAASamples, + SDL_Window *window, + int contextProfileMask, + int contextMajorVersion, + int contextMinorVersion, + int contextFlags, + int contextResetNotificationStrategy) + : GHOST_Context(stereoVisual, numOfAASamples), + m_window(window), + m_contextProfileMask(contextProfileMask), + m_contextMajorVersion(contextMajorVersion), + m_contextMinorVersion(contextMinorVersion), + m_contextFlags(contextFlags), + m_contextResetNotificationStrategy(contextResetNotificationStrategy), + m_context(NULL) +{ + assert(m_window != NULL); +} + + +GHOST_ContextSDL::~GHOST_ContextSDL() +{ + if (m_context != NULL) { + if (m_window != 0 && m_context == SDL_GL_GetCurrentContext()) + SDL_GL_MakeCurrent(m_window, m_context); + + if (m_context != s_sharedContext || s_sharedCount == 1) { + assert(s_sharedCount > 0); + + s_sharedCount--; + + if (s_sharedCount == 0) + s_sharedContext = NULL; + + SDL_GL_DeleteContext(m_context); + } + } +} + + +GHOST_TSuccess GHOST_ContextSDL::swapBuffers() +{ + SDL_GL_SwapWindow(m_window); + + return GHOST_kSuccess; +} + + +GHOST_TSuccess GHOST_ContextSDL::activateDrawingContext() +{ + if (m_context) { + activateGLEW(); + + return SDL_GL_MakeCurrent(m_window, m_context) ? GHOST_kSuccess : GHOST_kFailure; + } + else { + return GHOST_kFailure; + } +} + + +GHOST_TSuccess GHOST_ContextSDL::initializeDrawingContext() +{ +#ifdef GHOST_OPENGL_ALPHA + const bool needAlpha = true; +#else + const bool needAlpha = false; +#endif + +#ifdef GHOST_OPENGL_STENCIL + const bool needStencil = true; +#else + const bool needStencil = false; +#endif + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, m_contextProfileMask); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, m_contextMajorVersion); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, m_contextMinorVersion); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, m_contextFlags); + + SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + + if (needAlpha) { + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); + } + + if (needStencil) { + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1); + } + + if (m_stereoVisual) { + SDL_GL_SetAttribute(SDL_GL_STEREO, 1); + } + + if (m_numOfAASamples) { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, m_numOfAASamples); + } + + m_context = SDL_GL_CreateContext(m_window); + + GHOST_TSuccess success; + + if (m_context != NULL) { + if (!s_sharedContext) + s_sharedContext = m_context; + + s_sharedCount++; + + success = (SDL_GL_MakeCurrent(m_window, m_context) < 0) ? + GHOST_kFailure : GHOST_kSuccess; + + initContextGLEW(); + + initClearGL(); + SDL_GL_SwapWindow(m_window); + + success = GHOST_kSuccess; + } + else { + success = GHOST_kFailure; + } + + return success; +} + + +GHOST_TSuccess GHOST_ContextSDL::releaseNativeHandles() +{ + m_window = NULL; + + return GHOST_kSuccess; +} + + +GHOST_TSuccess GHOST_ContextSDL::setSwapInterval(int interval) +{ + if (SDL_GL_SetSwapInterval(interval) != -1) { + return GHOST_kSuccess; + } + else { + return GHOST_kFailure; + } +} + + +GHOST_TSuccess GHOST_ContextSDL::getSwapInterval(int &intervalOut) +{ + intervalOut = SDL_GL_GetSwapInterval(); + return GHOST_kSuccess; +} diff --git a/intern/ghost/intern/GHOST_ContextSDL.h b/intern/ghost/intern/GHOST_ContextSDL.h new file mode 100644 index 00000000000..61f339c1bc2 --- /dev/null +++ b/intern/ghost/intern/GHOST_ContextSDL.h @@ -0,0 +1,131 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2014 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Jason Wilkins + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file ghost/intern/GHOST_ContextSDL.h + * \ingroup GHOST + */ + +#ifndef __GHOST_CONTEXTSDL_H__ +#define __GHOST_CONTEXTSDL_H__ + +#include "GHOST_Context.h" + +extern "C" { + #include "SDL.h" +} + + +#ifndef GHOST_OPENGL_SDL_CONTEXT_FLAGS +# ifdef WITH_GPU_DEBUG +# define GHOST_OPENGL_SDL_CONTEXT_FLAGS SDL_GL_CONTEXT_DEBUG_FLAG +# else +# define GHOST_OPENGL_SDL_CONTEXT_FLAGS 0 +# endif +#endif + +#ifndef GHOST_OPENGL_SDL_RESET_NOTIFICATION_STRATEGY +#define GHOST_OPENGL_SDL_RESET_NOTIFICATION_STRATEGY 0 +#endif + + +class GHOST_ContextSDL : public GHOST_Context +{ +public: + /** + * Constructor. + */ + GHOST_ContextSDL( + bool stereoVisual, + GHOST_TUns16 numOfAASamples, + SDL_Window *window, + int contextProfileMask, + int contextMajorVersion, + int contextMinorVersion, + int contextFlags, + int contextResetNotificationStrategy); + + /** + * Destructor. + */ + ~GHOST_ContextSDL(); + + /** + * Swaps front and back buffers of a window. + * \return A boolean success indicator. + */ + GHOST_TSuccess swapBuffers(); + + /** + * Activates the drawing context of this window. + * \return A boolean success indicator. + */ + GHOST_TSuccess activateDrawingContext(); + + /** + * Call immediately after new to initialize. If this fails then immediately delete the object. + * \return Indication as to whether initialization has succeeded. + */ + GHOST_TSuccess initializeDrawingContext(); + + /** + * Removes references to native handles from this context and then returns + * \return GHOST_kSuccess if it is OK for the parent to release the handles and + * GHOST_kFailure if releasing the handles will interfere with sharing + */ + GHOST_TSuccess releaseNativeHandles(); + + /** + * Sets the swap interval for swapBuffers. + * \param interval The swap interval to use. + * \return A boolean success indicator. + */ + GHOST_TSuccess setSwapInterval(int interval); + + /** + * Gets the current swap interval for swapBuffers. + * \param intervalOut Variable to store the swap interval if it can be read. + * \return Whether the swap interval can be read. + */ + GHOST_TSuccess getSwapInterval(int &intervalOut); + +private: + SDL_Window *m_window; + + const int m_contextProfileMask; + const int m_contextMajorVersion; + const int m_contextMinorVersion; + const int m_contextFlags; + const int m_contextResetNotificationStrategy; + + SDL_GLContext m_context; /* m_sdl_glcontext */ + + /** The first created OpenGL context (for sharing display lists) */ + static SDL_GLContext s_sharedContext; + static int s_sharedCount; +}; + +#endif // __GHOST_CONTEXTSDL_H__ diff --git a/intern/ghost/intern/GHOST_ContextWGL.cpp b/intern/ghost/intern/GHOST_ContextWGL.cpp new file mode 100644 index 00000000000..d2a9eed95d9 --- /dev/null +++ b/intern/ghost/intern/GHOST_ContextWGL.cpp @@ -0,0 +1,956 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2013 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Jason Wilkins + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file ghost/intern/GHOST_ContextWGL.cpp + * \ingroup GHOST + * + * Definition of GHOST_ContextWGL class. + */ + +#include "GHOST_ContextWGL.h" + +#include <tchar.h> + +#include <cstdio> +#include <cassert> +#include <vector> + + +#ifdef WITH_GLEW_MX +WGLEWContext *wglewContext = NULL; +#endif + +HGLRC GHOST_ContextWGL::s_sharedHGLRC = NULL; +int GHOST_ContextWGL::s_sharedCount = 0; + +bool GHOST_ContextWGL::s_singleContextMode = false; +bool GHOST_ContextWGL::s_warn_old = false; + + +/* Intel video-cards don't work fine with multiple contexts and + * have to share the same context for all windows. + * But if we just share context for all windows it could work incorrect + * with multiple videocards configuration. Suppose, that Intel videocards + * can't be in multiple-devices configuration. */ +static bool is_crappy_intel_card() +{ + return strstr((const char *)glGetString(GL_VENDOR), "Intel") != NULL; +} + + +GHOST_ContextWGL::GHOST_ContextWGL( + bool stereoVisual, + GHOST_TUns16 numOfAASamples, + HWND hWnd, + HDC hDC, + int contextProfileMask, + int contextMajorVersion, + int contextMinorVersion, + int contextFlags, + int contextResetNotificationStrategy) + : GHOST_Context(stereoVisual, numOfAASamples), + m_hWnd(hWnd), + m_hDC(hDC), + m_contextProfileMask(contextProfileMask), + m_contextMajorVersion(contextMajorVersion), + m_contextMinorVersion(contextMinorVersion), + m_contextFlags(contextFlags), + m_contextResetNotificationStrategy(contextResetNotificationStrategy), + m_hGLRC(NULL) +#ifdef WITH_GLEW_MX + , + m_wglewContext(NULL) +#endif +#ifndef NDEBUG + , + m_dummyVendor(NULL), + m_dummyRenderer(NULL), + m_dummyVersion(NULL) +#endif +{ + assert(m_hWnd); + assert(m_hDC); +} + + +GHOST_ContextWGL::~GHOST_ContextWGL() +{ + if (m_hGLRC != NULL) { + if (m_hGLRC == ::wglGetCurrentContext()) + WIN32_CHK(::wglMakeCurrent(NULL, NULL)); + + if (m_hGLRC != s_sharedHGLRC || s_sharedCount == 1) { + assert(s_sharedCount > 0); + + s_sharedCount--; + + if (s_sharedCount == 0) + s_sharedHGLRC = NULL; + + WIN32_CHK(::wglDeleteContext(m_hGLRC)); + } + } + +#ifdef WITH_GLEW_MX + delete m_wglewContext; +#endif + +#ifndef NDEBUG + free((void*)m_dummyRenderer); + free((void*)m_dummyVendor); + free((void*)m_dummyVersion); +#endif +} + + +GHOST_TSuccess GHOST_ContextWGL::swapBuffers() +{ + return WIN32_CHK(::SwapBuffers(m_hDC)) ? GHOST_kSuccess : GHOST_kFailure; +} + + +GHOST_TSuccess GHOST_ContextWGL::setSwapInterval(int interval) +{ + if (WGLEW_EXT_swap_control) + return WIN32_CHK(::wglSwapIntervalEXT(interval)) == TRUE ? GHOST_kSuccess : GHOST_kFailure; + else + return GHOST_kFailure; +} + + +GHOST_TSuccess GHOST_ContextWGL::getSwapInterval(int &intervalOut) +{ + if (WGLEW_EXT_swap_control) { + intervalOut = ::wglGetSwapIntervalEXT(); + return GHOST_kSuccess; + } + else { + return GHOST_kFailure; + } +} + + +GHOST_TSuccess GHOST_ContextWGL::activateDrawingContext() +{ + if (WIN32_CHK(::wglMakeCurrent(m_hDC, m_hGLRC))) { + activateGLEW(); + return GHOST_kSuccess; + } + else { + return GHOST_kFailure; + } +} + + +/* Ron Fosner's code for weighting pixel formats and forcing software. + * See http://www.opengl.org/resources/faq/technical/weight.cpp + */ +static int weight_pixel_format(PIXELFORMATDESCRIPTOR &pfd) +{ + int weight = 0; + + /* assume desktop color depth is 32 bits per pixel */ + + /* cull unusable pixel formats */ + /* if no formats can be found, can we determine why it was rejected? */ + if (!(pfd.dwFlags & PFD_SUPPORT_OPENGL) || + !(pfd.dwFlags & PFD_DRAW_TO_WINDOW) || + !(pfd.dwFlags & PFD_DOUBLEBUFFER) || /* Blender _needs_ this */ + !(pfd.iPixelType == PFD_TYPE_RGBA) || + (pfd.cDepthBits < 16) || + (pfd.dwFlags & PFD_GENERIC_FORMAT)) /* no software renderers */ + { + return 0; + } + + weight = 1; /* it's usable */ + + /* the bigger the depth buffer the better */ + /* give no weight to a 16-bit depth buffer, because those are crap */ + weight += pfd.cDepthBits - 16; + + weight += pfd.cColorBits - 8; + +#ifdef GHOST_OPENGL_ALPHA + if (pfd.cAlphaBits > 0) + weight++; +#endif + +#ifdef GHOST_OPENGL_STENCIL + if (pfd.cStencilBits >= 8) + weight++; +#endif + + /* want swap copy capability -- it matters a lot */ + if (pfd.dwFlags & PFD_SWAP_COPY) + weight += 16; + + return weight; +} + + +/* + * A modification of Ron Fosner's replacement for ChoosePixelFormat + * returns 0 on error, else returns the pixel format number to be used + */ +static int choose_pixel_format_legacy(HDC hDC, PIXELFORMATDESCRIPTOR &preferredPFD) +{ + int iPixelFormat = 0; + int weight = 0; + + int iStereoPixelFormat = 0; + int stereoWeight = 0; + + /* choose a pixel format using the useless Windows function in case we come up empty handed */ + int iLastResortPixelFormat = ::ChoosePixelFormat(hDC, &preferredPFD); + + WIN32_CHK(iLastResortPixelFormat != 0); + + int lastPFD = ::DescribePixelFormat(hDC, 1, sizeof(PIXELFORMATDESCRIPTOR), NULL); + + WIN32_CHK(lastPFD != 0); + + for (int i = 1; i <= lastPFD; i++) { + PIXELFORMATDESCRIPTOR pfd; + int check = ::DescribePixelFormat(hDC, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd); + + WIN32_CHK(check == lastPFD); + + int w = weight_pixel_format(pfd); + + if (w > weight) { + weight = w; + iPixelFormat = i; + } + + if (w > stereoWeight && (preferredPFD.dwFlags & pfd.dwFlags & PFD_STEREO)) { + stereoWeight = w; + iStereoPixelFormat = i; + } + } + + /* choose any available stereo format over a non-stereo format */ + if (iStereoPixelFormat != 0) + iPixelFormat = iStereoPixelFormat; + + if (iPixelFormat == 0) { + fprintf(stderr, "Warning! Using result of ChoosePixelFormat.\n"); + iPixelFormat = iLastResortPixelFormat; + } + + return iPixelFormat; +} + + +/* + * Clone a window for the purpose of creating a temporary context to initialize WGL extensions. + * There is no generic way to clone the lpParam parameter, so the caller is responsible for cloning it themselves. + */ + +static HWND clone_window(HWND hWnd, LPVOID lpParam) +{ + int count; + + SetLastError(NO_ERROR); + + DWORD dwExStyle = GetWindowLong(hWnd, GWL_EXSTYLE); + WIN32_CHK(GetLastError() == NO_ERROR); + + WCHAR lpClassName[100] = L""; + count = GetClassNameW(hWnd, lpClassName, sizeof(lpClassName)); + WIN32_CHK(count != 0); + + WCHAR lpWindowName[100] = L""; + count = GetWindowTextW(hWnd, lpWindowName, sizeof(lpWindowName)); + WIN32_CHK(count != 0); + + DWORD dwStyle = GetWindowLong(hWnd, GWL_STYLE); + WIN32_CHK(GetLastError() == NO_ERROR); + + RECT rect; + GetWindowRect(hWnd, &rect); + WIN32_CHK(GetLastError() == NO_ERROR); + + HWND hWndParent = (HWND)GetWindowLongPtr(hWnd, GWLP_HWNDPARENT); + WIN32_CHK(GetLastError() == NO_ERROR); + + HMENU hMenu = GetMenu(hWnd); + WIN32_CHK(GetLastError() == NO_ERROR); + + HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE); + WIN32_CHK(GetLastError() == NO_ERROR); + + HWND hwndCloned = CreateWindowExW( + dwExStyle, + lpClassName, + lpWindowName, + dwStyle, + rect.left, + rect.top, + rect.right - rect.left, + rect.bottom - rect.top, + hWndParent, + hMenu, + hInstance, + lpParam); + + WIN32_CHK(hwndCloned != NULL); + + return hwndCloned; +} + + +void GHOST_ContextWGL::initContextWGLEW(PIXELFORMATDESCRIPTOR &preferredPFD) +{ + HWND dummyHWND = NULL; + HDC dummyHDC = NULL; + HGLRC dummyHGLRC = NULL; + + HDC prevHDC; + HGLRC prevHGLRC; + + int iPixelFormat; + + +#ifdef WITH_GLEW_MX + wglewContext = new WGLEWContext; + memset(wglewContext, 0, sizeof(WGLEWContext)); + + delete m_wglewContext; + m_wglewContext = wglewContext; +#endif + + SetLastError(NO_ERROR); + + prevHDC = ::wglGetCurrentDC(); + WIN32_CHK(GetLastError() == NO_ERROR); + + prevHGLRC = ::wglGetCurrentContext(); + WIN32_CHK(GetLastError() == NO_ERROR); + + dummyHWND = clone_window(m_hWnd, NULL); + + if (dummyHWND == NULL) + goto finalize; + + dummyHDC = GetDC(dummyHWND); + + if (!WIN32_CHK(dummyHDC != NULL)) + goto finalize; + + iPixelFormat = choose_pixel_format_legacy(dummyHDC, preferredPFD); + + if (iPixelFormat == 0) + goto finalize; + + PIXELFORMATDESCRIPTOR chosenPFD; + if (!WIN32_CHK(::DescribePixelFormat(dummyHDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &chosenPFD))) + goto finalize; + + if (!WIN32_CHK(::SetPixelFormat(dummyHDC, iPixelFormat, &chosenPFD))) + goto finalize; + + dummyHGLRC = ::wglCreateContext(dummyHDC); + + if (!WIN32_CHK(dummyHGLRC != NULL)) + goto finalize; + + if (!WIN32_CHK(::wglMakeCurrent(dummyHDC, dummyHGLRC))) + goto finalize; + +#ifdef WITH_GLEW_MX + if (GLEW_CHK(wglewInit()) != GLEW_OK) + fprintf(stderr, "Warning! WGLEW failed to initialize properly.\n"); +#else + if (GLEW_CHK(glewInit()) != GLEW_OK) + fprintf(stderr, "Warning! Dummy GLEW/WGLEW failed to initialize properly.\n"); +#endif + + // the following are not technially WGLEW, but they also require a context to work + +#ifndef NDEBUG + free((void*)m_dummyRenderer); + free((void*)m_dummyVendor); + free((void*)m_dummyVersion); + + m_dummyRenderer = _strdup(reinterpret_cast<const char *>(glGetString(GL_RENDERER))); + m_dummyVendor = _strdup(reinterpret_cast<const char *>(glGetString(GL_VENDOR))); + m_dummyVersion = _strdup(reinterpret_cast<const char *>(glGetString(GL_VERSION))); +#endif + + s_singleContextMode = is_crappy_intel_card(); + +finalize: + WIN32_CHK(::wglMakeCurrent(prevHDC, prevHGLRC)); + + if (dummyHGLRC != NULL) + WIN32_CHK(::wglDeleteContext(dummyHGLRC)); + + if (dummyHWND != NULL) { + if (dummyHDC != NULL) + WIN32_CHK(::ReleaseDC(dummyHWND, dummyHDC)); + + WIN32_CHK(::DestroyWindow(dummyHWND)); + } +} + + +static void makeAttribList( + std::vector<int>& out, + bool stereoVisual, + int numOfAASamples, + int swapMethod, + bool needAlpha, + bool needStencil, + bool sRGB) +{ + out.clear(); + out.reserve(30); + + out.push_back(WGL_SUPPORT_OPENGL_ARB); + out.push_back(GL_TRUE); + + out.push_back(WGL_DRAW_TO_WINDOW_ARB); + out.push_back(GL_TRUE); + + out.push_back(WGL_DOUBLE_BUFFER_ARB); + out.push_back(GL_TRUE); + + out.push_back(WGL_ACCELERATION_ARB); + out.push_back(WGL_FULL_ACCELERATION_ARB); + + out.push_back(WGL_SWAP_METHOD_ARB); + out.push_back(swapMethod); + + if (stereoVisual) { + out.push_back(WGL_STEREO_ARB); + out.push_back(GL_TRUE); + } + + out.push_back(WGL_PIXEL_TYPE_ARB); + out.push_back(WGL_TYPE_RGBA_ARB); + + out.push_back(WGL_COLOR_BITS_ARB); + out.push_back(24); + + out.push_back(WGL_DEPTH_BITS_ARB); + out.push_back(24); + + if (needAlpha) { + out.push_back(WGL_ALPHA_BITS_ARB); + out.push_back(8); + } + + if (needStencil) { + out.push_back(WGL_STENCIL_BITS_ARB); + out.push_back(8); + } + + if (numOfAASamples > 0) { + out.push_back(WGL_SAMPLES_ARB); + out.push_back(numOfAASamples); + + out.push_back(WGL_SAMPLE_BUFFERS_ARB); + out.push_back(GL_TRUE); + } + + if (sRGB) { + out.push_back(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB); + out.push_back(GL_TRUE); + } + + out.push_back(0); +} + + +int GHOST_ContextWGL::_choose_pixel_format_arb_2( + bool stereoVisual, + int *numOfAASamples, + bool needAlpha, + bool needStencil, + bool sRGB, + int swapMethod) +{ + std::vector<int> iAttributes; + + int iPixelFormat = 0; + + int samples; + + // guard against some insanely high number of samples + if (*numOfAASamples > 64) { + fprintf(stderr, "Warning! Clamping number of samples to 64.\n"); + samples = 64; + } + else { + samples = *numOfAASamples; + } + + // request a format with as many samples as possible, but not more than requested + while (samples >= 0) { + makeAttribList( + iAttributes, + stereoVisual, + samples, + swapMethod, + needAlpha, + needStencil, + sRGB); + + UINT nNumFormats; + WIN32_CHK(wglChoosePixelFormatARB(m_hDC, &(iAttributes[0]), NULL, 1, &iPixelFormat, &nNumFormats)); + + /* total number of formats that match (regardless of size of iPixelFormat array) + * see: WGL_ARB_pixel_format extension spec */ + if (nNumFormats > 0) + break; + + /* if not reset, then the state of iPixelFormat is undefined after call to wglChoosePixelFormatARB + * see: WGL_ARB_pixel_format extension spec */ + iPixelFormat = 0; + + samples--; + } + + // check how many samples were actually gotten + if (iPixelFormat != 0) { + int iQuery[] = { WGL_SAMPLES_ARB }; + int actualSamples; + wglGetPixelFormatAttribivARB(m_hDC, iPixelFormat, 0, 1, iQuery, &actualSamples); + + if (actualSamples != *numOfAASamples) { + fprintf(stderr, + "Warning! Unable to find a multisample pixel format that supports exactly %d samples. " + "Substituting one that uses %d samples.\n", + *numOfAASamples, actualSamples); + + *numOfAASamples = actualSamples; // set context property to actual value + } + } + else { + *numOfAASamples = 0; + } + return iPixelFormat; +} + + +int GHOST_ContextWGL::_choose_pixel_format_arb_1(bool stereoVisual, + int numOfAASamples, + bool needAlpha, + bool needStencil, + bool sRGB, + int *swapMethodOut) +{ + int iPixelFormat; + int copyPixelFormat = 0; + int undefPixelFormat = 0; + int exchPixelFormat = 0; + int copyNumOfAASamples = 0; + int undefNumOfAASamples = 0; + int exchNumOfAASamples = 0; + + *swapMethodOut = WGL_SWAP_COPY_ARB; + copyNumOfAASamples = numOfAASamples; + copyPixelFormat = _choose_pixel_format_arb_2( + stereoVisual, ©NumOfAASamples, needAlpha, needStencil, sRGB, *swapMethodOut); + + if (copyPixelFormat == 0 || copyNumOfAASamples < numOfAASamples) { + *swapMethodOut = WGL_SWAP_UNDEFINED_ARB; + undefNumOfAASamples = numOfAASamples; + undefPixelFormat = _choose_pixel_format_arb_2( + stereoVisual, &undefNumOfAASamples, needAlpha, needStencil, sRGB, *swapMethodOut); + + if (undefPixelFormat == 0 || undefNumOfAASamples < numOfAASamples) { + *swapMethodOut = WGL_SWAP_EXCHANGE_ARB; + exchNumOfAASamples = numOfAASamples; + exchPixelFormat = _choose_pixel_format_arb_2( + stereoVisual, &exchNumOfAASamples, needAlpha, needStencil, sRGB, *swapMethodOut); + if (exchPixelFormat == 0 || exchNumOfAASamples < numOfAASamples) { + // the number of AA samples cannot be met, take the highest + if (undefPixelFormat != 0 && undefNumOfAASamples >= exchNumOfAASamples) { + exchNumOfAASamples = undefNumOfAASamples; + exchPixelFormat = undefPixelFormat; + *swapMethodOut = WGL_SWAP_UNDEFINED_ARB; + } + if (copyPixelFormat != 0 && copyNumOfAASamples >= exchNumOfAASamples) { + exchNumOfAASamples = copyNumOfAASamples; + exchPixelFormat = copyPixelFormat; + *swapMethodOut = WGL_SWAP_COPY_ARB; + } + } + iPixelFormat = exchPixelFormat; + m_numOfAASamples = exchNumOfAASamples; + } + else { + iPixelFormat = undefPixelFormat; + m_numOfAASamples = undefNumOfAASamples; + } + } + else { + iPixelFormat = copyPixelFormat; + m_numOfAASamples = copyNumOfAASamples; + } + return iPixelFormat; +} + + +int GHOST_ContextWGL::choose_pixel_format_arb( + bool stereoVisual, + int numOfAASamples, + bool needAlpha, + bool needStencil, + bool sRGB) +{ + int iPixelFormat; + int swapMethodOut; + + iPixelFormat = _choose_pixel_format_arb_1( + stereoVisual, + numOfAASamples, + needAlpha, + needStencil, + sRGB, + &swapMethodOut); + + if (iPixelFormat == 0 && stereoVisual) { + fprintf(stderr, "Warning! Unable to find a stereo pixel format.\n"); + + iPixelFormat = _choose_pixel_format_arb_1( + false, + numOfAASamples, + needAlpha, + needStencil, + sRGB, + &swapMethodOut); + + m_stereoVisual = false; // set context property to actual value + } + + if (swapMethodOut != WGL_SWAP_COPY_ARB) { + fprintf(stderr, + "Warning! Unable to find a pixel format that supports WGL_SWAP_COPY_ARB. " + "Substituting one that uses %s.\n", + swapMethodOut == WGL_SWAP_UNDEFINED_ARB ? "WGL_SWAP_UNDEFINED_ARB" : "WGL_SWAP_EXCHANGE_ARB"); + } + + return iPixelFormat; +} + + +int GHOST_ContextWGL::choose_pixel_format( + bool stereoVisual, + int numOfAASamples, + bool needAlpha, + bool needStencil, + bool sRGB) +{ + PIXELFORMATDESCRIPTOR preferredPFD = { + sizeof(PIXELFORMATDESCRIPTOR), /* size */ + 1, /* version */ + PFD_SUPPORT_OPENGL | + PFD_DRAW_TO_WINDOW | + PFD_SWAP_COPY | /* support swap copy */ + PFD_DOUBLEBUFFER | /* support double-buffering */ + (stereoVisual ? PFD_STEREO : 0), /* support stereo */ + PFD_TYPE_RGBA, /* color type */ + 24, /* preferred color depth */ + 0, 0, 0, 0, 0, 0, /* color bits (ignored) */ + needAlpha ? 8 : 0, /* alpha buffer */ + 0, /* alpha shift (ignored) */ + 0, /* no accumulation buffer */ + 0, 0, 0, 0, /* accum bits (ignored) */ + 24, /* depth buffer */ + needStencil ? 8 : 0, /* stencil buffer */ + 0, /* no auxiliary buffers */ + PFD_MAIN_PLANE, /* main layer */ + 0, /* reserved */ + 0, 0, 0 /* layer, visible, and damage masks (ignored) */ + }; + + initContextWGLEW(preferredPFD); + + if (numOfAASamples > 0 && !WGLEW_ARB_multisample) { + fprintf(stderr, "Warning! Unable to request a multisample framebuffer.\n"); + numOfAASamples = 0; + } + + if (sRGB && !(WGLEW_ARB_framebuffer_sRGB || WGLEW_EXT_framebuffer_sRGB)) { + fprintf(stderr, "Warning! Unable to request an sRGB framebuffer.\n"); + sRGB = false; + } + + int iPixelFormat = 0; + + if (WGLEW_ARB_pixel_format) + iPixelFormat = choose_pixel_format_arb(stereoVisual, numOfAASamples, needAlpha, needStencil, sRGB); + + if (iPixelFormat == 0) + iPixelFormat = choose_pixel_format_legacy(m_hDC, preferredPFD); + + return iPixelFormat; +} + + +#ifndef NDEBUG +static void reportContextString(const char *name, const char *dummy, const char *context) +{ + fprintf(stderr, "%s: %s\n", name, context); + + if (strcmp(dummy, context) != 0) + fprintf(stderr, "Warning! Dummy %s: %s\n", name, dummy); +} +#endif + + +GHOST_TSuccess GHOST_ContextWGL::initializeDrawingContext() +{ +#ifdef GHOST_OPENGL_ALPHA + const bool needAlpha = true; +#else + const bool needAlpha = false; +#endif + +#ifdef GHOST_OPENGL_STENCIL + const bool needStencil = true; +#else + const bool needStencil = false; +#endif + +#ifdef GHOST_OPENGL_SRGB + const bool sRGB = true; +#else + const bool sRGB = false; +#endif + + HGLRC prevHGLRC; + HDC prevHDC; + + int iPixelFormat; + int lastPFD; + + PIXELFORMATDESCRIPTOR chosenPFD; + + + SetLastError(NO_ERROR); + + prevHGLRC = ::wglGetCurrentContext(); + WIN32_CHK(GetLastError() == NO_ERROR); + + prevHDC = ::wglGetCurrentDC(); + WIN32_CHK(GetLastError() == NO_ERROR); + + iPixelFormat = choose_pixel_format(m_stereoVisual, m_numOfAASamples, needAlpha, needStencil, sRGB); + + if (iPixelFormat == 0) { + ::wglMakeCurrent(prevHDC, prevHGLRC); + return GHOST_kFailure; + } + + lastPFD = ::DescribePixelFormat(m_hDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &chosenPFD); + + if (!WIN32_CHK(lastPFD != 0)) { + ::wglMakeCurrent(prevHDC, prevHGLRC); + return GHOST_kFailure; + } + + if (needAlpha && chosenPFD.cAlphaBits == 0) + fprintf(stderr, "Warning! Unable to find a pixel format with an alpha channel.\n"); + + if (needStencil && chosenPFD.cStencilBits == 0) + fprintf(stderr, "Warning! Unable to find a pixel format with a stencil buffer.\n"); + + if (!WIN32_CHK(::SetPixelFormat(m_hDC, iPixelFormat, &chosenPFD))) { + ::wglMakeCurrent(prevHDC, prevHGLRC); + return GHOST_kFailure; + } + + activateWGLEW(); + + if (WGLEW_ARB_create_context) { + int profileBitCore = m_contextProfileMask & WGL_CONTEXT_CORE_PROFILE_BIT_ARB; + int profileBitCompat = m_contextProfileMask & WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; + +#ifdef WITH_GLEW_ES + int profileBitES = m_contextProfileMask & WGL_CONTEXT_ES_PROFILE_BIT_EXT; +#endif + + if (!WGLEW_ARB_create_context_profile && profileBitCore) + fprintf(stderr, "Warning! OpenGL core profile not available.\n"); + + if (!WGLEW_ARB_create_context_profile && profileBitCompat) + fprintf(stderr, "Warning! OpenGL compatibility profile not available.\n"); + +#ifdef WITH_GLEW_ES + if (!WGLEW_EXT_create_context_es_profile && profileBitES && m_contextMajorVersion == 1) + fprintf(stderr, "Warning! OpenGL ES profile not available.\n"); + + if (!WGLEW_EXT_create_context_es2_profile && profileBitES && m_contextMajorVersion == 2) + fprintf(stderr, "Warning! OpenGL ES2 profile not available.\n"); +#endif + + int profileMask = 0; + + if (WGLEW_ARB_create_context_profile && profileBitCore) + profileMask |= profileBitCore; + + if (WGLEW_ARB_create_context_profile && profileBitCompat) + profileMask |= profileBitCompat; + +#ifdef WITH_GLEW_ES + if (WGLEW_EXT_create_context_es_profile && profileBitES) + profileMask |= profileBitES; +#endif + + if (profileMask != m_contextProfileMask) + fprintf(stderr, "Warning! Ignoring untested OpenGL context profile mask bits."); + + std::vector<int> iAttributes; + + if (profileMask) { + iAttributes.push_back(WGL_CONTEXT_PROFILE_MASK_ARB); + iAttributes.push_back(profileMask); + } + + if (m_contextMajorVersion != 0) { + iAttributes.push_back(WGL_CONTEXT_MAJOR_VERSION_ARB); + iAttributes.push_back(m_contextMajorVersion); + } + + if (m_contextMinorVersion != 0) { + iAttributes.push_back(WGL_CONTEXT_MINOR_VERSION_ARB); + iAttributes.push_back(m_contextMinorVersion); + } + + if (m_contextFlags != 0) { + iAttributes.push_back(WGL_CONTEXT_FLAGS_ARB); + iAttributes.push_back(m_contextFlags); + } + + if (m_contextResetNotificationStrategy != 0) { + if (WGLEW_ARB_create_context_robustness) { + iAttributes.push_back(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB); + iAttributes.push_back(m_contextResetNotificationStrategy); + } + else { + fprintf(stderr, "Warning! Cannot set the reset notification strategy."); + } + } + + iAttributes.push_back(0); + + if (!s_singleContextMode || s_sharedHGLRC == NULL) + m_hGLRC = ::wglCreateContextAttribsARB(m_hDC, NULL, &(iAttributes[0])); + else + m_hGLRC = s_sharedHGLRC; + } + else { + if (m_contextProfileMask != 0) + fprintf(stderr, "Warning! Legacy WGL is unable to select between OpenGL profiles."); + + if (m_contextMajorVersion != 0 || m_contextMinorVersion != 0) + fprintf(stderr, "Warning! Legacy WGL is unable to select between OpenGL versions."); + + if (m_contextFlags != 0) + fprintf(stderr, "Warning! Legacy WGL is unable to set context flags."); + + if (!s_singleContextMode || s_sharedHGLRC == NULL) + m_hGLRC = ::wglCreateContext(m_hDC); + else + m_hGLRC = s_sharedHGLRC; + } + + if (!WIN32_CHK(m_hGLRC != NULL)) { + ::wglMakeCurrent(prevHDC, prevHGLRC); + return GHOST_kFailure; + } + + if (s_sharedHGLRC == NULL) + s_sharedHGLRC = m_hGLRC; + + s_sharedCount++; + + if (!s_singleContextMode && s_sharedHGLRC != m_hGLRC && !WIN32_CHK(::wglShareLists(s_sharedHGLRC, m_hGLRC))) { + ::wglMakeCurrent(prevHDC, prevHGLRC); + return GHOST_kFailure; + } + + if (!WIN32_CHK(::wglMakeCurrent(m_hDC, m_hGLRC))) { + ::wglMakeCurrent(prevHDC, prevHGLRC); + return GHOST_kFailure; + } + + initContextGLEW(); + + initClearGL(); + ::SwapBuffers(m_hDC); + + const char *vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR)); + const char *renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER)); + const char *version = reinterpret_cast<const char*>(glGetString(GL_VERSION)); + +#ifndef NDEBUG + reportContextString("Vendor", m_dummyVendor, vendor); + reportContextString("Renderer", m_dummyRenderer, renderer); + reportContextString("Version", m_dummyVersion, version); +#endif + + if (!s_warn_old) { + if ((strcmp(vendor, "Microsoft Corporation") == 0 || + strcmp(renderer, "GDI Generic") == 0) && version[0] == '1' && version[2] == '1') + { + MessageBox(m_hWnd, "Your system does not use 3D hardware acceleration.\n" + "Such systems can cause stability problems in Blender and they are unsupported.\n\n" + "This may be caused by:\n" + "* A missing or faulty graphics driver installation.\n" + " Blender needs a graphics card driver to work correctly.\n" + "* Accessing Blender through a remote connection.\n" + "* Using Blender through a virtual machine.\n\n" + "Disable this message in <User Preferences - Interface - Warn On Deprecated OpenGL>", + "Blender - Can't detect 3D hardware accelerated Driver!", MB_OK | MB_ICONWARNING); + } + else if (version[0] == '1' && version[2] < '4') { + MessageBox(m_hWnd, "The OpenGL version provided by your graphics driver version is too low\n" + "Blender requires version 1.4 and may not work correctly\n\n" + "Disable this message in <User Preferences - Interface - Warn On Deprecated OpenGL>", + "Blender - Unsupported Graphics Driver!", MB_OK | MB_ICONWARNING); + } + s_warn_old = true; + } + + return GHOST_kSuccess; +} + + +GHOST_TSuccess GHOST_ContextWGL::releaseNativeHandles() +{ + GHOST_TSuccess success = m_hGLRC != s_sharedHGLRC || s_sharedCount == 1 ? GHOST_kSuccess : GHOST_kFailure; + + m_hWnd = NULL; + m_hDC = NULL; + + return success; +} diff --git a/intern/ghost/intern/GHOST_ContextWGL.h b/intern/ghost/intern/GHOST_ContextWGL.h new file mode 100644 index 00000000000..c457ddab2f7 --- /dev/null +++ b/intern/ghost/intern/GHOST_ContextWGL.h @@ -0,0 +1,190 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2013 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Jason Wilkins + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file ghost/intern/GHOST_ContextWGL.h + * \ingroup GHOST + */ + +#ifndef __GHOST_CONTEXTWGL_H__ +#define __GHOST_CONTEXTWGL_H__ + +#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 + + +class GHOST_ContextWGL : public GHOST_Context +{ +public: + /** + * Constructor. + */ + GHOST_ContextWGL( + bool stereoVisual, + GHOST_TUns16 numOfAASamples, + HWND hWnd, + HDC hDC, + int contextProfileMask, + int contextMajorVersion, + int contextMinorVersion, + int contextFlags, + int contextResetNotificationStrategy); + + /** + * Destructor. + */ + ~GHOST_ContextWGL(); + + /** + * Swaps front and back buffers of a window. + * \return A boolean success indicator. + */ + GHOST_TSuccess swapBuffers(); + + /** + * Activates the drawing context of this window. + * \return A boolean success indicator. + */ + GHOST_TSuccess activateDrawingContext(); + + /** + * Call immediately after new to initialize. If this fails then immediately delete the object. + * \return Indication as to whether initialization has succeeded. + */ + GHOST_TSuccess initializeDrawingContext(); + + /** + * Removes references to native handles from this context and then returns + * \return GHOST_kSuccess if it is OK for the parent to release the handles and + * GHOST_kFailure if releasing the handles will interfere with sharing + */ + GHOST_TSuccess releaseNativeHandles(); + + /** + * Sets the swap interval for swapBuffers. + * \param interval The swap interval to use. + * \return A boolean success indicator. + */ + GHOST_TSuccess setSwapInterval(int interval); + + /** + * Gets the current swap interval for swapBuffers. + * \param intervalOut Variable to store the swap interval if it can be read. + * \return Whether the swap interval can be read. + */ + GHOST_TSuccess getSwapInterval(int &intervalOut); + + static void unSetWarningOld(){s_warn_old = true;} + +protected: + inline void activateWGLEW() const { +#ifdef WITH_GLEW_MX + wglewContext = m_wglewContext; +#endif + } + +private: + int choose_pixel_format( + bool stereoVisual, + int numOfAASamples, + bool needAlpha, + bool needStencil, + bool sRGB); + + int choose_pixel_format_arb( + bool stereoVisual, + int numOfAASamples, + bool needAlpha, + bool needStencil, + bool sRGB); + + int _choose_pixel_format_arb_1( + bool stereoVisual, + int numOfAASamples, + bool needAlpha, + bool needStencil, + bool sRGB, + int *swapMethodOut); + + int _choose_pixel_format_arb_2(bool stereoVisual, + int *numOfAASamples, + bool needAlpha, + bool needStencil, + bool sRGB, + int swapMethod); + + void initContextWGLEW(PIXELFORMATDESCRIPTOR &preferredPFD); + + HWND m_hWnd; + HDC m_hDC; + + const int m_contextProfileMask; + const int m_contextMajorVersion; + const int m_contextMinorVersion; + const int m_contextFlags; + 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; + const char *m_dummyVersion; +#endif + + static HGLRC s_sharedHGLRC; + static int s_sharedCount; + + static bool s_singleContextMode; + static bool s_warn_old; +}; + +#endif // __GHOST_CONTEXTWGL_H__ diff --git a/intern/ghost/intern/GHOST_Debug.h b/intern/ghost/intern/GHOST_Debug.h index 9292235a9c7..db49627b378 100644 --- a/intern/ghost/intern/GHOST_Debug.h +++ b/intern/ghost/intern/GHOST_Debug.h @@ -80,7 +80,7 @@ } \ } (void)0 #else // GHOST_DEBUG -# define GHOST_ASSERT(x, info) +# define GHOST_ASSERT(x, info) ((void)0) #endif // GHOST_DEBUG #endif // __GHOST_DEBUG_H__ diff --git a/intern/ghost/intern/GHOST_DisplayManagerCocoa.h b/intern/ghost/intern/GHOST_DisplayManagerCocoa.h index 1f17b4dd976..f580820d8f6 100644 --- a/intern/ghost/intern/GHOST_DisplayManagerCocoa.h +++ b/intern/ghost/intern/GHOST_DisplayManagerCocoa.h @@ -58,15 +58,15 @@ public: * \param numDisplays The number of displays on this system. * \return Indication of success. */ - virtual GHOST_TSuccess getNumDisplays(GHOST_TUns8& numDisplays) const; + GHOST_TSuccess getNumDisplays(GHOST_TUns8& numDisplays) const; /** * Returns the number of display settings for this display device. * \param display The index of the display to query with 0 <= display < getNumDisplays(). - * \param setting The number of settings of the display device with this index. + * \param numSetting: The number of settings of the display device with this index. * \return Indication of success. */ - virtual GHOST_TSuccess getNumDisplaySettings(GHOST_TUns8 display, GHOST_TInt32& numSettings) const; + GHOST_TSuccess getNumDisplaySettings(GHOST_TUns8 display, GHOST_TInt32& numSettings) const; /** * Returns the current setting for this display device. @@ -75,7 +75,7 @@ public: * \param setting The setting of the display device with this index. * \return Indication of success. */ - virtual GHOST_TSuccess getDisplaySetting(GHOST_TUns8 display, GHOST_TInt32 index, GHOST_DisplaySetting& setting) const; + GHOST_TSuccess getDisplaySetting(GHOST_TUns8 display, GHOST_TInt32 index, GHOST_DisplaySetting& setting) const; /** * Returns the current setting for this display device. @@ -83,7 +83,7 @@ public: * \param setting The current setting of the display device with this index. * \return Indication of success. */ - virtual GHOST_TSuccess getCurrentDisplaySetting(GHOST_TUns8 display, GHOST_DisplaySetting& setting) const; + GHOST_TSuccess getCurrentDisplaySetting(GHOST_TUns8 display, GHOST_DisplaySetting& setting) const; /** * Changes the current setting for this display device. @@ -91,7 +91,7 @@ public: * \param setting The current setting of the display device with this index. * \return Indication of success. */ - virtual GHOST_TSuccess setCurrentDisplaySetting(GHOST_TUns8 display, const GHOST_DisplaySetting& setting); + GHOST_TSuccess setCurrentDisplaySetting(GHOST_TUns8 display, const GHOST_DisplaySetting& setting); protected: //Do not cache values as OS X supports screen hot plug diff --git a/intern/ghost/intern/GHOST_DisplayManagerWin32.h b/intern/ghost/intern/GHOST_DisplayManagerWin32.h index ced4ceac044..ff8849f03c9 100644 --- a/intern/ghost/intern/GHOST_DisplayManagerWin32.h +++ b/intern/ghost/intern/GHOST_DisplayManagerWin32.h @@ -58,15 +58,15 @@ public: * \param numDisplays The number of displays on this system. * \return Indication of success. */ - virtual GHOST_TSuccess getNumDisplays(GHOST_TUns8& numDisplays) const; + GHOST_TSuccess getNumDisplays(GHOST_TUns8& numDisplays) const; /** * Returns the number of display settings for this display device. * \param display The index of the display to query with 0 <= display < getNumDisplays(). - * \param setting The number of settings of the display device with this index. + * \param numSetting: The number of settings of the display device with this index. * \return Indication of success. */ - virtual GHOST_TSuccess getNumDisplaySettings(GHOST_TUns8 display, GHOST_TInt32& numSettings) const; + GHOST_TSuccess getNumDisplaySettings(GHOST_TUns8 display, GHOST_TInt32& numSettings) const; /** * Returns the current setting for this display device. @@ -75,7 +75,7 @@ public: * \param setting The setting of the display device with this index. * \return Indication of success. */ - virtual GHOST_TSuccess getDisplaySetting(GHOST_TUns8 display, GHOST_TInt32 index, GHOST_DisplaySetting& setting) const; + GHOST_TSuccess getDisplaySetting(GHOST_TUns8 display, GHOST_TInt32 index, GHOST_DisplaySetting& setting) const; /** * Returns the current setting for this display device. @@ -83,7 +83,7 @@ public: * \param setting The current setting of the display device with this index. * \return Indication of success. */ - virtual GHOST_TSuccess getCurrentDisplaySetting(GHOST_TUns8 display, GHOST_DisplaySetting& setting) const; + GHOST_TSuccess getCurrentDisplaySetting(GHOST_TUns8 display, GHOST_DisplaySetting& setting) const; /** * Changes the current setting for this display device. @@ -91,7 +91,7 @@ public: * \param setting The current setting of the display device with this index. * \return Indication of success. */ - virtual GHOST_TSuccess setCurrentDisplaySetting(GHOST_TUns8 display, const GHOST_DisplaySetting& setting); + GHOST_TSuccess setCurrentDisplaySetting(GHOST_TUns8 display, const GHOST_DisplaySetting& setting); protected: }; diff --git a/intern/ghost/intern/GHOST_DisplayManagerX11.cpp b/intern/ghost/intern/GHOST_DisplayManagerX11.cpp index 24289e6b006..b3e6f2e53fa 100644 --- a/intern/ghost/intern/GHOST_DisplayManagerX11.cpp +++ b/intern/ghost/intern/GHOST_DisplayManagerX11.cpp @@ -90,6 +90,7 @@ getNumDisplaySettings( numSettings = 1; #endif + (void) display; return GHOST_kSuccess; } @@ -116,6 +117,8 @@ getDisplaySetting( if (dpy == NULL) return GHOST_kFailure; + (void)display; + #ifdef WITH_X11_XF86VMODE int majorVersion, minorVersion; @@ -146,6 +149,7 @@ getDisplaySetting( GHOST_ASSERT(display < 1, "Only single display systems are currently supported.\n"); GHOST_ASSERT(index < 1, "Requested setting outside of valid range.\n"); + (void)index; setting.xPixels = DisplayWidth(dpy, DefaultScreen(dpy)); setting.yPixels = DisplayHeight(dpy, DefaultScreen(dpy)); @@ -171,7 +175,7 @@ getCurrentDisplaySetting( GHOST_TSuccess GHOST_DisplayManagerX11:: setCurrentDisplaySetting( - GHOST_TUns8 display, + GHOST_TUns8 /*display*/, const GHOST_DisplaySetting& setting) { #ifdef WITH_X11_XF86VMODE @@ -264,6 +268,8 @@ setCurrentDisplaySetting( return GHOST_kSuccess; #else + (void)setting; + /* Just pretend the request was successful. */ return GHOST_kSuccess; #endif diff --git a/intern/ghost/intern/GHOST_DisplayManagerX11.h b/intern/ghost/intern/GHOST_DisplayManagerX11.h index 66fd1e5c15e..b54c53c67bd 100644 --- a/intern/ghost/intern/GHOST_DisplayManagerX11.h +++ b/intern/ghost/intern/GHOST_DisplayManagerX11.h @@ -66,7 +66,7 @@ public: /** * Returns the number of display settings for this display device. * \param display The index of the display to query with 0 <= display < getNumDisplays(). - * \param setting The number of settings of the display device with this index. + * \param numSetting: The number of settings of the display device with this index. * \return Indication of success. */ GHOST_TSuccess diff --git a/intern/ghost/intern/GHOST_DropTargetWin32.h b/intern/ghost/intern/GHOST_DropTargetWin32.h index fb85feffde0..56bae1fd1b2 100644 --- a/intern/ghost/intern/GHOST_DropTargetWin32.h +++ b/intern/ghost/intern/GHOST_DropTargetWin32.h @@ -89,7 +89,7 @@ public: * Destructor * Do NOT destroy directly. Use Release() instead to make COM happy. */ - virtual ~GHOST_DropTargetWin32(); + ~GHOST_DropTargetWin32(); private: diff --git a/intern/ghost/intern/GHOST_DropTargetX11.cpp b/intern/ghost/intern/GHOST_DropTargetX11.cpp index 639fd503759..0efc8a78df5 100644 --- a/intern/ghost/intern/GHOST_DropTargetX11.cpp +++ b/intern/ghost/intern/GHOST_DropTargetX11.cpp @@ -132,7 +132,8 @@ void GHOST_DropTargetX11::UrlDecode(char *decodedOut, int bufferSize, const char unsigned int i; unsigned int len = strlen(encodedIn); DecodeState_e state = STATE_SEARCH; - int j, asciiCharacter; + int j; + unsigned int asciiCharacter; char tempNumBuf[3] = {0}; bool bothDigits = true; diff --git a/intern/ghost/intern/GHOST_Event.h b/intern/ghost/intern/GHOST_Event.h index ea4d603be83..6a715498b87 100644 --- a/intern/ghost/intern/GHOST_Event.h +++ b/intern/ghost/intern/GHOST_Event.h @@ -59,7 +59,7 @@ public: * Returns the event type. * \return The event type. */ - virtual GHOST_TEventType getType() + GHOST_TEventType getType() { return m_type; } @@ -68,7 +68,7 @@ public: * Returns the time this event was generated. * \return The event generation time. */ - virtual GHOST_TUns64 getTime() + GHOST_TUns64 getTime() { return m_time; } @@ -78,7 +78,7 @@ public: * or NULL if it is a 'system' event. * \return The generating window. */ - virtual GHOST_IWindow *getWindow() + GHOST_IWindow *getWindow() { return m_window; } @@ -87,7 +87,7 @@ public: * Returns the event data. * \return The event data. */ - virtual GHOST_TEventDataPtr getData() + GHOST_TEventDataPtr getData() { return m_data; } diff --git a/intern/ghost/intern/GHOST_EventButton.h b/intern/ghost/intern/GHOST_EventButton.h index f88fe6e201d..ef5c5efa202 100644 --- a/intern/ghost/intern/GHOST_EventButton.h +++ b/intern/ghost/intern/GHOST_EventButton.h @@ -47,9 +47,8 @@ public: * Constructor. * \param time The time this event was generated. * \param type The type of this event. - * \param x The x-coordinate of the location the cursor was at at the time of the event. - * \param y The y-coordinate of the location the cursor was at at the time of the event. - * \param buttons The state of the buttons was at at the time of the event. + * \param window: The window of this event. + * \param button: The state of the buttons were at at the time of the event. */ GHOST_EventButton(GHOST_TUns64 time, GHOST_TEventType type, GHOST_IWindow *window, GHOST_TButtonMask button) : GHOST_Event(time, type, window) diff --git a/intern/ghost/intern/GHOST_EventDragnDrop.h b/intern/ghost/intern/GHOST_EventDragnDrop.h index c51f9568087..b7bf37c99d8 100644 --- a/intern/ghost/intern/GHOST_EventDragnDrop.h +++ b/intern/ghost/intern/GHOST_EventDragnDrop.h @@ -112,6 +112,7 @@ public: for (i = 0; i < strArray->count; i++) free(strArray->strings[i]); + free(strArray->strings); free(strArray); } break; diff --git a/intern/ghost/intern/GHOST_EventManager.h b/intern/ghost/intern/GHOST_EventManager.h index 8e513b4ee76..c8b5d1debe5 100644 --- a/intern/ghost/intern/GHOST_EventManager.h +++ b/intern/ghost/intern/GHOST_EventManager.h @@ -58,27 +58,27 @@ public: /** * Destructor. */ - virtual ~GHOST_EventManager(); + ~GHOST_EventManager(); /** * Returns the number of events currently on the stack. * \return The number of events on the stack. */ - virtual GHOST_TUns32 getNumEvents(); + GHOST_TUns32 getNumEvents(); /** * Returns the number of events of a certain type currently on the stack. * \param type The type of events to be counted. * \return The number of events on the stack of this type. */ - virtual GHOST_TUns32 getNumEvents(GHOST_TEventType type); + GHOST_TUns32 getNumEvents(GHOST_TEventType type); /** * Return the event at the top of the stack without removal. * Do not delete the event! * \return The event at the top of the stack. */ - virtual GHOST_IEvent *peekEvent(); + GHOST_IEvent *peekEvent(); /** * Pushes an event on the stack. @@ -86,47 +86,47 @@ public: * Do not delete the event! * \param event The event to push on the stack. */ - virtual GHOST_TSuccess pushEvent(GHOST_IEvent *event); + GHOST_TSuccess pushEvent(GHOST_IEvent *event); /** * Dispatches the given event directly, bypassing the event stack. * \return Indication as to whether any of the consumers handled the event. */ - virtual bool dispatchEvent(GHOST_IEvent *event); + bool dispatchEvent(GHOST_IEvent *event); /** * Dispatches the event at the back of the stack. * The event will be removed from the stack. * \return Indication as to whether any of the consumers handled the event. */ - virtual bool dispatchEvent(); + bool dispatchEvent(); /** * Dispatches all the events on the stack. * The event stack will be empty afterwards. * \return Indication as to whether any of the consumers handled the events. */ - virtual bool dispatchEvents(); + bool dispatchEvents(); /** * Adds a consumer to the list of event consumers. * \param consumer The consumer added to the list. * \return Indication as to whether addition has succeeded. */ - virtual GHOST_TSuccess addConsumer(GHOST_IEventConsumer *consumer); + GHOST_TSuccess addConsumer(GHOST_IEventConsumer *consumer); /** * Removes a consumer from the list of event consumers. * \param consumer The consumer removed from the list. * \return Indication as to whether removal has succeeded. */ - virtual GHOST_TSuccess removeConsumer(GHOST_IEventConsumer *consumer); + GHOST_TSuccess removeConsumer(GHOST_IEventConsumer *consumer); /** * Removes all events for a window from the stack. * \param window The window to remove events for. */ - virtual void + void removeWindowEvents( GHOST_IWindow *window ); @@ -138,7 +138,7 @@ public: * \param type The type of events to be removed. * \param window The window to remove the events for. */ - virtual void + void removeTypeEvents( GHOST_TEventType type, GHOST_IWindow *window = NULL @@ -150,12 +150,12 @@ protected: * Delete the event after use! * \return The event at the top of the stack. */ - virtual GHOST_IEvent *popEvent(); + GHOST_IEvent *popEvent(); /** * Removes all events from the stack. */ - virtual void disposeEvents(); + void disposeEvents(); /** A stack with events. */ typedef std::deque<GHOST_IEvent *> TEventStack; diff --git a/intern/ghost/intern/GHOST_EventPrinter.cpp b/intern/ghost/intern/GHOST_EventPrinter.cpp index 4b7be84ac81..f25f6637cb1 100644 --- a/intern/ghost/intern/GHOST_EventPrinter.cpp +++ b/intern/ghost/intern/GHOST_EventPrinter.cpp @@ -179,6 +179,9 @@ bool GHOST_EventPrinter::processEvent(GHOST_IEvent *event) std::cout << "not found"; handled = false; break; } + + std::cout.flush(); + return handled; } diff --git a/intern/ghost/intern/GHOST_EventPrinter.h b/intern/ghost/intern/GHOST_EventPrinter.h index 280d4208413..0cbb7ca467d 100644 --- a/intern/ghost/intern/GHOST_EventPrinter.h +++ b/intern/ghost/intern/GHOST_EventPrinter.h @@ -49,7 +49,7 @@ public: * \param event The event that can be handled or not. * \return Indication as to whether the event was handled. */ - virtual bool processEvent(GHOST_IEvent *event); + bool processEvent(GHOST_IEvent *event); protected: /** diff --git a/intern/ghost/intern/GHOST_EventTrackpad.h b/intern/ghost/intern/GHOST_EventTrackpad.h index dc8b1649754..b4e80b5fd06 100644 --- a/intern/ghost/intern/GHOST_EventTrackpad.h +++ b/intern/ghost/intern/GHOST_EventTrackpad.h @@ -45,7 +45,7 @@ public: /** * Constructor. * \param msec The time this event was generated. - * \param type The type of this event. + * \param window: The window of this event. * \param subtype The subtype of the event. * \param x The x-delta of the pan event. * \param y The y-delta of the pan event. diff --git a/intern/ghost/intern/GHOST_EventWheel.h b/intern/ghost/intern/GHOST_EventWheel.h index 216f13fda4e..a632492acb9 100644 --- a/intern/ghost/intern/GHOST_EventWheel.h +++ b/intern/ghost/intern/GHOST_EventWheel.h @@ -47,9 +47,9 @@ class GHOST_EventWheel : public GHOST_Event public: /** * Constructor. - * \param msec The time this event was generated. - * \param type The type of this event. - * \param z The displacement of the mouse wheel. + * \param msec: The time this event was generated. + * \param window: The window of this event. + * \param z: The displacement of the mouse wheel. */ GHOST_EventWheel(GHOST_TUns64 msec, GHOST_IWindow *window, GHOST_TInt32 z) : GHOST_Event(msec, GHOST_kEventWheel, window) diff --git a/intern/ghost/intern/GHOST_ISystem.cpp b/intern/ghost/intern/GHOST_ISystem.cpp index 009753cb29e..d7dd2b1cde1 100644 --- a/intern/ghost/intern/GHOST_ISystem.cpp +++ b/intern/ghost/intern/GHOST_ISystem.cpp @@ -38,21 +38,22 @@ #include "GHOST_ISystem.h" -#ifdef WITH_HEADLESS -# include "GHOST_SystemNULL.h" -#elif defined(WITH_GHOST_SDL) -# include "GHOST_SystemSDL.h" -#elif defined(WIN32) -# include "GHOST_SystemWin32.h" +#ifdef WITH_X11 +# include "GHOST_SystemX11.h" #else -# ifdef __APPLE__ -# include "GHOST_SystemCocoa.h" +# ifdef WITH_HEADLESS +# include "GHOST_SystemNULL.h" +# elif defined(WITH_GHOST_SDL) +# include "GHOST_SystemSDL.h" +# elif defined(WIN32) +# include "GHOST_SystemWin32.h" # else -# include "GHOST_SystemX11.h" +# ifdef __APPLE__ +# include "GHOST_SystemCocoa.h" +# endif # endif #endif - GHOST_ISystem *GHOST_ISystem::m_system = 0; @@ -60,19 +61,21 @@ GHOST_TSuccess GHOST_ISystem::createSystem() { GHOST_TSuccess success; if (!m_system) { -#ifdef WITH_HEADLESS +#ifdef WITH_X11 + m_system = new GHOST_SystemX11(); +#else +# ifdef WITH_HEADLESS m_system = new GHOST_SystemNULL(); -#elif defined(WITH_GHOST_SDL) +# elif defined(WITH_GHOST_SDL) m_system = new GHOST_SystemSDL(); -#elif defined(WIN32) +# elif defined(WIN32) m_system = new GHOST_SystemWin32(); -#else -# ifdef __APPLE__ - m_system = new GHOST_SystemCocoa(); # else - m_system = new GHOST_SystemX11(); +# ifdef __APPLE__ + m_system = new GHOST_SystemCocoa(); +# endif # endif -#endif +#endif success = m_system != 0 ? GHOST_kSuccess : GHOST_kFailure; } else { diff --git a/intern/ghost/intern/GHOST_ImeWin32.cpp b/intern/ghost/intern/GHOST_ImeWin32.cpp new file mode 100644 index 00000000000..af5a2ed7097 --- /dev/null +++ b/intern/ghost/intern/GHOST_ImeWin32.cpp @@ -0,0 +1,520 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (c) 2010 The Chromium Authors. All rights reserved. + * All rights reserved. + * + * The Original Code is: some of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + + +/** \file ghost/intern/GHOST_ImeWin32.cpp + * \ingroup GHOST + */ + +#ifdef WITH_INPUT_IME + +#include "GHOST_C-api.h" +#include "GHOST_ImeWin32.h" +#include "GHOST_WindowWin32.h" +#include "utfconv.h" + + +GHOST_ImeWin32::GHOST_ImeWin32() + : ime_status_(false), + input_language_id_(LANG_USER_DEFAULT), + is_composing_(false), + system_caret_(false), + caret_rect_(-1, -1, 0, 0), + is_first(true), + is_enable(true) +{ +} + + +GHOST_ImeWin32::~GHOST_ImeWin32() +{ +} + + +bool GHOST_ImeWin32::SetInputLanguage() +{ + /** + * Retrieve the current keyboard layout from Windows and determine whether + * or not the current input context has IMEs. + * Also save its input language for language-specific operations required + * while composing a text. + */ + HKL keyboard_layout = ::GetKeyboardLayout(0); + input_language_id_ = reinterpret_cast<LANGID>(keyboard_layout); + ime_status_ = ::ImmIsIME(keyboard_layout); + return ime_status_; +} + + +void GHOST_ImeWin32::CreateImeWindow(HWND window_handle) +{ + /** + * When a user disables TSF (Text Service Framework) and CUAS (Cicero + * Unaware Application Support), Chinese IMEs somehow ignore function calls + * to ::ImmSetCandidateWindow(), i.e. they do not move their candidate + * window to the position given as its parameters, and use the position + * of the current system caret instead, i.e. it uses ::GetCaretPos() to + * retrieve the position of their IME candidate window. + * Therefore, we create a temporary system caret for Chinese IMEs and use + * it during this input context. + * Since some third-party Japanese IME also uses ::GetCaretPos() to determine + * their window position, we also create a caret for Japanese IMEs. + */ + if (PRIMARYLANGID(input_language_id_) == LANG_CHINESE || + PRIMARYLANGID(input_language_id_) == LANG_JAPANESE) { + if (!system_caret_) { + if (::CreateCaret(window_handle, NULL, 1, 1)) { + system_caret_ = true; + } + } + } + /* Restore the positions of the IME windows. */ + UpdateImeWindow(window_handle); +} + + +void GHOST_ImeWin32::SetImeWindowStyle(HWND window_handle, UINT message, WPARAM wparam, LPARAM lparam, BOOL *handled) +{ + /** + * To prevent the IMM (Input Method Manager) from displaying the IME + * composition window, Update the styles of the IME windows and EXPLICITLY + * call ::DefWindowProc() here. + * NOTE(hbono): We can NEVER let WTL call ::DefWindowProc() when we update + * the styles of IME windows because the 'lparam' variable is a local one + * and all its updates disappear in returning from this function, i.e. WTL + * does not call ::DefWindowProc() with our updated 'lparam' value but call + * the function with its original value and over-writes our window styles. + */ + *handled = TRUE; + lparam &= ~ISC_SHOWUICOMPOSITIONWINDOW; + ::DefWindowProc(window_handle, message, wparam, lparam); +} + + +void GHOST_ImeWin32::DestroyImeWindow(HWND window_handle) +{ + /* Destroy the system caret if we have created for this IME input context. */ + if (system_caret_) { + ::DestroyCaret(); + system_caret_ = false; + } +} + + +void GHOST_ImeWin32::MoveImeWindow(HWND window_handle, HIMC imm_context) +{ + int x = caret_rect_.m_l; + int y = caret_rect_.m_t; + const int kCaretMargin = 1; + /** + * As written in a comment in GHOST_ImeWin32::CreateImeWindow(), + * Chinese IMEs ignore function calls to ::ImmSetCandidateWindow() + * when a user disables TSF (Text Service Framework) and CUAS (Cicero + * Unaware Application Support). + * On the other hand, when a user enables TSF and CUAS, Chinese IMEs + * ignore the position of the current system caret and uses the + * parameters given to ::ImmSetCandidateWindow() with its 'dwStyle' + * parameter CFS_CANDIDATEPOS. + * Therefore, we do not only call ::ImmSetCandidateWindow() but also + * set the positions of the temporary system caret if it exists. + */ + CANDIDATEFORM candidate_position = { 0, CFS_CANDIDATEPOS, { x, y }, + { 0, 0, 0, 0 } }; + ::ImmSetCandidateWindow(imm_context, &candidate_position); + if (system_caret_) { + switch (PRIMARYLANGID(input_language_id_)) { + case LANG_JAPANESE: + ::SetCaretPos(x, y + caret_rect_.getHeight()); + break; + default: + ::SetCaretPos(x, y); + break; + } + } + if (PRIMARYLANGID(input_language_id_) == LANG_KOREAN) { + /** + * Chinese IMEs and Japanese IMEs require the upper-left corner of + * the caret to move the position of their candidate windows. + * On the other hand, Korean IMEs require the lower-left corner of the + * caret to move their candidate windows. + */ + y += kCaretMargin; + } + /** + * Japanese IMEs and Korean IMEs also use the rectangle given to + * ::ImmSetCandidateWindow() with its 'dwStyle' parameter CFS_EXCLUDE + * to move their candidate windows when a user disables TSF and CUAS. + * Therefore, we also set this parameter here. + */ + CANDIDATEFORM exclude_rectangle = { 0, CFS_EXCLUDE, { x, y }, + { x, y, x + caret_rect_.getWidth(), y + caret_rect_.getHeight() } }; + ::ImmSetCandidateWindow(imm_context, &exclude_rectangle); +} + + +void GHOST_ImeWin32::UpdateImeWindow(HWND window_handle) +{ + /* Just move the IME window attached to the given window. */ + if (caret_rect_.m_l >= 0 && caret_rect_.m_t >= 0) { + HIMC imm_context = ::ImmGetContext(window_handle); + if (imm_context) { + MoveImeWindow(window_handle, imm_context); + ::ImmReleaseContext(window_handle, imm_context); + } + } +} + + +void GHOST_ImeWin32::CleanupComposition(HWND window_handle) +{ + /** + * Notify the IMM attached to the given window to complete the ongoing + * composition, (this case happens when the given window is de-activated + * while composing a text and re-activated), and reset the omposition status. + */ + if (is_composing_) { + HIMC imm_context = ::ImmGetContext(window_handle); + if (imm_context) { + ::ImmNotifyIME(imm_context, NI_COMPOSITIONSTR, CPS_COMPLETE, 0); + ::ImmReleaseContext(window_handle, imm_context); + } + ResetComposition(window_handle); + } +} + + +void GHOST_ImeWin32::CheckFirst(HWND window_handle) +{ + if (is_first) { + this->EndIME(window_handle); + is_first = false; + } +} + + +void GHOST_ImeWin32::ResetComposition(HWND window_handle) +{ + /* Currently, just reset the composition status. */ + is_composing_ = false; +} + + +void GHOST_ImeWin32::CompleteComposition(HWND window_handle, HIMC imm_context) +{ + /** + * We have to confirm there is an ongoing composition before completing it. + * This is for preventing some IMEs from getting confused while completing an + * ongoing composition even if they do not have any ongoing compositions.) + */ + if (is_composing_) { + ::ImmNotifyIME(imm_context, NI_COMPOSITIONSTR, CPS_COMPLETE, 0); + ResetComposition(window_handle); + } +} + + +void GHOST_ImeWin32::GetCaret(HIMC imm_context, LPARAM lparam, ImeComposition *composition) +{ + /** + * This operation is optional and language-dependent because the caret + * style is depended on the language, e.g.: + * * Korean IMEs: the caret is a blinking block, + * (It contains only one hangul character); + * * Chinese IMEs: the caret is a blinking line, + * (i.e. they do not need to retrieve the target selection); + * * Japanese IMEs: the caret is a selection (or underlined) block, + * (which can contain one or more Japanese characters). + */ + int target_start = -1; + int target_end = -1; + switch (PRIMARYLANGID(input_language_id_)) { + case LANG_KOREAN: + if (lparam & CS_NOMOVECARET) { + target_start = 0; + target_end = 1; + } + break; + case LANG_CHINESE: + { + int clause_size = ImmGetCompositionStringW(imm_context, GCS_COMPCLAUSE, NULL, 0); + if (clause_size) { + static std::vector<unsigned long> clauses; + clause_size = clause_size / sizeof(clauses[0]); + clauses.resize(clause_size); + ImmGetCompositionStringW(imm_context, GCS_COMPCLAUSE, &clauses[0], + sizeof(clauses[0]) *clause_size); + if (composition->cursor_position == composition->ime_string.size()) { + target_start = clauses[clause_size - 2]; + target_end = clauses[clause_size - 1]; + } + else { + for (int i = 0; i < clause_size - 1; i++) { + if (clauses[i] == composition->cursor_position) { + target_start = clauses[i]; + target_end = clauses[i + 1]; + break; + } + } + } + } + else { + if (composition->cursor_position != -1) { + target_start = composition->cursor_position; + target_end = composition->ime_string.size(); + } + } + break; + } + case LANG_JAPANESE: + + /** + * For Japanese IMEs, the robustest way to retrieve the caret + * is scanning the attribute of the latest composition string and + * retrieving the begining and the end of the target clause, i.e. + * a clause being converted. + */ + if (lparam & GCS_COMPATTR) { + int attribute_size = ::ImmGetCompositionStringW(imm_context, + GCS_COMPATTR, + NULL, 0); + if (attribute_size > 0) { + char *attribute_data = new char[attribute_size]; + if (attribute_data) { + ::ImmGetCompositionStringW(imm_context, GCS_COMPATTR, + attribute_data, attribute_size); + for (target_start = 0; target_start < attribute_size; + ++target_start) { + if (IsTargetAttribute(attribute_data[target_start])) + break; + } + for (target_end = target_start; target_end < attribute_size; + ++target_end) { + if (!IsTargetAttribute(attribute_data[target_end])) + break; + } + if (target_start == attribute_size) { + /** + * This composition clause does not contain any target clauses, + * i.e. this clauses is an input clause. + * We treat whole this clause as a target clause. + */ + target_end = target_start; + target_start = 0; + } + if (target_start != -1 && target_start < attribute_size && + attribute_data[target_start] == ATTR_TARGET_NOTCONVERTED) + { + composition->cursor_position = target_start; + } + } + delete[] attribute_data; + } + } + break; + } + composition->target_start = target_start; + composition->target_end = target_end; +} + + +bool GHOST_ImeWin32::GetString(HIMC imm_context, WPARAM lparam, int type, ImeComposition *composition) +{ + bool result = false; + if (lparam & type) { + int string_size = ::ImmGetCompositionStringW(imm_context, type, NULL, 0); + if (string_size > 0) { + int string_length = string_size / sizeof(wchar_t); + wchar_t *string_data = new wchar_t[string_length + 1]; + string_data[string_length] = '\0'; + if (string_data) { + /* Fill the given ImeComposition object. */ + ::ImmGetCompositionStringW(imm_context, type, + string_data, string_size); + composition->string_type = type; + composition->ime_string = string_data; + result = true; + } + delete[] string_data; + } + } + return result; +} + + +bool GHOST_ImeWin32::GetResult(HWND window_handle, LPARAM lparam, ImeComposition *composition) +{ + bool result = false; + HIMC imm_context = ::ImmGetContext(window_handle); + if (imm_context) { + /* Copy the result string to the ImeComposition object. */ + result = GetString(imm_context, lparam, GCS_RESULTSTR, composition); + /** + * Reset all the other parameters because a result string does not + * have composition attributes. + */ + composition->cursor_position = -1; + composition->target_start = -1; + composition->target_end = -1; + ::ImmReleaseContext(window_handle, imm_context); + } + return result; +} + + +bool GHOST_ImeWin32::GetComposition(HWND window_handle, LPARAM lparam, ImeComposition *composition) +{ + bool result = false; + HIMC imm_context = ::ImmGetContext(window_handle); + if (imm_context) { + /* Copy the composition string to the ImeComposition object. */ + result = GetString(imm_context, lparam, GCS_COMPSTR, composition); + + /* Retrieve the cursor position in the IME composition. */ + int cursor_position = ::ImmGetCompositionStringW(imm_context, GCS_CURSORPOS, NULL, 0); + composition->cursor_position = cursor_position; + composition->target_start = -1; + composition->target_end = -1; + + /* Retrieve the target selection and Update the ImeComposition object. */ + GetCaret(imm_context, lparam, composition); + + /* Mark that there is an ongoing composition. */ + is_composing_ = true; + + ::ImmReleaseContext(window_handle, imm_context); + } + return result; +} + + +void GHOST_ImeWin32::EndIME(HWND window_handle) +{ + /** + * A renderer process have moved its input focus to a password input + * when there is an ongoing composition, e.g. a user has clicked a + * mouse button and selected a password input while composing a text. + * For this case, we have to complete the ongoing composition and + * clean up the resources attached to this object BEFORE DISABLING THE IME. + */ + if (!is_enable) return; + is_enable = false; + CleanupComposition(window_handle); + ::ImmAssociateContextEx(window_handle, NULL, 0); + eventImeData.composite_len = 0; +} + + +void GHOST_ImeWin32::BeginIME(HWND window_handle, const GHOST_Rect &caret_rect, bool complete) +{ + if (is_enable && complete) return; + is_enable = true; + /** + * Load the default IME context. + * NOTE(hbono) + * IMM ignores this call if the IME context is loaded. Therefore, we do + * not have to check whether or not the IME context is loaded. + */ + ::ImmAssociateContextEx(window_handle, NULL, IACE_DEFAULT); + /* Complete the ongoing composition and move the IME windows. */ + HIMC imm_context = ::ImmGetContext(window_handle); + if (imm_context) { + if (complete) { + /** + * A renderer process have moved its input focus to another edit + * control when there is an ongoing composition, e.g. a user has + * clicked a mouse button and selected another edit control while + * composing a text. + * For this case, we have to complete the ongoing composition and + * hide the IME windows BEFORE MOVING THEM. + */ + CompleteComposition(window_handle, imm_context); + } + /** + * Save the caret position, and Update the position of the IME window. + * This update is used for moving an IME window when a renderer process + * resize/moves the input caret. + */ + if (caret_rect.m_l >= 0 && caret_rect.m_t >= 0) { + caret_rect_ = caret_rect; + MoveImeWindow(window_handle, imm_context); + } + ::ImmReleaseContext(window_handle, imm_context); + } +} + + +static void convert_utf16_to_utf8_len(std::wstring s, int &len) +{ + if (len >= 0 && len <= s.size()) + len = count_utf_8_from_16(s.substr(0, len).c_str()) - 1; + else + len = -1; +} + + +static size_t updateUtf8Buf(ImeComposition &info) +{ + size_t len = count_utf_8_from_16(info.ime_string.c_str()); + info.utf8_buf.resize(len); + conv_utf_16_to_8(info.ime_string.c_str(), &info.utf8_buf[0], len); + convert_utf16_to_utf8_len(info.ime_string, info.cursor_position); + convert_utf16_to_utf8_len(info.ime_string, info.target_start); + convert_utf16_to_utf8_len(info.ime_string, info.target_end); + return len - 1; +} + + +void GHOST_ImeWin32::UpdateInfo(HWND window_handle) +{ + int res = this->GetResult(window_handle, GCS_RESULTSTR, &resultInfo); + int comp = this->GetComposition(window_handle, GCS_COMPSTR | GCS_COMPATTR, &compInfo); + /* convert wchar to utf8 */ + if (res) { + eventImeData.result_len = (GHOST_TUserDataPtr)updateUtf8Buf(resultInfo); + eventImeData.result = &resultInfo.utf8_buf[0]; + } + else { + eventImeData.result = 0; + eventImeData.result_len = 0; + } + if (comp) { + eventImeData.composite_len = (GHOST_TUserDataPtr)updateUtf8Buf(compInfo); + eventImeData.composite = &compInfo.utf8_buf[0]; + eventImeData.cursor_position = compInfo.cursor_position; + eventImeData.target_start = compInfo.target_start; + eventImeData.target_end = compInfo.target_end; + } + else { + eventImeData.composite = 0; + eventImeData.composite_len = 0; + eventImeData.cursor_position = -1; + eventImeData.target_start = -1; + eventImeData.target_end = -1; + } +} + +#endif // WITH_INPUT_IME diff --git a/intern/ghost/intern/GHOST_ImeWin32.h b/intern/ghost/intern/GHOST_ImeWin32.h new file mode 100644 index 00000000000..bfd9a49d785 --- /dev/null +++ b/intern/ghost/intern/GHOST_ImeWin32.h @@ -0,0 +1,405 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (c) 2010 The Chromium Authors. All rights reserved. + * All rights reserved. + * + * The Original Code is: some of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** +*/ + +/** \file ghost/intern/GHOST_ImeWin32.h + * \ingroup GHOST + */ + +#ifndef __GHOST_IME_H__ +#define __GHOST_IME_H__ + +#ifdef WITH_INPUT_IME + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +#include <string> + +#include "GHOST_Event.h" +#include "GHOST_Rect.h" +#include <vector> + +class GHOST_EventIME : public GHOST_Event +{ +public: + /** + * Constructor. + * \param msec The time this event was generated. + * \param type The type of key event. + * \param key The key code of the key. + */ + GHOST_EventIME(GHOST_TUns64 msec, + GHOST_TEventType type, + GHOST_IWindow *window, void *customdata) + : GHOST_Event(msec, type, window) + { + this->m_data = customdata; + } + +}; + + +/** + * This header file defines a struct and a class used for encapsulating IMM32 + * APIs, controls IMEs attached to a window, and enables the 'on-the-spot' + * input without deep knowledge about the APIs, i.e. knowledge about the + * language-specific and IME-specific behaviors. + * The following items enumerates the simplest steps for an (window) + * application to control its IMEs with the struct and the class defined + * this file. + * 1. Add an instance of the GHOST_ImeWin32 class to its window class. + * (The GHOST_ImeWin32 class needs a window handle.) + * 2. Add messages handlers listed in the following subsections, follow the + * instructions written in each subsection, and use the GHOST_ImeWin32 class. + * 2.1. WM_IME_SETCONTEXT (0x0281) + * Call the functions listed below: + * - GHOST_ImeWin32::CreateImeWindow(); + * - GHOST_ImeWin32::CleanupComposition(), and; + * - GHOST_ImeWin32::SetImeWindowStyle(). + * An application MUST prevent from calling ::DefWindowProc(). + * 2.2. WM_IME_STARTCOMPOSITION (0x010D) + * Call the functions listed below: + * - GHOST_ImeWin32::CreateImeWindow(), and; + * - GHOST_ImeWin32::ResetComposition(). + * An application MUST prevent from calling ::DefWindowProc(). + * 2.3. WM_IME_COMPOSITION (0x010F) + * Call the functions listed below: + * - GHOST_ImeWin32::UpdateImeWindow(); + * - GHOST_ImeWin32::GetResult(); + * - GHOST_ImeWin32::GetComposition(), and; + * - GHOST_ImeWin32::ResetComposition() (optional). + * An application MUST prevent from calling ::DefWindowProc(). + * 2.4. WM_IME_ENDCOMPOSITION (0x010E) + * Call the functions listed below: + * - GHOST_ImeWin32::ResetComposition(), and; + * - GHOST_ImeWin32::DestroyImeWindow(). + * An application CAN call ::DefWindowProc(). + * 2.5. WM_INPUTLANGCHANGE (0x0051) + * Call the functions listed below: + * - GHOST_ImeWin32::SetInputLanguage(). + * An application CAN call ::DefWindowProc(). + */ + +/* This struct represents the status of an ongoing composition. */ +struct ImeComposition { + /* Represents the cursor position in the IME composition. */ + int cursor_position; + + /* Represents the position of the beginning of the selection */ + int target_start; + + /* Represents the position of the end of the selection */ + int target_end; + + /** + * Represents the type of the string in the 'ime_string' parameter. + * Its possible values and description are listed below: + * Value Description + * 0 The parameter is not used. + * GCS_RESULTSTR The parameter represents a result string. + * GCS_COMPSTR The parameter represents a composition string. + */ + int string_type; + + /* Represents the string retrieved from IME (Input Method Editor) */ + std::wstring ime_string; + std::vector<char> utf8_buf; + std::vector<unsigned char> format; +}; + +/** + * This class controls the IMM (Input Method Manager) through IMM32 APIs and + * enables it to retrieve the string being controled by the IMM. (I wrote + * a note to describe the reason why I do not use 'IME' but 'IMM' below.) + * NOTE(hbono): + * Fortunately or unfortunately, TSF (Text Service Framework) and + * CUAS (Cicero Unaware Application Support) allows IMM32 APIs for + * retrieving not only the inputs from IMEs (Input Method Editors), used + * only for inputting East-Asian language texts, but also the ones from + * tablets (on Windows XP Tablet PC Edition and Windows Vista), voice + * recognizers (e.g. ViaVoice and Microsoft Office), etc. + * We can disable TSF and CUAS in Windows XP Tablet PC Edition. On the other + * hand, we can NEVER disable either TSF or CUAS in Windows Vista, i.e. + * THIS CLASS IS NOT ONLY USED ON THE INPUT CONTEXTS OF EAST-ASIAN + * LANGUAGES BUT ALSO USED ON THE INPUT CONTEXTS OF ALL LANGUAGES. + */ +class GHOST_ImeWin32 { +public: + GHOST_ImeWin32(); + ~GHOST_ImeWin32(); + + /* Retrieves whether or not there is an ongoing composition. */ + bool is_composing() const {return is_composing_;} + + /** + * Retrieves the input language from Windows and update it. + * Return values + * * true + * The given input language has IMEs. + * * false + * The given input language does not have IMEs. + */ + bool SetInputLanguage(); + + /** + * Create the IME windows, and allocate required resources for them. + * Parameters + * * window_handle [in] (HWND) + * Represents the window handle of the caller. + */ + void CreateImeWindow(HWND window_handle); + + /** + * Update the style of the IME windows. + * Parameters + * * window_handle [in] (HWND) + * Represents the window handle of the caller. + * * message [in] (UINT) + * * wparam [in] (WPARAM) + * * lparam [in] (LPARAM) + * Represent the windows message of the caller. + * These parameters are used for verifying if this function is called + * in a handler function for WM_IME_SETCONTEXT messages because this + * function uses ::DefWindowProc() to update the style. + * A caller just has to pass the input parameters for the handler + * function without modifications. + * * handled [out] (BOOL*) + * Returns ::DefWindowProc() is really called in this function. + * PLEASE DO NOT CALL ::DefWindowProc() IF THIS VALUE IS TRUE! + * All the window styles set in this function are over-written when + * calling ::DefWindowProc() after returning this function. + */ + void SetImeWindowStyle(HWND window_handle, UINT message, + WPARAM wparam, LPARAM lparam, BOOL* handled); + + /** + * Destroy the IME windows and all the resources attached to them. + * Parameters + * * window_handle [in] (HWND) + * Represents the window handle of the caller. + */ + void DestroyImeWindow(HWND window_handle); + + /** + * Update the position of the IME windows. + * Parameters + * * window_handle [in] (HWND) + * Represents the window handle of the caller. + */ + void UpdateImeWindow(HWND window_handle); + + /** + * Clean up the all resources attached to the given GHOST_ImeWin32 object, and + * reset its composition status. + * Parameters + * * window_handle [in] (HWND) + * Represents the window handle of the caller. + */ + void CleanupComposition(HWND window_handle); + + /** + * Reset the composition status. + * Cancel the ongoing composition if it exists. + * NOTE(hbono): This method does not release the allocated resources. + * Parameters + * * window_handle [in] (HWND) + * Represents the window handle of the caller. + */ + void ResetComposition(HWND window_handle); + + /** + * Retrieve a composition result of the ongoing composition if it exists. + * Parameters + * * window_handle [in] (HWND) + * Represents the window handle of the caller. + * * lparam [in] (LPARAM) + * Specifies the updated members of the ongoing composition, and must be + * the same parameter of a WM_IME_COMPOSITION message handler. + * This parameter is used for checking if the ongoing composition has + * its result string, + * * composition [out] (ImeComposition) + * Represents the struct contains the composition result. + * Return values + * * true + * The ongoing composition has a composition result. + * * false + * The ongoing composition does not have composition results. + * Remarks + * This function is designed for being called from WM_IME_COMPOSITION + * message handlers. + */ + bool GetResult(HWND window_handle, LPARAM lparam, + ImeComposition* composition); + + /** + * Retrieve the current composition status of the ongoing composition. + * Parameters + * * window_handle [in] (HWND) + * Represents the window handle of the caller. + * * lparam [in] (LPARAM) + * Specifies the updated members of the ongoing composition, and must be + * the same parameter of a WM_IME_COMPOSITION message handler. + * This parameter is used for checking if the ongoing composition has + * its result string, + * * composition [out] (ImeComposition) + * Represents the struct contains the composition status. + * Return values + * * true + * The status of the ongoing composition is updated. + * * false + * The status of the ongoing composition is not updated. + * Remarks + * This function is designed for being called from WM_IME_COMPOSITION + * message handlers. + */ + bool GetComposition(HWND window_handle, LPARAM lparam, + ImeComposition* composition); + + /** + * Enable the IME attached to the given window, i.e. allows user-input + * events to be dispatched to the IME. + * In Chrome, this function is used when: + * * a renderer process moves its input focus to another edit control, or; + * * a renrerer process moves the position of the focused edit control. + * Parameters + * * window_handle [in] (HWND) + * Represents the window handle of the caller. + * * caret_rect [in] (const gfx::Rect&) + * Represent the rectangle of the input caret. + * This rectangle is used for controlling the positions of IME windows. + * * complete [in] (bool) + * Represents whether or not to complete the ongoing composition. + * + true + * After finishing the ongoing composition and close its IME windows, + * start another composition and display its IME windows to the given + * position. + * + false + * Just move the IME windows of the ongoing composition to the given + * position without finishing it. + */ + void BeginIME(HWND window_handle, + const GHOST_Rect& caret_rect, + bool complete); + + /** + * Disable the IME attached to the given window, i.e. prohibits any user-input + * events from being dispatched to the IME. + * In Chrome, this function is used when: + * * a renreder process sets its input focus to a password input. + * Parameters + * * window_handle [in] (HWND) + * Represents the window handle of the caller. + */ + void EndIME(HWND window_handle); + + /* Updatg resultInfo and compInfo */ + void UpdateInfo(HWND window_handle); + + /* disable ime when start up */ + void CheckFirst(HWND window_handle); + + ImeComposition resultInfo, compInfo; + GHOST_TEventImeData eventImeData; + +protected: + /* Determines whether or not the given attribute represents a target (a.k.a. a selection). */ + bool IsTargetAttribute(char attribute) const { + return (attribute == ATTR_TARGET_CONVERTED || + attribute == ATTR_TARGET_NOTCONVERTED); + } + + /* Retrieve the target area. */ + void GetCaret(HIMC imm_context, LPARAM lparam, + ImeComposition* composition); + + /* Update the position of the IME windows. */ + void MoveImeWindow(HWND window_handle, HIMC imm_context); + + /* Complete the ongoing composition if it exists. */ + void CompleteComposition(HWND window_handle, HIMC imm_context); + + /* Retrieve a string from the IMM. */ + bool GetString(HIMC imm_context, WPARAM lparam, int type, + ImeComposition* composition); + +private: + /** + * Represents whether or not there is an ongoing composition in a browser + * process, i.e. whether or not a browser process is composing a text. + */ + bool is_composing_; + + /** + * This value represents whether or not the current input context has IMEs. + * The following table shows the list of IME status: + * Value Description + * false The current input language does not have IMEs. + * true The current input language has IMEs. + */ + bool ime_status_; + + /** + * The current input Language ID retrieved from Windows, which consists of: + * * Primary Language ID (bit 0 to bit 9), which shows a natunal language + * (English, Korean, Chinese, Japanese, etc.) and; + * * Sub-Language ID (bit 10 to bit 15), which shows a geometrical region + * the language is spoken (For English, United States, United Kingdom, + * Australia, Canada, etc.) + * The following list enumerates some examples for the Language ID: + * * "en-US" (0x0409) + * MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US); + * * "ko-KR" (0x0412) + * MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN); + * * "zh-TW" (0x0404) + * MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL); + * * "zh-CN" (0x0804) + * MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED); + * * "ja-JP" (0x0411) + * MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN), etc. + * (See <winnt.h> for other available values.) + * This Language ID is used for processing language-specific operations in + * IME functions. + */ + LANGID input_language_id_; + + /** + * Represents whether or not the current input context has created a system + * caret to set the position of its IME candidate window. + * * true: it creates a system caret. + * * false: it does not create a system caret. + */ + bool system_caret_; + + /* The rectangle of the input caret retrieved from a renderer process. */ + GHOST_Rect caret_rect_; + + /* used for disable ime when start up */ + bool is_first, is_enable; +}; + +#endif // WITH_INPUT_IME +#endif // __GHOST_IME_H__ diff --git a/intern/ghost/intern/GHOST_ModifierKeys.h b/intern/ghost/intern/GHOST_ModifierKeys.h index 8189006f602..a30b66c4634 100644 --- a/intern/ghost/intern/GHOST_ModifierKeys.h +++ b/intern/ghost/intern/GHOST_ModifierKeys.h @@ -47,7 +47,7 @@ struct GHOST_ModifierKeys { */ GHOST_ModifierKeys(); - virtual ~GHOST_ModifierKeys(); + ~GHOST_ModifierKeys(); /** * Returns the modifier key's key code from a modifier key mask. @@ -59,29 +59,29 @@ struct GHOST_ModifierKeys { /** * Returns the state of a single modifier key. - * \param mask. Key state to return. + * \param mask: Key state to return. * \return The state of the key (pressed == true). */ - virtual bool get(GHOST_TModifierKeyMask mask) const; + bool get(GHOST_TModifierKeyMask mask) const; /** * Updates the state of a single modifier key. - * \param mask. Key state to update. - * \param down. The new state of the key. + * \param mask: Key state to update. + * \param down: The new state of the key. */ - virtual void set(GHOST_TModifierKeyMask mask, bool down); + void set(GHOST_TModifierKeyMask mask, bool down); /** * Sets the state of all modifier keys to up. */ - virtual void clear(); + void clear(); /** * Determines whether to modifier key states are equal. - * \param keys. The modifier key state to compare to. + * \param keys: The modifier key state to compare to. * \return Indication of equality. */ - virtual bool equals(const GHOST_ModifierKeys& keys) const; + bool equals(const GHOST_ModifierKeys& keys) const; /** Bitfield that stores the appropriate key state. */ GHOST_TUns8 m_LeftShift : 1; diff --git a/intern/ghost/intern/GHOST_NDOFManager.cpp b/intern/ghost/intern/GHOST_NDOFManager.cpp index 9bd6226d5c2..27285e49e9e 100644 --- a/intern/ghost/intern/GHOST_NDOFManager.cpp +++ b/intern/ghost/intern/GHOST_NDOFManager.cpp @@ -146,8 +146,8 @@ static const NDOF_ButtonT SpaceExplorer_HID_map[] = { NDOF_BUTTON_ROTATE }; -/* this is the older SpacePilot (sans Pro) - * thanks to polosson for info about this device */ +// this is the older SpacePilot (sans Pro) +// thanks to polosson for info about this device static const NDOF_ButtonT SpacePilot_HID_map[] = { NDOF_BUTTON_1, NDOF_BUTTON_2, @@ -190,17 +190,17 @@ static const NDOF_ButtonT Generic_HID_map[] = { static const int genericButtonCount = sizeof(Generic_HID_map) / sizeof(NDOF_ButtonT); GHOST_NDOFManager::GHOST_NDOFManager(GHOST_System &sys) - : m_system(sys), - m_deviceType(NDOF_UnknownDevice), /* each platform has its own device detection code */ - m_buttonCount(genericButtonCount), - m_buttonMask(0), - m_hidMap(Generic_HID_map), - m_buttons(0), - m_motionTime(0), - m_prevMotionTime(0), - m_motionState(GHOST_kNotStarted), - m_motionEventPending(false), - m_deadZone(0.0f) + : m_system(sys), + m_deviceType(NDOF_UnknownDevice), // each platform has its own device detection code + m_buttonCount(genericButtonCount), + m_buttonMask(0), + m_hidMap(Generic_HID_map), + m_buttons(0), + m_motionTime(0), + m_prevMotionTime(0), + m_motionState(GHOST_kNotStarted), + m_motionEventPending(false), + m_deadZone(0.0f) { // to avoid the rare situation where one triple is updated and // the other is not, initialize them both here: @@ -228,7 +228,7 @@ bool GHOST_NDOFManager::setDevice(unsigned short vendor_id, unsigned short produ // that I don't have access to. Thanks! switch (vendor_id) { - case 0x046D: // Logitech (3Dconnexion) + case 0x046D: // Logitech (3Dconnexion was a subsidiary) switch (product_id) { // -- current devices -- case 0xC626: // full-size SpaceNavigator @@ -281,6 +281,29 @@ bool GHOST_NDOFManager::setDevice(unsigned short vendor_id, unsigned short produ printf("ndof: unknown Logitech product %04hx\n", product_id); } break; + case 0x256F: // 3Dconnexion + switch (product_id) { + case 0xC62E: // plugged in + case 0xC62F: // wireless + puts("ndof: using SpaceMouse Wireless"); + m_deviceType = NDOF_SpaceMouseWireless; + m_buttonCount = 2; + m_hidMap = Modern3Dx_HID_map; + break; + case 0xC631: // plugged in + case 0xC632: // wireless + puts("ndof: using SpaceMouse Pro Wireless"); + m_deviceType = NDOF_SpaceMouseProWireless; + m_buttonCount = 27; + // ^^ actually has 15 buttons, but their HID codes range from 0 to 26 + m_buttonMask = 0x07C0F137; + m_hidMap = Modern3Dx_HID_map; + break; + + default: + printf("ndof: unknown 3Dconnexion product %04hx\n", product_id); + } + break; default: printf("ndof: unknown device %04hx:%04hx\n", vendor_id, product_id); } @@ -390,9 +413,9 @@ void GHOST_NDOFManager::updateButtons(int button_bits, GHOST_TUns64 time) void GHOST_NDOFManager::setDeadZone(float dz) { - if (dz < 0.f) { + if (dz < 0.0f) { // negative values don't make sense, so clamp at zero - dz = 0.f; + dz = 0.0f; } else if (dz > 0.5f) { // warn the rogue user/developer, but allow it @@ -405,14 +428,14 @@ void GHOST_NDOFManager::setDeadZone(float dz) static bool atHomePosition(GHOST_TEventNDOFMotionData *ndof) { -#define HOME(foo) (ndof->foo == 0.f) +#define HOME(foo) (ndof->foo == 0.0f) return HOME(tx) && HOME(ty) && HOME(tz) && HOME(rx) && HOME(ry) && HOME(rz); #undef HOME } static bool nearHomePosition(GHOST_TEventNDOFMotionData *ndof, float threshold) { - if (threshold == 0.f) { + if (threshold == 0.0f) { return atHomePosition(ndof); } else { @@ -442,7 +465,7 @@ bool GHOST_NDOFManager::sendMotionEvent() // scale axis values here to normalize them to around +/- 1 // they are scaled again for overall sensitivity in the WM based on user prefs - const float scale = 1.f / 350.f; // 3Dconnexion devices send +/- 350 usually + const float scale = 1.0f / 350.0f; // 3Dconnexion devices send +/- 350 usually data->tx = scale * m_translation[0]; data->ty = scale * m_translation[1]; diff --git a/intern/ghost/intern/GHOST_NDOFManager.h b/intern/ghost/intern/GHOST_NDOFManager.h index 0b2080e36a9..48a4881afb1 100644 --- a/intern/ghost/intern/GHOST_NDOFManager.h +++ b/intern/ghost/intern/GHOST_NDOFManager.h @@ -20,7 +20,7 @@ * * ***** END GPL LICENSE BLOCK ***** */ - + #ifndef __GHOST_NDOFMANAGER_H__ #define __GHOST_NDOFMANAGER_H__ @@ -38,6 +38,8 @@ typedef enum { NDOF_SpaceExplorer, NDOF_SpacePilotPro, NDOF_SpaceMousePro, + NDOF_SpaceMouseWireless, + NDOF_SpaceMouseProWireless, // older devices NDOF_SpacePilot, diff --git a/intern/ghost/intern/GHOST_NDOFManagerWin32.cpp b/intern/ghost/intern/GHOST_NDOFManagerWin32.cpp index 6c1627905e0..7ccd2e602b4 100644 --- a/intern/ghost/intern/GHOST_NDOFManagerWin32.cpp +++ b/intern/ghost/intern/GHOST_NDOFManagerWin32.cpp @@ -30,7 +30,7 @@ GHOST_NDOFManagerWin32::GHOST_NDOFManagerWin32(GHOST_System& sys) : GHOST_NDOFManager(sys) { - setDeadZone(0.1f); + /* pass */ } // whether multi-axis functionality is available (via the OS or driver) diff --git a/intern/ghost/intern/GHOST_NDOFManagerX11.cpp b/intern/ghost/intern/GHOST_NDOFManagerX11.cpp index 8e6f9994e51..753321be58e 100644 --- a/intern/ghost/intern/GHOST_NDOFManagerX11.cpp +++ b/intern/ghost/intern/GHOST_NDOFManagerX11.cpp @@ -33,8 +33,6 @@ GHOST_NDOFManagerX11::GHOST_NDOFManagerX11(GHOST_System& sys) : GHOST_NDOFManager(sys), m_available(false) { - setDeadZone(0.1f); /* how to calibrate on Linux? throw away slight motion! */ - if (spnav_open() != -1) { m_available = true; @@ -42,8 +40,8 @@ GHOST_NDOFManagerX11::GHOST_NDOFManagerX11(GHOST_System& sys) #define MAX_LINE_LENGTH 100 - /* look for USB devices with Logitech's vendor ID */ - FILE *command_output = popen("lsusb -d 046d:", "r"); + /* look for USB devices with Logitech or 3Dconnexion's vendor ID */ + FILE *command_output = popen("lsusb | grep '046d:\\|256f:'", "r"); if (command_output) { char line[MAX_LINE_LENGTH] = {0}; while (fgets(line, MAX_LINE_LENGTH, command_output)) { @@ -58,7 +56,7 @@ GHOST_NDOFManagerX11::GHOST_NDOFManagerX11(GHOST_System& sys) } else { #ifdef DEBUG - /* annoying for official builds, just adds noise and most prople don't own these */ + /* annoying for official builds, just adds noise and most people don't own these */ puts("ndof: spacenavd not found"); /* This isn't a hard error, just means the user doesn't have a 3D mouse. */ #endif @@ -81,11 +79,11 @@ bool GHOST_NDOFManagerX11::available() * this causes any proceeding event to have a very high 'dt' (time delta), * many seconds for eg, causing the view to jump. * - * this workaround expect's continuous events, if we miss a motion event, + * this workaround expects continuous events, if we miss a motion event, * immediately send a dummy event with no motion to ensure the finished state is reached. */ #define USE_FINISH_GLITCH_WORKAROUND - +/* TODO: make this available on all platforms */ #ifdef USE_FINISH_GLITCH_WORKAROUND static bool motion_test_prev = false; diff --git a/intern/ghost/intern/GHOST_System.cpp b/intern/ghost/intern/GHOST_System.cpp index d1f2d5d87c8..1698d2f0f31 100644 --- a/intern/ghost/intern/GHOST_System.cpp +++ b/intern/ghost/intern/GHOST_System.cpp @@ -169,7 +169,7 @@ GHOST_TSuccess GHOST_System::beginFullScreen(const GHOST_DisplaySetting& setting } -GHOST_TSuccess GHOST_System::updateFullScreen(const GHOST_DisplaySetting& setting, GHOST_IWindow **window) +GHOST_TSuccess GHOST_System::updateFullScreen(const GHOST_DisplaySetting& setting, GHOST_IWindow ** /*window*/) { GHOST_TSuccess success = GHOST_kFailure; GHOST_ASSERT(m_windowManager, "GHOST_System::updateFullScreen(): invalid window manager"); @@ -295,6 +295,15 @@ GHOST_TSuccess GHOST_System::getButtonState(GHOST_TButtonMask mask, bool& isDown return success; } +void GHOST_System::setNDOFDeadZone(float deadzone) +{ +#ifdef WITH_INPUT_NDOF + this->m_ndofManager->setDeadZone(deadzone); +#else + (void)deadzone; +#endif +} + GHOST_TSuccess GHOST_System::init() { m_timerManager = new GHOST_TimerManager(); @@ -350,6 +359,12 @@ GHOST_TSuccess GHOST_System::exit() GHOST_TSuccess GHOST_System::createFullScreenWindow(GHOST_Window **window, const GHOST_DisplaySetting &settings, const bool stereoVisual, const GHOST_TUns16 numOfAASamples) { + GHOST_GLSettings glSettings = {0}; + + if (stereoVisual) + glSettings.flags |= GHOST_glStereoVisual; + glSettings.numOfAASamples = numOfAASamples; + /* note: don't use getCurrentDisplaySetting() because on X11 we may * be zoomed in and the desktop may be bigger then the viewport. */ GHOST_ASSERT(m_displayManager, "GHOST_System::createFullScreenWindow(): invalid display manager"); @@ -359,14 +374,13 @@ GHOST_TSuccess GHOST_System::createFullScreenWindow(GHOST_Window **window, const 0, 0, settings.xPixels, settings.yPixels, GHOST_kWindowStateNormal, GHOST_kDrawingContextTypeOpenGL, - stereoVisual, - true, /* exclusive */ - numOfAASamples); + glSettings, + true /* exclusive */); return (*window == NULL) ? GHOST_kFailure : GHOST_kSuccess; } -int GHOST_System::confirmQuit(GHOST_IWindow *window) const +int GHOST_System::confirmQuit(GHOST_IWindow * /*window*/) const { return 1; } diff --git a/intern/ghost/intern/GHOST_System.h b/intern/ghost/intern/GHOST_System.h index 79230b0f505..9dcba11527c 100644 --- a/intern/ghost/intern/GHOST_System.h +++ b/intern/ghost/intern/GHOST_System.h @@ -97,7 +97,7 @@ public: * \param userData Placeholder for user data. * \return A timer task (0 if timer task installation failed). */ - virtual GHOST_ITimerTask *installTimer(GHOST_TUns64 delay, + GHOST_ITimerTask *installTimer(GHOST_TUns64 delay, GHOST_TUns64 interval, GHOST_TimerProcPtr timerProc, GHOST_TUserDataPtr userData = NULL); @@ -107,7 +107,7 @@ public: * \param timerTask Timer task to be removed. * \return Indication of success. */ - virtual GHOST_TSuccess removeTimer(GHOST_ITimerTask *timerTask); + GHOST_TSuccess removeTimer(GHOST_ITimerTask *timerTask); /*************************************************************************************** * Display/window management functionality @@ -126,14 +126,14 @@ public: * \param window Pointer to the window to be disposed. * \return Indication of success. */ - virtual GHOST_TSuccess disposeWindow(GHOST_IWindow *window); + GHOST_TSuccess disposeWindow(GHOST_IWindow *window); /** * Returns whether a window is valid. * \param window Pointer to the window to be checked. * \return Indication of validity. */ - virtual bool validWindow(GHOST_IWindow *window); + bool validWindow(GHOST_IWindow *window); /** * Begins full screen mode. @@ -143,7 +143,7 @@ public: * This window is invalid after full screen has been ended. * \return Indication of success. */ - virtual GHOST_TSuccess beginFullScreen(const GHOST_DisplaySetting& setting, GHOST_IWindow **window, + GHOST_TSuccess beginFullScreen(const GHOST_DisplaySetting& setting, GHOST_IWindow **window, const bool stereoVisual, const GHOST_TUns16 numOfAASamples = 0); /** @@ -153,26 +153,26 @@ public: * * \return Indication of success. */ - virtual GHOST_TSuccess updateFullScreen(const GHOST_DisplaySetting& setting, GHOST_IWindow **window); + GHOST_TSuccess updateFullScreen(const GHOST_DisplaySetting& setting, GHOST_IWindow **window); /** * Ends full screen mode. * \return Indication of success. */ - virtual GHOST_TSuccess endFullScreen(void); + GHOST_TSuccess endFullScreen(void); /** * Returns current full screen mode status. * \return The current status. */ - virtual bool getFullScreen(void); + bool getFullScreen(void); /** * Native pixel size support (MacBook 'retina'). * \return The pixel size in float. */ - virtual bool useNativePixel(void); + bool useNativePixel(void); bool m_nativePixel; /*************************************************************************************** @@ -192,21 +192,21 @@ public: * The event stack will be empty afterwards. * \return Indication as to whether any of the consumers handled the events. */ - virtual bool dispatchEvents(); + bool dispatchEvents(); /** * Adds the given event consumer to our list. * \param consumer The event consumer to add. * \return Indication of success. */ - virtual GHOST_TSuccess addEventConsumer(GHOST_IEventConsumer *consumer); + GHOST_TSuccess addEventConsumer(GHOST_IEventConsumer *consumer); /** * Remove the given event consumer to our list. * \param consumer The event consumer to remove. * \return Indication of success. */ - virtual GHOST_TSuccess removeEventConsumer(GHOST_IEventConsumer *consumer); + GHOST_TSuccess removeEventConsumer(GHOST_IEventConsumer *consumer); /*************************************************************************************** * Cursor management functionality @@ -227,7 +227,7 @@ public: * \param isDown The state of a modifier key (true == pressed). * \return Indication of success. */ - virtual GHOST_TSuccess getModifierKeyState(GHOST_TModifierKeyMask mask, bool& isDown) const; + GHOST_TSuccess getModifierKeyState(GHOST_TModifierKeyMask mask, bool& isDown) const; /** * Returns the state of a mouse button (ouside the message queue). @@ -235,9 +235,19 @@ public: * \param isDown Button state. * \return Indication of success. */ - virtual GHOST_TSuccess getButtonState(GHOST_TButtonMask mask, bool& isDown) const; + GHOST_TSuccess getButtonState(GHOST_TButtonMask mask, bool& isDown) const; /*************************************************************************************** + * Access to 3D mouse. + ***************************************************************************************/ + + /** + * Sets 3D mouse deadzone + * \param deadzone: Deadzone of the 3D mouse (both for rotation and pan) relative to full range + */ + void setNDOFDeadZone(float deadzone); + + /*************************************************************************************** * Other (internal) functionality. ***************************************************************************************/ @@ -247,28 +257,28 @@ public: * Do not delete the event! * \param event The event to push on the stack. */ - virtual GHOST_TSuccess pushEvent(GHOST_IEvent *event); + GHOST_TSuccess pushEvent(GHOST_IEvent *event); /** * \return The timer manager. */ - inline virtual GHOST_TimerManager *getTimerManager() const; + inline GHOST_TimerManager *getTimerManager() const; /** * \return A pointer to our event manager. */ - virtual inline GHOST_EventManager *getEventManager() const; + inline GHOST_EventManager *getEventManager() const; /** * \return A pointer to our window manager. */ - virtual inline GHOST_WindowManager *getWindowManager() const; + inline GHOST_WindowManager *getWindowManager() const; #ifdef WITH_INPUT_NDOF /** * \return A pointer to our n-degree of freedom manager. */ - virtual inline GHOST_NDOFManager *getNDOFManager() const; + inline GHOST_NDOFManager *getNDOFManager() const; #endif /** @@ -326,7 +336,7 @@ protected: * \param window The window created. * \return Indication of success. */ - virtual GHOST_TSuccess createFullScreenWindow(GHOST_Window **window, const GHOST_DisplaySetting &settings, + GHOST_TSuccess createFullScreenWindow(GHOST_Window **window, const GHOST_DisplaySetting &settings, const bool stereoVisual, const GHOST_TUns16 numOfAASamples = 0); /** The display manager (platform dependent). */ diff --git a/intern/ghost/intern/GHOST_SystemCocoa.h b/intern/ghost/intern/GHOST_SystemCocoa.h index 9b30abe75d5..3d6b40ee541 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.h +++ b/intern/ghost/intern/GHOST_SystemCocoa.h @@ -71,7 +71,7 @@ public: * Based on ANSI clock() routine. * \return The number of milliseconds. */ - virtual GHOST_TUns64 getMilliSeconds() const; + GHOST_TUns64 getMilliSeconds() const; /*************************************************************************************** * Display/window management functionality @@ -81,18 +81,18 @@ public: * Returns the number of displays on this system. * \return The number of displays. */ - virtual GHOST_TUns8 getNumDisplays() const; + GHOST_TUns8 getNumDisplays() const; /** * Returns the dimensions of the main display on this system. * \return The dimension of the main display. */ - virtual void getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const; + void getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const; /** Returns the combine dimensions of all monitors. * \return The dimension of the workspace. */ - virtual void getAllDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const; + void getAllDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const; /** * Create a new window. @@ -105,12 +105,12 @@ public: * \param height The height the window. * \param state The state of the window when opened. * \param type The type of drawing context installed in this window. - * \param stereoVisual Stereo visual for quad buffered stereo. - * \param numOfAASamples Number of samples used for AA (zero if no AA) + * \param glSettings: Misc OpenGL settings. + * \param exclusive: Use to show the window ontop and ignore others (used fullscreen). * \param parentWindow Parent (embedder) window * \return The new window (or 0 if creation failed). */ - virtual GHOST_IWindow *createWindow( + GHOST_IWindow *createWindow( const STR_String& title, GHOST_TInt32 left, GHOST_TInt32 top, @@ -118,9 +118,8 @@ public: GHOST_TUns32 height, GHOST_TWindowState state, GHOST_TDrawingContextType type, - const bool stereoVisual = false, - const bool exclusive = false, - const GHOST_TUns16 numOfAASamples = 0, + GHOST_GLSettings glSettings, + const bool exclusive = false, const GHOST_TEmbedderWindowID parentWindow = 0 ); @@ -133,7 +132,7 @@ public: * \param waitForEvent Flag to wait for an event (or return immediately). * \return Indication of the presence of events. */ - virtual bool processEvents(bool waitForEvent); + bool processEvents(bool waitForEvent); /** * Handle User request to quit, from Menu bar Quit, and Cmd+Q @@ -169,7 +168,7 @@ public: * \param y The y-coordinate of the cursor. * \return Indication of success. */ - virtual GHOST_TSuccess getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const; + GHOST_TSuccess getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const; /** * Updates the location of the cursor (location in screen coordinates). @@ -177,7 +176,7 @@ public: * \param y The y-coordinate of the cursor. * \return Indication of success. */ - virtual GHOST_TSuccess setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y); + GHOST_TSuccess setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y); /*************************************************************************************** * Access to mouse button and keyboard states. @@ -188,28 +187,28 @@ public: * \param keys The state of all modifier keys (true == pressed). * \return Indication of success. */ - virtual GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys& keys) const; + GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys& keys) const; /** * Returns the state of the mouse buttons (ouside the message queue). * \param buttons The state of the buttons. * \return Indication of success. */ - virtual GHOST_TSuccess getButtons(GHOST_Buttons& buttons) const; + GHOST_TSuccess getButtons(GHOST_Buttons& buttons) const; /** * Returns Clipboard data * \param selection Indicate which buffer to return * \return Returns the selected buffer */ - virtual GHOST_TUns8 *getClipboard(bool selection) const; + GHOST_TUns8 *getClipboard(bool selection) const; /** * Puts buffer to system clipboard * \param buffer The buffer to be copied * \param selection Indicates which buffer to copy too, only used on X11 */ - virtual void putClipboard(GHOST_TInt8 *buffer, bool selection) const; + void putClipboard(GHOST_TInt8 *buffer, bool selection) const; /** * Handles a window event. Called by GHOST_WindowCocoa window delegate @@ -268,7 +267,7 @@ protected: * For now, it just registers the window class (WNDCLASS). * \return A success value. */ - virtual GHOST_TSuccess init(); + GHOST_TSuccess init(); /** * Performs the actual cursor position update (location in screen coordinates). diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index 7b08c46b65d..b9686e5af9b 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -22,6 +22,7 @@ * * Contributors: Maarten Gribnau 05/2001 * Damien Plisson 09/2009 + * Jens Verwiebe 10/2014 * * ***** END GPL LICENSE BLOCK ***** */ @@ -56,6 +57,7 @@ #include "AssertMacros.h" + #pragma mark KeyMap, mouse converters static GHOST_TButtonMask convertButton(int button) @@ -71,6 +73,10 @@ static GHOST_TButtonMask convertButton(int button) return GHOST_kButtonMaskButton4; case 4: return GHOST_kButtonMaskButton5; + case 5: + return GHOST_kButtonMaskButton6; + case 6: + return GHOST_kButtonMaskButton7; default: return GHOST_kButtonMaskLeft; } @@ -203,6 +209,7 @@ static GHOST_TKey convertKey(int rawCode, unichar recvChar, UInt16 keyAction) return GHOST_kKeyUnknown; default: + { /* alphanumerical or punctuation key that is remappable in int'l keyboards */ if ((recvChar >= 'A') && (recvChar <= 'Z')) { return (GHOST_TKey) (recvChar - 'A' + GHOST_kKeyA); @@ -211,27 +218,25 @@ static GHOST_TKey convertKey(int rawCode, unichar recvChar, UInt16 keyAction) return (GHOST_TKey) (recvChar - 'a' + GHOST_kKeyA); } else { - - /* Leopard and Snow Leopard 64bit compatible API*/ - CFDataRef uchrHandle; /*the keyboard layout*/ - TISInputSourceRef kbdTISHandle; - - kbdTISHandle = TISCopyCurrentKeyboardLayoutInputSource(); - uchrHandle = (CFDataRef)TISGetInputSourceProperty(kbdTISHandle,kTISPropertyUnicodeKeyLayoutData); - CFRelease(kbdTISHandle); - - /*get actual character value of the "remappable" keys in int'l keyboards, - if keyboard layout is not correctly reported (e.g. some non Apple keyboards in Tiger), - then fallback on using the received charactersIgnoringModifiers */ - if (uchrHandle) - { - UInt32 deadKeyState=0; - UniCharCount actualStrLength=0; + /* Leopard and Snow Leopard 64bit compatible API*/ + CFDataRef uchrHandle; /*the keyboard layout*/ + TISInputSourceRef kbdTISHandle; - UCKeyTranslate((UCKeyboardLayout*)CFDataGetBytePtr(uchrHandle), rawCode, keyAction, 0, - LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit, &deadKeyState, 1, &actualStrLength, &recvChar); + kbdTISHandle = TISCopyCurrentKeyboardLayoutInputSource(); + uchrHandle = (CFDataRef)TISGetInputSourceProperty(kbdTISHandle,kTISPropertyUnicodeKeyLayoutData); + CFRelease(kbdTISHandle); - } + /*get actual character value of the "remappable" keys in int'l keyboards, + if keyboard layout is not correctly reported (e.g. some non Apple keyboards in Tiger), + then fallback on using the received charactersIgnoringModifiers */ + if (uchrHandle) { + UInt32 deadKeyState=0; + UniCharCount actualStrLength=0; + + UCKeyTranslate((UCKeyboardLayout*)CFDataGetBytePtr(uchrHandle), rawCode, keyAction, 0, + LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit, &deadKeyState, 1, &actualStrLength, &recvChar); + + } switch (recvChar) { case '-': return GHOST_kKeyMinus; @@ -249,13 +254,11 @@ static GHOST_TKey convertKey(int rawCode, unichar recvChar, UInt16 keyAction) return GHOST_kKeyUnknown; } } + } } return GHOST_kKeyUnknown; } - -#pragma mark defines for 10.6 api not documented in 10.5 - #pragma mark Utility functions #define FIRSTFILEBUFLG 512 @@ -271,7 +274,7 @@ extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG]) return 1; } else { - return 0; + return 0; } } @@ -334,12 +337,8 @@ extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG]) @end - - #pragma mark initialization/finalization -char GHOST_user_locale[128]; // Global current user locale - GHOST_SystemCocoa::GHOST_SystemCocoa() { int mib[2]; @@ -377,15 +376,6 @@ GHOST_SystemCocoa::GHOST_SystemCocoa() rstring = NULL; m_ignoreWindowSizedMessages = false; - - //Get current locale - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - CFLocaleRef myCFLocale = CFLocaleCopyCurrent(); - NSLocale * myNSLocale = (NSLocale *) myCFLocale; - [myNSLocale autorelease]; - NSString *nsIdentifier = [myNSLocale localeIdentifier]; - strncpy(GHOST_user_locale, [nsIdentifier UTF8String], sizeof(GHOST_user_locale) - 1); - [pool drain]; } GHOST_SystemCocoa::~GHOST_SystemCocoa() @@ -544,9 +534,8 @@ GHOST_IWindow* GHOST_SystemCocoa::createWindow( GHOST_TUns32 height, GHOST_TWindowState state, GHOST_TDrawingContextType type, - bool stereoVisual, + GHOST_GLSettings glSettings, const bool exclusive, - const GHOST_TUns16 numOfAASamples, const GHOST_TEmbedderWindowID parentWindow ) { @@ -565,27 +554,23 @@ GHOST_IWindow* GHOST_SystemCocoa::createWindow( // Add contentRect.origin.y to respect docksize bottom = bottom > contentRect.origin.y ? bottom + contentRect.origin.y : contentRect.origin.y; - window = new GHOST_WindowCocoa (this, title, left, bottom, width, height, state, type, stereoVisual, numOfAASamples); + window = new GHOST_WindowCocoa (this, title, left, bottom, width, height, state, type, ((glSettings.flags & GHOST_glStereoVisual) != 0), glSettings.numOfAASamples); - if (window) { - if (window->getValid()) { - // Store the pointer to the window - GHOST_ASSERT(m_windowManager, "m_windowManager not initialized"); - m_windowManager->addWindow(window); - m_windowManager->setActiveWindow(window); - //Need to tell window manager the new window is the active one (Cocoa does not send the event activate upon window creation) - pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window)); - pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window)); - } - else { - GHOST_PRINT("GHOST_SystemCocoa::createWindow(): window invalid\n"); - delete window; - window = 0; - } + if (window->getValid()) { + // Store the pointer to the window + GHOST_ASSERT(m_windowManager, "m_windowManager not initialized"); + m_windowManager->addWindow(window); + m_windowManager->setActiveWindow(window); + //Need to tell window manager the new window is the active one (Cocoa does not send the event activate upon window creation) + pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window)); + pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window)); } else { - GHOST_PRINT("GHOST_SystemCocoa::createWindow(): could not create window\n"); + GHOST_PRINT("GHOST_SystemCocoa::createWindow(): window invalid\n"); + delete window; + window = 0; } + [pool drain]; return window; } @@ -672,7 +657,6 @@ GHOST_TSuccess GHOST_SystemCocoa::getButtons(GHOST_Buttons& buttons) const } - #pragma mark Event handlers /** @@ -740,7 +724,7 @@ bool GHOST_SystemCocoa::processEvents(bool waitForEvent) // For some reason NSApp is swallowing the key up events when modifier // key is pressed, even if there seems to be no apparent reason to do // so, as a workaround we always handle these up events. - if ([event type] == NSKeyUp && (([event modifierFlags] & NSCommandKeyMask) || ([event modifierFlags] & NSAlternateKeyMask))) + if ([event type] == NSKeyUp && ([event modifierFlags] & (NSCommandKeyMask | NSAlternateKeyMask))) handleKeyEvent(event); [NSApp sendEvent:event]; @@ -812,58 +796,57 @@ GHOST_TSuccess GHOST_SystemCocoa::handleWindowEvent(GHOST_TEventType eventType, if (!validWindow(window)) { return GHOST_kFailure; } - switch (eventType) { - case GHOST_kEventWindowClose: - // check for index of mainwindow as it would quit blender without dialog and discard - if ([windowsList count] > 1 && window->getCocoaWindow() != [windowsList objectAtIndex:[windowsList count] - 1]) { - pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) ); - } - else { - handleQuitRequest(); // -> quit dialog - } - break; - case GHOST_kEventWindowActivate: - m_windowManager->setActiveWindow(window); - window->loadCursor(window->getCursorVisibility(), window->getCursorShape()); - pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) ); - break; - case GHOST_kEventWindowDeactivate: - m_windowManager->setWindowInactive(window); - pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) ); - break; - case GHOST_kEventWindowUpdate: - if (m_nativePixel) { - window->setNativePixelSize(); - pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventNativeResolutionChange, window) ); - } - pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) ); - break; - case GHOST_kEventWindowMove: - pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowMove, window) ); - break; - case GHOST_kEventWindowSize: - if (!m_ignoreWindowSizedMessages) - { - //Enforce only one resize message per event loop (coalescing all the live resize messages) - window->updateDrawingContext(); - pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) ); - //Mouse up event is trapped by the resizing event loop, so send it anyway to the window manager - pushEvent(new GHOST_EventButton(getMilliSeconds(), GHOST_kEventButtonUp, window, convertButton(0))); - //m_ignoreWindowSizedMessages = true; - } - break; - case GHOST_kEventNativeResolutionChange: - - if (m_nativePixel) { - window->setNativePixelSize(); - pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventNativeResolutionChange, window) ); - } + switch (eventType) { + case GHOST_kEventWindowClose: + // check for index of mainwindow as it would quit blender without dialog and discard + if ([windowsList count] > 1 && window->getCocoaWindow() != [windowsList objectAtIndex:[windowsList count] - 1]) { + pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) ); + } + else { + handleQuitRequest(); // -> quit dialog + } + break; + case GHOST_kEventWindowActivate: + m_windowManager->setActiveWindow(window); + window->loadCursor(window->getCursorVisibility(), window->getCursorShape()); + pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) ); + break; + case GHOST_kEventWindowDeactivate: + m_windowManager->setWindowInactive(window); + pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) ); + break; + case GHOST_kEventWindowUpdate: + if (m_nativePixel) { + window->setNativePixelSize(); + pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventNativeResolutionChange, window) ); + } + pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) ); + break; + case GHOST_kEventWindowMove: + pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowMove, window) ); + break; + case GHOST_kEventWindowSize: + if (!m_ignoreWindowSizedMessages) { + //Enforce only one resize message per event loop (coalescing all the live resize messages) + window->updateDrawingContext(); + pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) ); + //Mouse up event is trapped by the resizing event loop, so send it anyway to the window manager + pushEvent(new GHOST_EventButton(getMilliSeconds(), GHOST_kEventButtonUp, window, convertButton(0))); + //m_ignoreWindowSizedMessages = true; + } + break; + case GHOST_kEventNativeResolutionChange: + + if (m_nativePixel) { + window->setNativePixelSize(); + pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventNativeResolutionChange, window) ); + } + + default: + return GHOST_kFailure; + break; + } - default: - return GHOST_kFailure; - break; - } - m_outsideLoopEventProcessed = true; return GHOST_kSuccess; } @@ -902,7 +885,10 @@ GHOST_TSuccess GHOST_SystemCocoa::handleDraggingEvent(GHOST_TEventType eventType if (!strArray) return GHOST_kFailure; strArray->count = [droppedArray count]; - if (strArray->count == 0) return GHOST_kFailure; + if (strArray->count == 0) { + free(strArray); + return GHOST_kFailure; + } strArray->strings = (GHOST_TUns8**) malloc(strArray->count*sizeof(GHOST_TUns8*)); @@ -1059,16 +1045,17 @@ GHOST_TSuccess GHOST_SystemCocoa::handleDraggingEvent(GHOST_TEventType eventType } eventData = (GHOST_TEventDataPtr) ibuf; - } + break; - + } default: return GHOST_kFailure; break; } pushEvent(new GHOST_EventDragnDrop(getMilliSeconds(),eventType,draggedObjectType,window,mouseX,mouseY,eventData)); - } + break; + } default: return GHOST_kFailure; } @@ -1251,12 +1238,12 @@ bool GHOST_SystemCocoa::handleTabletEvent(void *eventPtr) #if MAC_OS_X_VERSION_MAX_ALLOWED < 1070 enum { - NSEventPhaseNone = 0, - NSEventPhaseBegan = 0x1 << 0, - NSEventPhaseStationary = 0x1 << 1, - NSEventPhaseChanged = 0x1 << 2, - NSEventPhaseEnded = 0x1 << 3, - NSEventPhaseCancelled = 0x1 << 4, + NSEventPhaseNone = 0, + NSEventPhaseBegan = 0x1 << 0, + NSEventPhaseStationary = 0x1 << 1, + NSEventPhaseChanged = 0x1 << 2, + NSEventPhaseEnded = 0x1 << 3, + NSEventPhaseCancelled = 0x1 << 4, }; typedef NSUInteger NSEventPhase; @@ -1609,7 +1596,6 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr) } - #pragma mark Clipboard get/set GHOST_TUns8* GHOST_SystemCocoa::getClipboard(bool selection) const diff --git a/intern/ghost/intern/GHOST_SystemNULL.h b/intern/ghost/intern/GHOST_SystemNULL.h index 77a741c2efb..868416cd227 100644 --- a/intern/ghost/intern/GHOST_SystemNULL.h +++ b/intern/ghost/intern/GHOST_SystemNULL.h @@ -75,12 +75,12 @@ public: GHOST_TUns32 height, GHOST_TWindowState state, GHOST_TDrawingContextType type, - bool stereoVisual, + GHOST_GLSettings glSettings, bool exclusive, - const GHOST_TUns16 numOfAASamples, const GHOST_TEmbedderWindowID parentWindow) { - return new GHOST_WindowNULL(this, title, left, top, width, height, state, parentWindow, type, stereoVisual, 1); + return new GHOST_WindowNULL(this, title, left, top, width, height, state, parentWindow, type, + ((glSettings.flags & GHOST_glStereoVisual) != 0), 1); } }; diff --git a/intern/ghost/intern/GHOST_SystemPathsCocoa.h b/intern/ghost/intern/GHOST_SystemPathsCocoa.h index cd8d161ff9c..1c76284857c 100644 --- a/intern/ghost/intern/GHOST_SystemPathsCocoa.h +++ b/intern/ghost/intern/GHOST_SystemPathsCocoa.h @@ -57,20 +57,20 @@ public: * "unpack and run" path, then look for properly installed path, including versioning. * \return Unsigned char string pointing to system dir (eg /usr/share/blender/). */ - virtual const GHOST_TUns8 *getSystemDir(int version, const char *versionstr) const; + const GHOST_TUns8 *getSystemDir(int version, const char *versionstr) const; /** * Determine the base dir in which user configuration is stored, including versioning. * If needed, it will create the base directory. * \return Unsigned char string pointing to user dir (eg ~/.blender/). */ - virtual const GHOST_TUns8 *getUserDir(int version, const char *versionstr) const; + const GHOST_TUns8 *getUserDir(int version, const char *versionstr) const; /** * Determine the directory of the current binary * \return Unsigned char string pointing to the binary dir */ - virtual const GHOST_TUns8 *getBinaryDir() const; + const GHOST_TUns8 *getBinaryDir() const; /** * Add the file to the operating system most recently used files diff --git a/intern/ghost/intern/GHOST_SystemPathsCocoa.mm b/intern/ghost/intern/GHOST_SystemPathsCocoa.mm index 50ad91eeb99..36ae534da87 100644 --- a/intern/ghost/intern/GHOST_SystemPathsCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemPathsCocoa.mm @@ -24,14 +24,7 @@ * ***** END GPL LICENSE BLOCK ***** */ -#import <Cocoa/Cocoa.h> - -/*For the currently not ported to Cocoa keyboard layout functions (64bit & 10.6 compatible)*/ -#include <Carbon/Carbon.h> - -#include <sys/time.h> -#include <sys/types.h> -#include <sys/sysctl.h> +#import <Foundation/Foundation.h> #include "GHOST_SystemPathsCocoa.h" diff --git a/intern/ghost/intern/GHOST_SystemPathsWin32.h b/intern/ghost/intern/GHOST_SystemPathsWin32.h index c074a7902b8..b63d20bfcbd 100644 --- a/intern/ghost/intern/GHOST_SystemPathsWin32.h +++ b/intern/ghost/intern/GHOST_SystemPathsWin32.h @@ -60,7 +60,7 @@ public: /** * Destructor. */ - virtual ~GHOST_SystemPathsWin32(); + ~GHOST_SystemPathsWin32(); /** * Determine the base dir in which shared resources are located. It will first try to use diff --git a/intern/ghost/intern/GHOST_SystemPathsX11.cpp b/intern/ghost/intern/GHOST_SystemPathsX11.cpp index e2d9733a9b2..5473e404593 100644 --- a/intern/ghost/intern/GHOST_SystemPathsX11.cpp +++ b/intern/ghost/intern/GHOST_SystemPathsX11.cpp @@ -121,7 +121,7 @@ const GHOST_TUns8 *GHOST_SystemPathsX11::getBinaryDir() const return NULL; } -void GHOST_SystemPathsX11::addToSystemRecentFiles(const char *filename) const +void GHOST_SystemPathsX11::addToSystemRecentFiles(const char * /*filename*/) const { /* XXXXX TODO: Implementation for X11 if possible */ diff --git a/intern/ghost/intern/GHOST_SystemPathsX11.h b/intern/ghost/intern/GHOST_SystemPathsX11.h index 1b5710e0f57..23cc2e4b104 100644 --- a/intern/ghost/intern/GHOST_SystemPathsX11.h +++ b/intern/ghost/intern/GHOST_SystemPathsX11.h @@ -48,7 +48,7 @@ public: /** * Destructor. */ - virtual ~GHOST_SystemPathsX11(); + ~GHOST_SystemPathsX11(); /** * Determine the base dir in which shared resources are located. It will first try to use diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp index 1d2401e6f71..e9768e4b845 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.cpp +++ b/intern/ghost/intern/GHOST_SystemSDL.cpp @@ -67,9 +67,8 @@ GHOST_SystemSDL::createWindow(const STR_String& title, GHOST_TUns32 height, GHOST_TWindowState state, GHOST_TDrawingContextType type, - const bool stereoVisual, + GHOST_GLSettings glSettings, const bool exclusive, - const GHOST_TUns16 numOfAASamples, const GHOST_TEmbedderWindowID parentWindow ) { @@ -78,8 +77,8 @@ GHOST_SystemSDL::createWindow(const STR_String& title, window = new GHOST_WindowSDL(this, title, left, top, width, height, state, parentWindow, type, - stereoVisual, exclusive, - numOfAASamples); + ((glSettings.flags & GHOST_glStereoVisual) != 0), exclusive, + glSettings.numOfAASamples); if (window) { if (GHOST_kWindowStateFullScreen == state) { @@ -321,8 +320,9 @@ GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window); break; } - } + break; + } case SDL_QUIT: g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL); break; @@ -418,8 +418,8 @@ GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) GHOST_WindowSDL *window = findGhostWindow(SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID)); assert(window != NULL); g_event = new GHOST_EventWheel(getMilliSeconds(), window, sdl_sub_evt.y); - } break; + } case SDL_KEYDOWN: case SDL_KEYUP: { @@ -489,8 +489,8 @@ GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) } g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, sym, NULL); - } break; + } } if (g_event) { diff --git a/intern/ghost/intern/GHOST_SystemSDL.h b/intern/ghost/intern/GHOST_SystemSDL.h index 5ad4fe09833..6f4ecec586b 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.h +++ b/intern/ghost/intern/GHOST_SystemSDL.h @@ -108,9 +108,8 @@ private: GHOST_TUns32 height, GHOST_TWindowState state, GHOST_TDrawingContextType type, - const bool stereoVisual, + GHOST_GLSettings glSettings, const bool exclusive = false, - const GHOST_TUns16 numOfAASamples = 0, const GHOST_TEmbedderWindowID parentWindow = 0 ); diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 3b95373f800..4f3b1127a18 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -31,11 +31,6 @@ * \author Maarten Gribnau */ -#ifdef WITH_GHOST_DEBUG -# include <iostream> -#endif - -#include <stdio.h> // [mce] temporary debug, remove soon! #include "GHOST_SystemWin32.h" #include "GHOST_EventDragnDrop.h" @@ -152,7 +147,7 @@ static void initRawInput() if (RegisterRawInputDevices(devices, DEVICE_COUNT, sizeof(RAWINPUTDEVICE))) ; // yay! else - printf("could not register for RawInput: %d\n", (int)GetLastError()); + GHOST_PRINTF("could not register for RawInput: %d\n", (int)GetLastError()); #undef DEVICE_COUNT } @@ -231,44 +226,37 @@ GHOST_IWindow *GHOST_SystemWin32::createWindow( GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, GHOST_TUns32 height, GHOST_TWindowState state, GHOST_TDrawingContextType type, - bool stereoVisual, + GHOST_GLSettings glSettings, const bool exclusive, - const GHOST_TUns16 numOfAASamples, const GHOST_TEmbedderWindowID parentWindow) { - GHOST_Window *window = 0; - window = new GHOST_WindowWin32(this, title, left, top, width, height, state, type, stereoVisual, numOfAASamples, parentWindow); - if (window) { - if (window->getValid()) { - // Store the pointer to the window -// if (state != GHOST_kWindowStateFullScreen) { - m_windowManager->addWindow(window); - m_windowManager->setActiveWindow(window); -// } - } - else { - - // Invalid parent window hwnd - if (((GHOST_WindowWin32 *)window)->getNextWindow() == NULL) { - delete window; - window = 0; - return window; - } - - // An invalid window could be one that was used to test for AA - window = ((GHOST_WindowWin32 *)window)->getNextWindow(); - - // If another window is found, let the wm know about that one, but not the old one - if (window->getValid()) { - m_windowManager->addWindow(window); - } - else { - delete window; - window = 0; - } - - } + GHOST_WindowWin32 *window = + new GHOST_WindowWin32( + this, + title, + left, + top, + width, + height, + state, + type, + ((glSettings.flags & GHOST_glStereoVisual) != 0), + ((glSettings.flags & GHOST_glWarnSupport) != 0), + glSettings.numOfAASamples, + parentWindow, + ((glSettings.flags & GHOST_glDebugContext) != 0)); + + if (window->getValid()) { + // Store the pointer to the window + m_windowManager->addWindow(window); + m_windowManager->setActiveWindow(window); } + else { + GHOST_PRINT("GHOST_SystemWin32::createWindow(): window invalid\n"); + delete window; + window = 0; + } + return window; } @@ -440,7 +428,7 @@ GHOST_TSuccess GHOST_SystemWin32::exit() return GHOST_System::exit(); } -GHOST_TKey GHOST_SystemWin32::hardKey(GHOST_IWindow *window, RAWINPUT const &raw, int *keyDown, char *vk) +GHOST_TKey GHOST_SystemWin32::hardKey(RAWINPUT const &raw, int *keyDown, char *vk) { GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); GHOST_TKey key = GHOST_kKeyUnknown; @@ -452,7 +440,7 @@ GHOST_TKey GHOST_SystemWin32::hardKey(GHOST_IWindow *window, RAWINPUT const &raw unsigned int msg = raw.data.keyboard.Message; *keyDown = !(raw.data.keyboard.Flags & RI_KEY_BREAK) && msg != WM_KEYUP && msg != WM_SYSKEYUP; - key = this->convertKey(window, raw.data.keyboard.VKey, raw.data.keyboard.MakeCode, (raw.data.keyboard.Flags & (RI_KEY_E1 | RI_KEY_E0))); + 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) { @@ -516,19 +504,24 @@ GHOST_TKey GHOST_SystemWin32::hardKey(GHOST_IWindow *window, RAWINPUT const &raw //! note: this function can be extended to include other exotic cases as they arise. // This function was added in response to bug [#25715] -GHOST_TKey GHOST_SystemWin32::processSpecialKey(GHOST_IWindow *window, short vKey, short scanCode) const +// This is going to be a long list [T42426] +GHOST_TKey GHOST_SystemWin32::processSpecialKey(short vKey, short scanCode) const { GHOST_TKey key = GHOST_kKeyUnknown; switch (PRIMARYLANGID(m_langId)) { case LANG_FRENCH: if (vKey == VK_OEM_8) key = GHOST_kKeyF13; // oem key; used purely for shortcuts . break; + case LANG_ENGLISH: + if (SUBLANGID(m_langId) == SUBLANG_ENGLISH_UK && vKey == VK_OEM_8) // "`¬" + key = GHOST_kKeyAccentGrave; + break; } return key; } -GHOST_TKey GHOST_SystemWin32::convertKey(GHOST_IWindow *window, short vKey, short scanCode, short extend) const +GHOST_TKey GHOST_SystemWin32::convertKey(short vKey, short scanCode, short extend) const { GHOST_TKey key; @@ -636,7 +629,7 @@ GHOST_TKey GHOST_SystemWin32::convertKey(GHOST_IWindow *window, short vKey, shor case VK_SCROLL: key = GHOST_kKeyScrollLock; break; case VK_CAPITAL: key = GHOST_kKeyCapsLock; break; case VK_OEM_8: - key = ((GHOST_SystemWin32 *)getSystem())->processSpecialKey(window, vKey, scanCode); + key = ((GHOST_SystemWin32 *)getSystem())->processSpecialKey(vKey, scanCode); break; case VK_MEDIA_PLAY_PAUSE: key = GHOST_kKeyMediaPlay; break; case VK_MEDIA_STOP: key = GHOST_kKeyMediaStop; break; @@ -653,18 +646,17 @@ GHOST_TKey GHOST_SystemWin32::convertKey(GHOST_IWindow *window, short vKey, shor GHOST_EventButton *GHOST_SystemWin32::processButtonEvent( GHOST_TEventType type, - GHOST_IWindow *window, + GHOST_WindowWin32 *window, GHOST_TButtonMask mask) { return new GHOST_EventButton(getSystem()->getMilliSeconds(), type, window, mask); } -GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_TEventType type, GHOST_IWindow *Iwindow) +GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_TEventType type, GHOST_WindowWin32 *window) { GHOST_TInt32 x_screen, y_screen; GHOST_SystemWin32 *system = (GHOST_SystemWin32 *) getSystem(); - GHOST_WindowWin32 *window = (GHOST_WindowWin32 *) Iwindow; system->getCursorPosition(x_screen, y_screen); @@ -716,27 +708,27 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_TEventType type, } -GHOST_EventWheel *GHOST_SystemWin32::processWheelEvent(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam) +GHOST_EventWheel *GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam) { // short fwKeys = LOWORD(wParam); // key flags int zDelta = (short) HIWORD(wParam); // wheel rotation // zDelta /= WHEEL_DELTA; // temporary fix below: microsoft now has added more precision, making the above division not work - if (zDelta <= 0) zDelta = -1; else zDelta = 1; - + zDelta = (zDelta <= 0) ? -1 : 1; + // short xPos = (short) LOWORD(lParam); // horizontal position of pointer // short yPos = (short) HIWORD(lParam); // vertical position of pointer return new GHOST_EventWheel(getSystem()->getMilliSeconds(), window, zDelta); } -GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_IWindow *window, RAWINPUT const &raw) +GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RAWINPUT const &raw) { int keyDown = 0; char vk; GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); - GHOST_TKey key = system->hardKey(window, raw, &keyDown, &vk); + GHOST_TKey key = system->hardKey(raw, &keyDown, &vk); GHOST_EventKey *event; if (key != GHOST_kKeyUnknown) { @@ -771,10 +763,8 @@ GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_IWindow *window, RAWINP } event = new GHOST_EventKey(system->getMilliSeconds(), keyDown ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, key, ascii, utf8_char); - -#ifdef GHOST_DEBUG - std::cout << ascii << std::endl; -#endif + + // GHOST_PRINTF("%c\n", ascii); // we already get this info via EventPrinter } else { event = 0; @@ -783,26 +773,35 @@ GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_IWindow *window, RAWINP } -GHOST_Event *GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type, GHOST_IWindow *window) +GHOST_Event *GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type, GHOST_WindowWin32 *window) { - GHOST_System *system = (GHOST_System *)getSystem(); + GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); if (type == GHOST_kEventWindowActivate) { system->getWindowManager()->setActiveWindow(window); - ((GHOST_WindowWin32 *)window)->bringTabletContextToFront(); + window->bringTabletContextToFront(); } return new GHOST_Event(system->getMilliSeconds(), type, window); } +#ifdef WITH_INPUT_IME +GHOST_Event *GHOST_SystemWin32::processImeEvent(GHOST_TEventType type, GHOST_WindowWin32 *window, GHOST_TEventImeData *data) +{ + GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); + return new GHOST_EventIME(system->getMilliSeconds(), type, window, data); +} +#endif + + GHOST_TSuccess GHOST_SystemWin32::pushDragDropEvent( GHOST_TEventType eventType, GHOST_TDragnDropTypes draggedObjectType, - GHOST_IWindow *window, + GHOST_WindowWin32 *window, int mouseX, int mouseY, void *data) { - GHOST_SystemWin32 *system = ((GHOST_SystemWin32 *)getSystem()); + GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); return system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(), eventType, draggedObjectType, @@ -832,7 +831,7 @@ bool GHOST_SystemWin32::processNDOF(RAWINPUT const &raw) if (info.dwType == RIM_TYPEHID) m_ndofManager->setDevice(info.hid.dwVendorId, info.hid.dwProductId); else - puts("<!> not a HID device... mouse/kb perhaps?"); + GHOST_PRINT("<!> not a HID device... mouse/kb perhaps?\n"); firstEvent = false; } @@ -877,7 +876,7 @@ bool GHOST_SystemWin32::processNDOF(RAWINPUT const &raw) m_ndofManager->updateRotation(r, now); // I've never gotten one of these, has anyone else? - puts("ndof: combined T + R"); + GHOST_PRINT("ndof: combined T + R\n"); } break; } @@ -906,7 +905,8 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, bool eventHandled = false; LRESULT lResult = 0; - GHOST_SystemWin32 *system = ((GHOST_SystemWin32 *)getSystem()); + GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); + GHOST_EventManager *eventManager = system->getEventManager(); GHOST_ASSERT(system, "GHOST_SystemWin32::s_wndProc(): system not initialized"); if (hwnd) { @@ -915,8 +915,13 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, switch (msg) { // we need to check if new key layout has AltGr case WM_INPUTLANGCHANGE: + { system->handleKeyboardChange(); +#ifdef WITH_INPUT_IME + window->getImeInput()->SetInputLanguage(); +#endif break; + } //////////////////////////////////////////////////////////////////////// // Keyboard events, processed //////////////////////////////////////////////////////////////////////// @@ -952,6 +957,64 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, } break; } +#ifdef WITH_INPUT_IME + //////////////////////////////////////////////////////////////////////// + // IME events, processed, read more in GHOST_IME.h + //////////////////////////////////////////////////////////////////////// + case WM_IME_SETCONTEXT: + { + GHOST_ImeWin32 *ime = window->getImeInput(); + ime->SetInputLanguage(); + ime->CreateImeWindow(hwnd); + ime->CleanupComposition(hwnd); + ime->CheckFirst(hwnd); + break; + } + case WM_IME_STARTCOMPOSITION: + { + GHOST_ImeWin32 *ime = window->getImeInput(); + eventHandled = true; + /* remove input event before start comp event, avoid redundant input */ + eventManager->removeTypeEvents(GHOST_kEventKeyDown, window); + ime->CreateImeWindow(hwnd); + ime->ResetComposition(hwnd); + event = processImeEvent( + GHOST_kEventImeCompositionStart, + window, + &ime->eventImeData); + break; + } + case WM_IME_COMPOSITION: + { + GHOST_ImeWin32 *ime = window->getImeInput(); + eventHandled = true; + ime->UpdateImeWindow(hwnd); + ime->UpdateInfo(hwnd); + if (ime->eventImeData.result_len) { + /* remove redundant IME event */ + eventManager->removeTypeEvents(GHOST_kEventImeComposition, window); + } + event = processImeEvent( + GHOST_kEventImeComposition, + window, + &ime->eventImeData); + break; + } + case WM_IME_ENDCOMPOSITION: + { + GHOST_ImeWin32 *ime = window->getImeInput(); + eventHandled = true; + /* remove input event after end comp event, avoid redundant input */ + eventManager->removeTypeEvents(GHOST_kEventKeyDown, window); + ime->ResetComposition(hwnd); + ime->DestroyImeWindow(hwnd); + event = processImeEvent( + GHOST_kEventImeCompositionEnd, + window, + &ime->eventImeData); + break; + } +#endif /* WITH_INPUT_IME */ //////////////////////////////////////////////////////////////////////// // Keyboard events, ignored //////////////////////////////////////////////////////////////////////// @@ -1002,11 +1065,11 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, // Tablet events, processed //////////////////////////////////////////////////////////////////////// case WT_PACKET: - ((GHOST_WindowWin32 *)window)->processWin32TabletEvent(wParam, lParam); + window->processWin32TabletEvent(wParam, lParam); break; case WT_CSRCHANGE: case WT_PROXIMITY: - ((GHOST_WindowWin32 *)window)->processWin32TabletInitEvent(); + window->processWin32TabletInitEvent(); break; //////////////////////////////////////////////////////////////////////// // Mouse events, processed @@ -1066,19 +1129,15 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, * until it finds a window that processes it. */ - /* Get the winow under the mouse and send event to it's queue. */ + /* Get the window under the mouse and send event to its queue. */ POINT mouse_pos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; - HWND mouse_hwnd = WindowFromPoint(mouse_pos); + HWND mouse_hwnd = ChildWindowFromPoint(HWND_DESKTOP, mouse_pos); GHOST_WindowWin32 *mouse_window = (GHOST_WindowWin32 *)::GetWindowLongPtr(mouse_hwnd, GWLP_USERDATA); if (mouse_window != NULL) { event = processWheelEvent(mouse_window, wParam, lParam); } else { - /* If it happened so window under the mouse is not found (which i'm not - * really sure might happen), then we add event to the focused window - * in order to avoid some possible negative side effects. - * - sergey - - */ + /* Happens when mouse is not over any of blender's windows. */ event = processWheelEvent(window, wParam, lParam); } diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h index 79fed06c6a5..0a8837294db 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.h +++ b/intern/ghost/intern/GHOST_SystemWin32.h @@ -53,6 +53,8 @@ class GHOST_EventWheel; class GHOST_EventWindow; class GHOST_EventDragnDrop; +class GHOST_WindowWin32; + /** * WIN32 Implementation of GHOST_System class. * \see GHOST_System. @@ -69,7 +71,7 @@ public: /** * Destructor. */ - virtual ~GHOST_SystemWin32(); + ~GHOST_SystemWin32(); /*************************************************************************************** ** Time(r) functionality @@ -81,7 +83,7 @@ public: * This overloaded method uses the high frequency timer if available. * \return The number of milliseconds. */ - virtual GHOST_TUns64 getMilliSeconds() const; + GHOST_TUns64 getMilliSeconds() const; /*************************************************************************************** ** Display/window management functionality @@ -91,19 +93,19 @@ public: * Returns the number of displays on this system. * \return The number of displays. */ - virtual GHOST_TUns8 getNumDisplays() const; + GHOST_TUns8 getNumDisplays() const; /** * Returns the dimensions of the main display on this system. * \return The dimension of the main display. */ - virtual void getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const; + void getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const; /** * Returns the dimensions of all displays on this system. * \return The dimension of the main display. */ - virtual void getAllDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const; + void getAllDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const; /** * Create a new window. @@ -116,18 +118,17 @@ public: * \param height The height the window. * \param state The state of the window when opened. * \param type The type of drawing context installed in this window. - * \param stereoVisual Stereo visual for quad buffered stereo. - * \param numOfAASamples Number of samples used for AA (zero if no AA) + * \param glSettings: Misc OpenGL settings. + * \param exclusive: Use to show the window ontop and ignore others (used fullscreen). * \param parentWindow Parent (embedder) window * \return The new window (or 0 if creation failed). */ - virtual GHOST_IWindow *createWindow( + GHOST_IWindow *createWindow( const STR_String& title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, GHOST_TUns32 height, GHOST_TWindowState state, GHOST_TDrawingContextType type, - const bool stereoVisual = false, - const bool exclusive = false, - const GHOST_TUns16 numOfAASamples = 0, + GHOST_GLSettings glSettings, + const bool exclusive = false, const GHOST_TEmbedderWindowID parentWindow = 0); /*************************************************************************************** @@ -139,7 +140,7 @@ public: * \param waitForEvent Flag to wait for an event (or return immediately). * \return Indication of the presence of events. */ - virtual bool processEvents(bool waitForEvent); + bool processEvents(bool waitForEvent); /*************************************************************************************** @@ -152,7 +153,7 @@ public: * \param y The y-coordinate of the cursor. * \return Indication of success. */ - virtual GHOST_TSuccess getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const; + GHOST_TSuccess getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const; /** * Updates the location of the cursor (location in screen coordinates). @@ -160,7 +161,7 @@ public: * \param y The y-coordinate of the cursor. * \return Indication of success. */ - virtual GHOST_TSuccess setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y); + GHOST_TSuccess setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y); /*************************************************************************************** ** Access to mouse button and keyboard states. @@ -171,28 +172,28 @@ public: * \param keys The state of all modifier keys (true == pressed). * \return Indication of success. */ - virtual GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys& keys) const; + GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys& keys) const; /** * Returns the state of the mouse buttons (ouside the message queue). * \param buttons The state of the buttons. * \return Indication of success. */ - virtual GHOST_TSuccess getButtons(GHOST_Buttons& buttons) const; + GHOST_TSuccess getButtons(GHOST_Buttons& buttons) const; /** - * Returns unsinged char from CUT_BUFFER0 + * Returns unsigned char from CUT_BUFFER0 * \param selection Used by X11 only * \return Returns the Clipboard */ - virtual GHOST_TUns8 *getClipboard(bool selection) const; + GHOST_TUns8 *getClipboard(bool selection) const; /** * Puts buffer to system clipboard * \param selection Used by X11 only * \return No return */ - virtual void putClipboard(GHOST_TInt8 *buffer, bool selection) const; + void putClipboard(GHOST_TInt8 *buffer, bool selection) const; /** * Creates a drag'n'drop event and pushes it immediately onto the event queue. @@ -204,13 +205,13 @@ public: * \param window The window on which the event occurred * \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); + 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 */ - virtual int confirmQuit(GHOST_IWindow *window) const; + int confirmQuit(GHOST_IWindow *window) const; protected: /** @@ -218,42 +219,31 @@ protected: * For now, it just registers the window class (WNDCLASS). * \return A success value. */ - virtual GHOST_TSuccess init(); + GHOST_TSuccess init(); /** * Closes the system down. * \return A success value. */ - virtual GHOST_TSuccess exit(); + GHOST_TSuccess exit(); /** * Converts raw WIN32 key codes from the wndproc to GHOST keys. - * \param window-> The window for this handling * \param vKey The virtual key from hardKey - * \param ScanCode The ScanCode of pressed key (simular to PS/2 Set 1) + * \param ScanCode The ScanCode of pressed key (similar to PS/2 Set 1) * \param extend Flag if key is not primly (left or right) * \return The GHOST key (GHOST_kKeyUnknown if no match). */ - virtual GHOST_TKey convertKey(GHOST_IWindow *window, short vKey, short ScanCode, short extend) const; + GHOST_TKey convertKey(short vKey, short ScanCode, short extend) const; /** * Catches raw WIN32 key codes from WM_INPUT in the wndproc. - * \param window The window for this handling * \param raw RawInput structure with detailed info about the key event * \param keyDown Pointer flag that specify if a key is down * \param vk Pointer to virtual key * \return The GHOST key (GHOST_kKeyUnknown if no match). */ - virtual GHOST_TKey hardKey(GHOST_IWindow *window, RAWINPUT const& raw, int *keyDown, char *vk); - - /** - * Creates modifier key event(s) and updates the key data stored locally (m_modifierKeys). - * With the modifier keys, we want to distinguish left and right keys. - * Sometimes this is not possible (Windows ME for instance). Then, we want - * events generated for both keys. - * \param window The window receiving the event (the active window). - */ - GHOST_EventKey *processModifierKeys(GHOST_IWindow *window); + GHOST_TKey hardKey(RAWINPUT const& raw, int *keyDown, char *vk); /** * Creates mouse button event. @@ -262,7 +252,7 @@ protected: * \param mask The button mask of this event. * \return The event created. */ - static GHOST_EventButton *processButtonEvent(GHOST_TEventType type, GHOST_IWindow *window, GHOST_TButtonMask mask); + static GHOST_EventButton *processButtonEvent(GHOST_TEventType type, GHOST_WindowWin32 *window, GHOST_TButtonMask mask); /** * Creates cursor event. @@ -270,7 +260,7 @@ protected: * \param window The window receiving the event (the active window). * \return The event created. */ - static GHOST_EventCursor *processCursorEvent(GHOST_TEventType type, GHOST_IWindow *Iwindow); + static GHOST_EventCursor *processCursorEvent(GHOST_TEventType type, GHOST_WindowWin32 *window); /** * Creates a mouse wheel event. @@ -278,7 +268,7 @@ protected: * \param wParam The wParam from the wndproc * \param lParam The lParam from the wndproc */ - static GHOST_EventWheel *processWheelEvent(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam); + static GHOST_EventWheel *processWheelEvent(GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam); /** * Creates a key event and updates the key data stored locally (m_modifierKeys). @@ -287,16 +277,15 @@ protected: * \param window The window receiving the event (the active window). * \param raw RawInput structure with detailed info about the key event */ - static GHOST_EventKey *processKeyEvent(GHOST_IWindow *window, RAWINPUT const& raw); + static GHOST_EventKey *processKeyEvent(GHOST_WindowWin32 *window, RAWINPUT const& raw); /** * Process special keys (VK_OEM_*), to see if current key layout * gives us anything special, like ! on french AZERTY. - * \param window The window receiving the event (the active window). * \param vKey The virtual key from hardKey - * \param ScanCode The ScanCode of pressed key (simular to PS/2 Set 1) + * \param scanCode The ScanCode of pressed key (simular to PS/2 Set 1) */ - virtual GHOST_TKey processSpecialKey(GHOST_IWindow *window, short vKey, short scanCode) const; + GHOST_TKey processSpecialKey(short vKey, short scanCode) const; /** * Creates a window event. @@ -304,7 +293,18 @@ protected: * \param window The window receiving the event (the active window). * \return The event created. */ - static GHOST_Event *processWindowEvent(GHOST_TEventType type, GHOST_IWindow *window); + static GHOST_Event *processWindowEvent(GHOST_TEventType type, GHOST_WindowWin32 *window); + +#ifdef WITH_INPUT_IME + /** + * Creates a IME event. + * \param type The type of event to create. + * \param window The window receiving the event (the active window). + * \param data IME data. + * \return The event created. + */ + static GHOST_Event *processImeEvent(GHOST_TEventType type, GHOST_WindowWin32 *window, GHOST_TEventImeData *data); +#endif // WITH_INPUT_IME /** * Handles minimum window size. @@ -327,19 +327,19 @@ protected: * Returns the local state of the modifier keys (from the message queue). * \param keys The state of the keys. */ - inline virtual void retrieveModifierKeys(GHOST_ModifierKeys& keys) const; + inline void retrieveModifierKeys(GHOST_ModifierKeys& keys) const; /** * Stores the state of the modifier keys locally. * For internal use only! * param keys The new state of the modifier keys. */ - inline virtual void storeModifierKeys(const GHOST_ModifierKeys& keys); + inline void storeModifierKeys(const GHOST_ModifierKeys& keys); /** * Check current key layout for AltGr */ - inline virtual void handleKeyboardChange(void); + inline void handleKeyboardChange(void); /** * Windows call back routine for our window class. diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index f797d8074e1..325cba0c631 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -32,6 +32,11 @@ * \ingroup GHOST */ +#include <X11/Xatom.h> +#include <X11/keysym.h> +#include <X11/XKBlib.h> /* allow detectable autorepeate */ +#include <X11/Xutil.h> + #include "GHOST_SystemX11.h" #include "GHOST_WindowX11.h" #include "GHOST_WindowManager.h" @@ -52,10 +57,6 @@ #include "GHOST_Debug.h" -#include <X11/Xatom.h> -#include <X11/keysym.h> -#include <X11/XKBlib.h> /* allow detectable autorepeate */ - #ifdef WITH_XF86KEYSYM #include <X11/XF86keysym.h> #endif @@ -279,40 +280,33 @@ getAllDisplayDimensions( * \param height The height the window. * \param state The state of the window when opened. * \param type The type of drawing context installed in this window. - * \param stereoVisual Stereo visual for quad buffered stereo. - * \param exclusive Use to show the window ontop and ignore others - * (used fullscreen). - * \param numOfAASamples Number of samples used for AA (zero if no AA) + * \param glSettings: Misc OpenGL settings. + * \param exclusive: Use to show the window ontop and ignore others (used fullscreen). * \param parentWindow Parent (embedder) window * \return The new window (or 0 if creation failed). */ GHOST_IWindow * GHOST_SystemX11:: -createWindow( - const STR_String& title, +createWindow(const STR_String& title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, GHOST_TUns32 height, GHOST_TWindowState state, GHOST_TDrawingContextType type, - const bool stereoVisual, + GHOST_GLSettings glSettings, const bool exclusive, - const GHOST_TUns16 numOfAASamples, const GHOST_TEmbedderWindowID parentWindow) { GHOST_WindowX11 *window = 0; if (!m_display) return 0; - - - window = new GHOST_WindowX11(this, m_display, title, left, top, width, height, state, parentWindow, type, - stereoVisual, exclusive, - numOfAASamples); + ((glSettings.flags & GHOST_glStereoVisual) != 0), exclusive, + glSettings.numOfAASamples, (glSettings.flags & GHOST_glDebugContext) != 0); if (window) { /* Both are now handle in GHOST_WindowX11.cpp @@ -333,7 +327,7 @@ createWindow( } #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) -static void destroyIMCallback(XIM xim, XPointer ptr, XPointer data) +static void destroyIMCallback(XIM /*xim*/, XPointer ptr, XPointer /*data*/) { GHOST_PRINT("XIM server died\n"); @@ -1180,7 +1174,11 @@ GHOST_SystemX11::processEvent(XEvent *xe) default: { #ifdef WITH_X11_XINPUT - if (xe->type == m_xtablet.MotionEvent) { + if (xe->type == m_xtablet.MotionEvent || + xe->type == m_xtablet.MotionEventEraser || + xe->type == m_xtablet.PressEvent || + xe->type == m_xtablet.PressEventEraser) + { XDeviceMotionEvent *data = (XDeviceMotionEvent *)xe; const unsigned char axis_first = data->first_axis; const unsigned char axes_end = axis_first + data->axes_count; /* after the last */ @@ -1873,13 +1871,16 @@ GHOST_TSuccess GHOST_SystemX11::pushDragDropEvent(GHOST_TEventType eventType, } #endif -#ifdef WITH_X11_XINPUT -/* +#if defined(USE_X11_ERROR_HANDLERS) || defined(WITH_X11_XINPUT) +/* + * These callbacks can be used for debugging, so we can breakpoint on an X11 error. + + * * Dummy function to get around IO Handler exiting if device invalid * Basically it will not crash blender now if you have a X device that * is configured but not plugged in. */ -int GHOST_X11_ApplicationErrorHandler(Display *display, XErrorEvent *theEvent) +int GHOST_X11_ApplicationErrorHandler(Display * /*display*/, XErrorEvent *theEvent) { fprintf(stderr, "Ignoring Xlib error: error code %d request code %d\n", theEvent->error_code, theEvent->request_code); @@ -1888,14 +1889,16 @@ int GHOST_X11_ApplicationErrorHandler(Display *display, XErrorEvent *theEvent) return 0; } -int GHOST_X11_ApplicationIOErrorHandler(Display *display) +int GHOST_X11_ApplicationIOErrorHandler(Display * /*display*/) { fprintf(stderr, "Ignoring Xlib error: error IO\n"); /* No exit! - but keep lint happy */ return 0; } +#endif +#ifdef WITH_X11_XINPUT /* These C functions are copied from Wine 1.1.13's wintab.c */ #define BOOL int #define TRUE 1 diff --git a/intern/ghost/intern/GHOST_SystemX11.h b/intern/ghost/intern/GHOST_SystemX11.h index a21300e36f0..a0088dbe8f0 100644 --- a/intern/ghost/intern/GHOST_SystemX11.h +++ b/intern/ghost/intern/GHOST_SystemX11.h @@ -34,7 +34,6 @@ #define __GHOST_SYSTEMX11_H__ #include <X11/Xlib.h> -#include <GL/glx.h> #include "GHOST_System.h" #include "../GHOST_Types.h" @@ -53,6 +52,7 @@ int GHOST_X11_ApplicationErrorHandler(Display *display, XErrorEvent *theEvent); int GHOST_X11_ApplicationIOErrorHandler(Display *display); + class GHOST_WindowX11; /** @@ -76,7 +76,7 @@ public: /** * Destructor. */ - virtual ~GHOST_SystemX11(); + ~GHOST_SystemX11(); GHOST_TSuccess @@ -152,17 +152,12 @@ public: GHOST_TUns32 height, GHOST_TWindowState state, GHOST_TDrawingContextType type, - const bool stereoVisual, + GHOST_GLSettings glSettings, const bool exclusive = false, - const GHOST_TUns16 numOfAASamples = 0, const GHOST_TEmbedderWindowID parentWindow = 0 ); /** - * \section Interface Inherited from GHOST_ISystem - */ - - /** * Retrieves events from the system and stores them in the queue. * \param waitForEvent Flag to wait for an event (or return immediately). * \return Indication of the presence of events. @@ -172,9 +167,6 @@ public: bool waitForEvent ); - /** - * \section Interface Inherited from GHOST_System - */ GHOST_TSuccess getCursorPosition( GHOST_TInt32& x, @@ -208,7 +200,6 @@ public: ) const; /** - * \section Interface Dirty * Flag a window as dirty. This will * generate a GHOST window update event on a call to processEvents() */ @@ -243,7 +234,7 @@ public: unsigned int *context) const; /** - * Returns unsinged char from CUT_BUFFER0 + * Returns unsigned char from CUT_BUFFER0 * \param selection Get selection, X11 only feature * \return Returns the Clipboard indicated by Flag */ @@ -273,7 +264,7 @@ public: /** * \see GHOST_ISystem */ - int toggleConsole(int action) { + int toggleConsole(int /*action*/) { return 0; } @@ -287,6 +278,12 @@ public: int MotionEvent; int ProxInEvent; int ProxOutEvent; + int PressEvent; + + int MotionEventEraser; + int ProxInEventEraser; + int ProxOutEventEraser; + int PressEventEraser; int PressureLevels; int XtiltLevels, YtiltLevels; diff --git a/intern/ghost/intern/GHOST_TimerManager.h b/intern/ghost/intern/GHOST_TimerManager.h index b94175e9ff8..6cf4bcf40eb 100644 --- a/intern/ghost/intern/GHOST_TimerManager.h +++ b/intern/ghost/intern/GHOST_TimerManager.h @@ -58,19 +58,19 @@ public: /** * Destructor. */ - virtual ~GHOST_TimerManager(); + ~GHOST_TimerManager(); /** * Returns the number of timer tasks. * \return The number of events on the stack. */ - virtual GHOST_TUns32 getNumTimers(); + GHOST_TUns32 getNumTimers(); /** * Returns whether this timer task ins in our list. * \return Indication of presence. */ - virtual bool getTimerFound(GHOST_TimerTask *timer); + bool getTimerFound(GHOST_TimerTask *timer); /** * Adds a timer task to the list. @@ -78,7 +78,7 @@ public: * \param timer The timer task added to the list. * \return Indication as to whether addition has succeeded. */ - virtual GHOST_TSuccess addTimer(GHOST_TimerTask *timer); + GHOST_TSuccess addTimer(GHOST_TimerTask *timer); /** * Removes a timer task from the list. @@ -86,21 +86,21 @@ public: * \param timer The timer task to be removed from the list. * \return Indication as to whether removal has succeeded. */ - virtual GHOST_TSuccess removeTimer(GHOST_TimerTask *timer); + GHOST_TSuccess removeTimer(GHOST_TimerTask *timer); /** * Finds the soonest time the next timer would fire. * \return The soonest time the next timer would fire, * or GHOST_kFireTimeNever if no timers exist. */ - virtual GHOST_TUns64 nextFireTime(); + GHOST_TUns64 nextFireTime(); /** * Checks all timer tasks to see if they are expired and fires them if needed. * \param time The current time. * \return True if any timers were fired. */ - virtual bool fireTimers(GHOST_TUns64 time); + bool fireTimers(GHOST_TUns64 time); /** * Checks this timer task to see if they are expired and fires them if needed. @@ -108,7 +108,7 @@ public: * \param task The timer task to check and optionally fire. * \return True if the timer fired. */ - virtual bool fireTimer(GHOST_TUns64 time, GHOST_TimerTask *task); + bool fireTimer(GHOST_TUns64 time, GHOST_TimerTask *task); protected: /** diff --git a/intern/ghost/intern/GHOST_TimerTask.h b/intern/ghost/intern/GHOST_TimerTask.h index e33788ec826..45aa66e4630 100644 --- a/intern/ghost/intern/GHOST_TimerTask.h +++ b/intern/ghost/intern/GHOST_TimerTask.h @@ -49,7 +49,7 @@ public: * \param start The timer start time. * \param interval The interval between calls to the timerProc * \param timerProc The callback invoked when the interval expires. - * \param data The timer user data. + * \param userData: The timer user data. */ GHOST_TimerTask(GHOST_TUns64 start, GHOST_TUns64 interval, @@ -68,7 +68,7 @@ public: * Returns the timer start time. * \return The timer start time. */ - inline virtual GHOST_TUns64 getStart() const + inline GHOST_TUns64 getStart() const { return m_start; } @@ -77,7 +77,7 @@ public: * Changes the timer start time. * \param start The timer start time. */ - virtual void setStart(GHOST_TUns64 start) + void setStart(GHOST_TUns64 start) { m_start = start; } @@ -86,7 +86,7 @@ public: * Returns the timer interval. * \return The timer interval. */ - inline virtual GHOST_TUns64 getInterval() const + inline GHOST_TUns64 getInterval() const { return m_interval; } @@ -95,7 +95,7 @@ public: * Changes the timer interval. * \param interval The timer interval. */ - virtual void setInterval(GHOST_TUns64 interval) + void setInterval(GHOST_TUns64 interval) { m_interval = interval; } @@ -104,7 +104,7 @@ public: * Returns the time the timerProc will be called. * \return The time the timerProc will be called. */ - inline virtual GHOST_TUns64 getNext() const + inline GHOST_TUns64 getNext() const { return m_next; } @@ -113,7 +113,7 @@ public: * Changes the time the timerProc will be called. * \param next The time the timerProc will be called. */ - virtual void setNext(GHOST_TUns64 next) + void setNext(GHOST_TUns64 next) { m_next = next; } @@ -122,16 +122,16 @@ public: * Returns the timer callback. * \return the timer callback. */ - inline virtual GHOST_TimerProcPtr getTimerProc() const + inline GHOST_TimerProcPtr getTimerProc() const { return m_timerProc; } /** * Changes the timer callback. - * \param The timer callback. + * \param timerProc: The timer callback. */ - inline virtual void setTimerProc(const GHOST_TimerProcPtr timerProc) + inline void setTimerProc(const GHOST_TimerProcPtr timerProc) { m_timerProc = timerProc; } @@ -140,16 +140,16 @@ public: * Returns the timer user data. * \return The timer user data. */ - inline virtual GHOST_TUserDataPtr getUserData() const + inline GHOST_TUserDataPtr getUserData() const { return m_userData; } /** * Changes the time user data. - * \param data The timer user data. + * \param userData: The timer user data. */ - virtual void setUserData(const GHOST_TUserDataPtr userData) + void setUserData(const GHOST_TUserDataPtr userData) { m_userData = userData; } @@ -158,7 +158,7 @@ public: * Returns the auxiliary storage room. * \return The auxiliary storage room. */ - inline virtual GHOST_TUns32 getAuxData() const + inline GHOST_TUns32 getAuxData() const { return m_auxData; } @@ -167,7 +167,7 @@ public: * Changes the auxiliary storage room. * \param auxData The auxiliary storage room. */ - virtual void setAuxData(GHOST_TUns32 auxData) + void setAuxData(GHOST_TUns32 auxData) { m_auxData = auxData; } diff --git a/intern/ghost/intern/GHOST_Window.cpp b/intern/ghost/intern/GHOST_Window.cpp index 3673831c7b1..71fa260f0f2 100644 --- a/intern/ghost/intern/GHOST_Window.cpp +++ b/intern/ghost/intern/GHOST_Window.cpp @@ -38,21 +38,23 @@ #include "GHOST_Window.h" +#include "GHOST_ContextNone.h" + #include <assert.h> GHOST_Window::GHOST_Window( GHOST_TUns32 width, GHOST_TUns32 height, GHOST_TWindowState state, - GHOST_TDrawingContextType type, - const bool stereoVisual, - const bool exclusive, - const GHOST_TUns16 numOfAASamples) - : m_drawingContextType(type), + const bool wantStereoVisual, + const bool /*exclusive*/, + const GHOST_TUns16 wantNumOfAASamples) + : m_drawingContextType(GHOST_kDrawingContextTypeNone), m_cursorVisible(true), m_cursorGrab(GHOST_kGrabDisable), m_cursorShape(GHOST_kStandardCursorDefault), - m_stereoVisual(stereoVisual), - m_numOfAASamples(numOfAASamples) + m_wantStereoVisual(wantStereoVisual), + m_wantNumOfAASamples(wantNumOfAASamples), + m_context(new GHOST_ContextNone(false, 0)) { m_isUnsavedChanges = false; m_canAcceptDragOperation = false; @@ -74,6 +76,7 @@ GHOST_Window::GHOST_Window( GHOST_Window::~GHOST_Window() { + delete m_context; } void *GHOST_Window::getOSWindow() const @@ -83,18 +86,61 @@ void *GHOST_Window::getOSWindow() const GHOST_TSuccess GHOST_Window::setDrawingContextType(GHOST_TDrawingContextType type) { - GHOST_TSuccess success = GHOST_kSuccess; if (type != m_drawingContextType) { - success = removeDrawingContext(); - if (success) { - success = installDrawingContext(type); + delete m_context; + m_context = NULL; + + if (type != GHOST_kDrawingContextTypeNone) + m_context = newDrawingContext(type); + + if (m_context != NULL) { m_drawingContextType = type; } else { + m_context = new GHOST_ContextNone(m_wantStereoVisual, m_wantNumOfAASamples); m_drawingContextType = GHOST_kDrawingContextTypeNone; } + + return (type == m_drawingContextType) ? GHOST_kSuccess : GHOST_kFailure; + } + else { + return GHOST_kSuccess; } - return success; +} + +GHOST_TSuccess GHOST_Window::swapBuffers() +{ + return m_context->swapBuffers(); +} + +GHOST_TSuccess GHOST_Window::setSwapInterval(int interval) +{ + return m_context->setSwapInterval(interval); +} + +GHOST_TSuccess GHOST_Window::getSwapInterval(int& intervalOut) +{ + return m_context->getSwapInterval(intervalOut); +} + +GHOST_TUns16 GHOST_Window::getNumOfAASamples() +{ + return m_context->getNumOfAASamples(); +} + +GHOST_TSuccess GHOST_Window::activateDrawingContext() +{ + return m_context->activateDrawingContext(); +} + +GHOST_TSuccess GHOST_Window::updateDrawingContext() +{ + return m_context->updateDrawingContext(); +} + +GHOST_TSuccess GHOST_Window::releaseNativeHandles() +{ + return m_context->releaseNativeHandles(); } GHOST_TSuccess GHOST_Window::setCursorVisibility(bool visible) diff --git a/intern/ghost/intern/GHOST_Window.h b/intern/ghost/intern/GHOST_Window.h index 77ee4db8543..52cb9cbf54b 100644 --- a/intern/ghost/intern/GHOST_Window.h +++ b/intern/ghost/intern/GHOST_Window.h @@ -36,6 +36,7 @@ #include "GHOST_IWindow.h" class STR_String; +class GHOST_Context; /** * Platform independent implementation of GHOST_IWindow. @@ -49,29 +50,6 @@ class STR_String; class GHOST_Window : public GHOST_IWindow { public: - /** - * \section Interface inherited from GHOST_IWindow left for derived class - * implementation. - * virtual bool getValid() const = 0; - * virtual void setTitle(const STR_String& title) = 0; - * virtual void getTitle(STR_String& title) const = 0; - * virtual void getWindowBounds(GHOST_Rect& bounds) const = 0; - * virtual void getClientBounds(GHOST_Rect& bounds) const = 0; - * virtual GHOST_TSuccess setClientWidth(GHOST_TUns32 width) = 0; - * virtual GHOST_TSuccess setClientHeight(GHOST_TUns32 height) = 0; - * virtual GHOST_TSuccess setClientSize(GHOST_TUns32 width, GHOST_TUns32 height) = 0; - * virtual void screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const = 0; - * virtual void clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const = 0; - * virtual GHOST_TWindowState getState() const = 0; - * virtual GHOST_TSuccess setState(GHOST_TWindowState state) = 0; - * virtual GHOST_TWindowOrder getOrder(void) = 0; - * virtual GHOST_TSuccess setOrder(GHOST_TWindowOrder order) = 0; - * virtual GHOST_TSuccess swapBuffers() = 0; - * virtual GHOST_TSuccess setSwapInterval() = 0; - * virtual int getSwapInterval() = 0; - * virtual GHOST_TSuccess activateDrawingContext() = 0; - * virtual GHOST_TSuccess invalidate() = 0; - */ /** * Constructor. @@ -90,10 +68,9 @@ public: GHOST_TUns32 width, GHOST_TUns32 height, GHOST_TWindowState state, - GHOST_TDrawingContextType type = GHOST_kDrawingContextTypeNone, - const bool stereoVisual = false, + const bool wantStereoVisual = false, const bool exclusive = false, - const GHOST_TUns16 numOfAASamples = 0); + const GHOST_TUns16 wantNumOfAASamples = 0); /** * \section Interface inherited from GHOST_IWindow left for derived class @@ -113,7 +90,7 @@ public: * virtual GHOST_TSuccess setOrder(GHOST_TWindowOrder order) = 0; * virtual GHOST_TSuccess swapBuffers() = 0; * virtual GHOST_TSuccess setSwapInterval() = 0; - * virtual int getSwapInterval() = 0; + * virtual GHOST_TSuccess getSwapInterval(int& intervalOut) = 0; * virtual GHOST_TSuccess activateDrawingContext() = 0; * virtual GHOST_TSuccess invalidate() = 0; */ @@ -125,6 +102,14 @@ public: virtual ~GHOST_Window(); /** + * Returns indication as to whether the window is valid. + * \return The validity of the window. + */ + virtual bool getValid() const { + return m_context != 0; + } + + /** * Returns the associated OS object/handle * \return The associated OS object/handle */ @@ -134,14 +119,14 @@ public: * Returns the current cursor shape. * \return The current cursor shape. */ - inline virtual GHOST_TStandardCursor getCursorShape() const; + inline GHOST_TStandardCursor getCursorShape() const; /** * Set the shape of the cursor. - * \param cursor The new cursor shape type id. + * \param cursorShape: The new cursor shape type id. * \return Indication of success. */ - virtual GHOST_TSuccess setCursorShape(GHOST_TStandardCursor cursorShape); + GHOST_TSuccess setCursorShape(GHOST_TStandardCursor cursorShape); /** * Set the shape of the cursor to a custom cursor. @@ -151,53 +136,53 @@ public: * \param hotY The Y coordinate of the cursor hotspot. * \return Indication of success. */ - virtual GHOST_TSuccess setCustomCursorShape(GHOST_TUns8 bitmap[16][2], - GHOST_TUns8 mask[16][2], - int hotX, - int hotY); - - virtual GHOST_TSuccess setCustomCursorShape(GHOST_TUns8 *bitmap, - GHOST_TUns8 *mask, - int sizex, int sizey, - int hotX, int hotY, - int fg_color, int bg_color); - + GHOST_TSuccess setCustomCursorShape(GHOST_TUns8 bitmap[16][2], + GHOST_TUns8 mask[16][2], + int hotX, + int hotY); + + GHOST_TSuccess setCustomCursorShape(GHOST_TUns8 *bitmap, + GHOST_TUns8 *mask, + int sizex, int sizey, + int hotX, int hotY, + int fg_color, int bg_color); + /** * Returns the visibility state of the cursor. * \return The visibility state of the cursor. */ - inline virtual bool getCursorVisibility() const; - inline virtual GHOST_TGrabCursorMode getCursorGrabMode() const; - inline virtual bool getCursorGrabModeIsWarp() const; - inline virtual void getCursorGrabInitPos(GHOST_TInt32 &x, GHOST_TInt32 &y) const; - inline virtual void getCursorGrabAccum(GHOST_TInt32 &x, GHOST_TInt32 &y) const; - inline virtual void setCursorGrabAccum(GHOST_TInt32 x, GHOST_TInt32 y); + inline bool getCursorVisibility() const; + inline GHOST_TGrabCursorMode getCursorGrabMode() const; + inline bool getCursorGrabModeIsWarp() const; + inline void getCursorGrabInitPos(GHOST_TInt32 &x, GHOST_TInt32 &y) const; + inline void getCursorGrabAccum(GHOST_TInt32 &x, GHOST_TInt32 &y) const; + inline void setCursorGrabAccum(GHOST_TInt32 x, GHOST_TInt32 y); /** * Shows or hides the cursor. * \param visible The new visibility state of the cursor. * \return Indication of success. */ - virtual GHOST_TSuccess setCursorVisibility(bool visible); + GHOST_TSuccess setCursorVisibility(bool visible); /** * Sets the cursor grab. * \param mode The new grab state of the cursor. * \return Indication of success. */ - virtual GHOST_TSuccess setCursorGrab(GHOST_TGrabCursorMode mode, GHOST_Rect *bounds, GHOST_TInt32 mouse_ungrab_xy[2]); + GHOST_TSuccess setCursorGrab(GHOST_TGrabCursorMode mode, GHOST_Rect *bounds, GHOST_TInt32 mouse_ungrab_xy[2]); /** * Gets the cursor grab region, if unset the window is used. * reset when grab is disabled. */ - virtual GHOST_TSuccess getCursorGrabBounds(GHOST_Rect& bounds); + GHOST_TSuccess getCursorGrabBounds(GHOST_Rect& bounds); /** * Sets the progress bar value displayed in the window/application icon * \param progress The progress % (0.0 to 1.0) */ - virtual GHOST_TSuccess setProgressBar(float progress) { + virtual GHOST_TSuccess setProgressBar(float /*progress*/) { return GHOST_kFailure; } @@ -213,28 +198,30 @@ public: * \param interval The swap interval to use. * \return A boolean success indicator. */ - virtual GHOST_TSuccess setSwapInterval(int interval) { - return GHOST_kFailure; - } - + GHOST_TSuccess setSwapInterval(int interval); + /** * Gets the current swap interval for swapBuffers. * \return An integer. */ - virtual int getSwapInterval() { - return 0; - } - + GHOST_TSuccess getSwapInterval(int& intervalOut); + + /** + * Gets the current swap interval for swapBuffers. + * \return Number of AA Samples (0 if there is no multisample buffer) + */ + GHOST_TUns16 getNumOfAASamples(); + /** * Tells if the ongoing drag'n'drop object can be accepted upon mouse drop */ - virtual void setAcceptDragOperation(bool canAccept); + void setAcceptDragOperation(bool canAccept); /** * Returns acceptance of the dropped object * Usually called by the "object dropped" event handling function */ - virtual bool canAcceptDragOperation() const; + bool canAcceptDragOperation() const; /** * Sets the window "modified" status, indicating unsaved changes @@ -253,55 +240,84 @@ public: * Returns the type of drawing context used in this window. * \return The current type of drawing context. */ - inline virtual GHOST_TDrawingContextType getDrawingContextType(); + inline GHOST_TDrawingContextType getDrawingContextType(); /** * Tries to install a rendering context in this window. - * Child classes do not need to overload this method. - * They should overload the installDrawingContext and removeDrawingContext instead. + * Child classes do not need to overload this method, + * They should overload newDrawingContext instead. * \param type The type of rendering context installed. * \return Indication as to whether installation has succeeded. */ - virtual GHOST_TSuccess setDrawingContextType(GHOST_TDrawingContextType type); + GHOST_TSuccess setDrawingContextType(GHOST_TDrawingContextType type); + + /** + * Swaps front and back buffers of a window. + * \return A boolean success indicator. + */ + virtual GHOST_TSuccess swapBuffers(); + + /** + * Activates the drawing context of this window. + * \return A boolean success indicator. + */ + virtual GHOST_TSuccess activateDrawingContext(); + + /** + * Updates the drawing context of this window. Needed + * whenever the window is changed. + * \return Indication of success. + */ + GHOST_TSuccess updateDrawingContext(); /** * Returns the window user data. * \return The window user data. */ - inline virtual GHOST_TUserDataPtr getUserData() const + inline GHOST_TUserDataPtr getUserData() const { return m_userData; } /** * Changes the window user data. - * \param data The window user data. + * \param userData: The window user data. */ - virtual void setUserData(const GHOST_TUserDataPtr userData) + void setUserData(const GHOST_TUserDataPtr userData) { m_userData = userData; } - virtual float getNativePixelSize(void) + float getNativePixelSize(void) { if (m_nativePixelSize > 0.0f) return m_nativePixelSize; return 1.0f; } +#ifdef WITH_INPUT_IME + virtual void beginIME(GHOST_TInt32 x, + GHOST_TInt32 y, + GHOST_TInt32 w, + GHOST_TInt32 h, + int completed) + { + /* do nothing temporarily if not in windows */ + } + + virtual void endIME() + { + /* do nothing temporarily if not in windows */ + } +#endif /* WITH_INPUT_IME */ + protected: /** * Tries to install a rendering context in this window. * \param type The type of rendering context installed. * \return Indication as to whether installation has succeeded. */ - virtual GHOST_TSuccess installDrawingContext(GHOST_TDrawingContextType type) = 0; - - /** - * Removes the current drawing context. - * \return Indication as to whether removal has succeeded. - */ - virtual GHOST_TSuccess removeDrawingContext() = 0; + virtual GHOST_Context *newDrawingContext(GHOST_TDrawingContextType type) = 0; /** * Sets the cursor visibility on the window using @@ -313,7 +329,7 @@ protected: * Sets the cursor grab on the window using * native window system calls. */ - virtual GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode) { + virtual GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode /*mode*/) { return GHOST_kSuccess; } @@ -333,7 +349,10 @@ protected: virtual GHOST_TSuccess setWindowCustomCursorShape(GHOST_TUns8 *bitmap, GHOST_TUns8 *mask, int szx, int szy, int hotX, int hotY, int fg, int bg) = 0; - /** The the of drawing context installed in this window. */ + + GHOST_TSuccess releaseNativeHandles(); + + /** The drawing context installed in this window. */ GHOST_TDrawingContextType m_drawingContextType; /** The window user data */ @@ -369,14 +388,11 @@ protected: /** Stores whether this is a full screen window. */ bool m_fullScreen; - /** Stereo visual created. Only necessary for 'real' stereo support, - * ie quad buffered stereo. This is not always possible, depends on - * the graphics h/w - */ - bool m_stereoVisual; - - /** Number of samples used in anti-aliasing, set to 0 if no AA **/ - GHOST_TUns16 m_numOfAASamples; + /** Whether to attempt to initialize a context with a stereo framebuffer. */ + bool m_wantStereoVisual; + + /** Attempt to initialize a context with this many samples. */ + GHOST_TUns16 m_wantNumOfAASamples; /** Full-screen width */ GHOST_TUns32 m_fullScreenWidth; @@ -385,6 +401,9 @@ protected: /* OSX only, retina screens */ float m_nativePixelSize; + +private: + GHOST_Context *m_context; }; diff --git a/intern/ghost/intern/GHOST_WindowCocoa.h b/intern/ghost/intern/GHOST_WindowCocoa.h index b30f6a340ad..3ed88086184 100644 --- a/intern/ghost/intern/GHOST_WindowCocoa.h +++ b/intern/ghost/intern/GHOST_WindowCocoa.h @@ -20,7 +20,7 @@ * * The Original Code is: all of this file. * - * Contributor(s): none yet. + * Contributor(s): Maarten Gribnau, Jason Wilkins * * ***** END GPL LICENSE BLOCK ***** */ @@ -42,20 +42,11 @@ @class CocoaWindow; @class CocoaOpenGLView; +@class NSCursor; +@class NSScreen; class GHOST_SystemCocoa; -/** - * Window on Mac OSX/Cocoa. - * Carbon windows have a size widget in the lower right corner of the window. - * To force it to be visible, the height of the client rectangle is reduced so - * that applications do not draw in that area. GHOST will manage that area - * which is called the gutter. - * When OpenGL contexts are active, GHOST will use AGL_BUFFER_RECT to prevent - * OpenGL drawing outside the reduced client rectangle. - * \author Maarten Gribnau - * \date May 23, 2001 - */ class GHOST_WindowCocoa : public GHOST_Window { public: /** @@ -90,77 +81,77 @@ public: * Destructor. * Closes the window and disposes resources allocated. */ - virtual ~GHOST_WindowCocoa(); + ~GHOST_WindowCocoa(); /** * Returns indication as to whether the window is valid. * \return The validity of the window. */ - virtual bool getValid() const; + bool getValid() const; /** * Returns the associated NSWindow object * \return The associated NSWindow object */ - virtual void *getOSWindow() const; + void *getOSWindow() const; /** * Sets the title displayed in the title bar. * \param title The title to display in the title bar. */ - virtual void setTitle(const STR_String& title); + void setTitle(const STR_String& title); /** * Returns the title displayed in the title bar. * \param title The title displayed in the title bar. */ - virtual void getTitle(STR_String& title) const; + void getTitle(STR_String& title) const; /** * Returns the window rectangle dimensions. * The dimensions are given in screen coordinates that are relative to the upper-left corner of the screen. * \param bounds The bounding rectangle of the window. */ - virtual void getWindowBounds(GHOST_Rect& bounds) const; + void getWindowBounds(GHOST_Rect& bounds) const; /** * Returns the client rectangle dimensions. * The left and top members of the rectangle are always zero. * \param bounds The bounding rectangle of the client area of the window. */ - virtual void getClientBounds(GHOST_Rect& bounds) const; + void getClientBounds(GHOST_Rect& bounds) const; /** * Resizes client rectangle width. * \param width The new width of the client area of the window. */ - virtual GHOST_TSuccess setClientWidth(GHOST_TUns32 width); + GHOST_TSuccess setClientWidth(GHOST_TUns32 width); /** * Resizes client rectangle height. * \param height The new height of the client area of the window. */ - virtual GHOST_TSuccess setClientHeight(GHOST_TUns32 height); + GHOST_TSuccess setClientHeight(GHOST_TUns32 height); /** * Resizes client rectangle. * \param width The new width of the client area of the window. * \param height The new height of the client area of the window. */ - virtual GHOST_TSuccess setClientSize(GHOST_TUns32 width, GHOST_TUns32 height); + GHOST_TSuccess setClientSize(GHOST_TUns32 width, GHOST_TUns32 height); /** * Returns the state of the window (normal, minimized, maximized). * \return The state of the window. */ - virtual GHOST_TWindowState getState() const; + GHOST_TWindowState getState() const; /** * Sets the window "modified" status, indicating unsaved changes * \param isUnsavedChanges Unsaved changes or not * \return Indication of success. */ - virtual GHOST_TSuccess setModifiedState(bool isUnsavedChanges); + GHOST_TSuccess setModifiedState(bool isUnsavedChanges); /** * Converts a point in screen coordinates to client rectangle coordinates @@ -169,7 +160,7 @@ public: * \param outX The x-coordinate in the client rectangle. * \param outY The y-coordinate in the client rectangle. */ - virtual void screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const; + void screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const; /** * Converts a point in screen coordinates to client rectangle coordinates @@ -178,7 +169,7 @@ public: * \param outX The x-coordinate on the screen. * \param outY The y-coordinate on the screen. */ - virtual void clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const; + void clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const; /** * Converts a point in screen coordinates to client rectangle coordinates @@ -211,37 +202,17 @@ public: * \param state The state of the window. * \return Indication of success. */ - virtual GHOST_TSuccess setState(GHOST_TWindowState state); + GHOST_TSuccess setState(GHOST_TWindowState state); /** * Sets the order of the window (bottom, top). * \param order The order of the window. * \return Indication of success. */ - virtual GHOST_TSuccess setOrder(GHOST_TWindowOrder order); - - /** - * Swaps front and back buffers of a window. - * \return A boolean success indicator. - */ - virtual GHOST_TSuccess swapBuffers(); - - /** - * Updates the drawing context of this window. Needed - * whenever the window is changed. - * \return Indication of success. - */ - GHOST_TSuccess updateDrawingContext(); - - /** - * Activates the drawing context of this window. - * \return A boolean success indicator. - */ - virtual GHOST_TSuccess activateDrawingContext(); + GHOST_TSuccess setOrder(GHOST_TWindowOrder order); - virtual void loadCursor(bool visible, GHOST_TStandardCursor cursor) const; + void loadCursor(bool visible, GHOST_TStandardCursor cursor) const; - const GHOST_TabletData *GetTabletData() { return &m_tablet; @@ -256,15 +227,15 @@ public: * Sets the progress bar value displayed in the window/application icon * \param progress The progress % (0.0 to 1.0) */ - virtual GHOST_TSuccess setProgressBar(float progress); + GHOST_TSuccess setProgressBar(float progress); /** * Hides the progress bar icon */ - virtual GHOST_TSuccess endProgressBar(); + GHOST_TSuccess endProgressBar(); - virtual void setNativePixelSize(void); + void setNativePixelSize(void); GHOST_TSuccess beginFullScreen() const {return GHOST_kFailure;} @@ -278,51 +249,45 @@ public: bool getImmediateDraw(void) const { return m_immediateDraw; } protected: - /** - * Tries to install a rendering context in this window. - * \param type The type of rendering context installed. - * \return Indication as to whether installation has succeeded. - */ - virtual GHOST_TSuccess installDrawingContext(GHOST_TDrawingContextType type); /** - * Removes the current drawing context. - * \return Indication as to whether removal has succeeded. + * \param type The type of rendering context create. + * \return Indication of success. */ - virtual GHOST_TSuccess removeDrawingContext(); - + GHOST_Context *newDrawingContext(GHOST_TDrawingContextType type); + /** * Invalidates the contents of this window. * \return Indication of success. */ - virtual GHOST_TSuccess invalidate(); + GHOST_TSuccess invalidate(); /** * Sets the cursor visibility on the window using * native window system calls. */ - virtual GHOST_TSuccess setWindowCursorVisibility(bool visible); + GHOST_TSuccess setWindowCursorVisibility(bool visible); /** * Sets the cursor grab on the window using * native window system calls. */ - virtual GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode); + GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode); /** * Sets the cursor shape on the window using * native window system calls. */ - virtual GHOST_TSuccess setWindowCursorShape(GHOST_TStandardCursor shape); + GHOST_TSuccess setWindowCursorShape(GHOST_TStandardCursor shape); /** * Sets the cursor shape on the window using * native window system calls. */ - virtual GHOST_TSuccess setWindowCustomCursorShape(GHOST_TUns8 *bitmap, GHOST_TUns8 *mask, + GHOST_TSuccess setWindowCustomCursorShape(GHOST_TUns8 *bitmap, GHOST_TUns8 *mask, int sizex, int sizey, int hotX, int hotY, int fg_color, int bg_color); - virtual GHOST_TSuccess setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], GHOST_TUns8 mask[16][2], int hotX, int hotY); + GHOST_TSuccess setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], GHOST_TUns8 mask[16][2], int hotX, int hotY); /** The window containing the OpenGL view */ CocoaWindow *m_window; @@ -330,15 +295,9 @@ protected: /** The openGL view */ CocoaOpenGLView *m_openGLView; - /** The opgnGL drawing context */ - NSOpenGLContext *m_openGLContext; - /** The mother SystemCocoa class to send events */ GHOST_SystemCocoa *m_systemCocoa; - - /** The first created OpenGL context (for sharing display lists) */ - static NSOpenGLContext *s_firstOpenGLcontext; - + NSCursor *m_customCursor; GHOST_TabletData m_tablet; @@ -349,4 +308,3 @@ protected: }; #endif // __GHOST_WINDOWCOCOA_H__ - diff --git a/intern/ghost/intern/GHOST_WindowCocoa.mm b/intern/ghost/intern/GHOST_WindowCocoa.mm index f9b8899fbe5..9c7b5c3fee7 100644 --- a/intern/ghost/intern/GHOST_WindowCocoa.mm +++ b/intern/ghost/intern/GHOST_WindowCocoa.mm @@ -20,29 +20,31 @@ * * The Original Code is: all of this file. * - * Contributor(s): Maarten Gribnau 05/2001 - Damien Plisson 10/2009 + * Contributor(s): Maarten Gribnau 05/2001 + * Damien Plisson 10/2009 + * Jason Wilkins 02/2014 + * Jens Verwiebe 10/2014 * * ***** END GPL LICENSE BLOCK ***** */ -#include <Cocoa/Cocoa.h> +#include "GHOST_WindowCocoa.h" +#include "GHOST_SystemCocoa.h" +#include "GHOST_ContextNone.h" +#include "GHOST_Debug.h" -#if MAC_OS_X_VERSION_MIN_REQUIRED <= 1050 -//Use of the SetSystemUIMode function (64bit compatible) -#include <Carbon/Carbon.h> +#if defined(WITH_GL_EGL) +# include "GHOST_ContextEGL.h" +#else +# include "GHOST_ContextCGL.h" #endif -#include <OpenGL/gl.h> -#include <OpenGL/CGLRenderers.h> -/***** Multithreaded opengl code : uncomment for enabling -#include <OpenGL/OpenGL.h> -*/ +#include <Cocoa/Cocoa.h> - -#include "GHOST_WindowCocoa.h" -#include "GHOST_SystemCocoa.h" -#include "GHOST_Debug.h" +#if MAC_OS_X_VERSION_MIN_REQUIRED <= 1050 + //Use of the SetSystemUIMode function (64bit compatible) +# include <Carbon/Carbon.h> +#endif #include <sys/sysctl.h> @@ -74,7 +76,6 @@ enum { - (void)windowDidChangeBackingProperties:(NSNotification *)notification; @end - @implementation CocoaWindowDelegate : NSObject - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa { @@ -85,6 +86,8 @@ enum { - (void)windowDidBecomeKey:(NSNotification *)notification { systemCocoa->handleWindowEvent(GHOST_kEventWindowActivate, associatedWindow); + // work around for broken appswitching when combining cmd-tab and missioncontrol + [(NSWindow*)associatedWindow->getOSWindow() orderFrontRegardless]; } - (void)windowDidResignKey:(NSNotification *)notification @@ -168,6 +171,7 @@ enum { - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa; - (GHOST_SystemCocoa*)systemCocoa; @end + @implementation CocoaWindow - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa { @@ -258,8 +262,6 @@ enum { @end - - #pragma mark NSOpenGLView subclass //We need to subclass it in order to give Cocoa the feeling key events are trapped @interface CocoaOpenGLView : NSOpenGLView <NSTextInput> @@ -274,6 +276,7 @@ enum { } - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa; @end + @implementation CocoaOpenGLView - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa @@ -535,8 +538,6 @@ enum { @end #endif -NSOpenGLContext* GHOST_WindowCocoa::s_firstOpenGLcontext = nil; - GHOST_WindowCocoa::GHOST_WindowCocoa( GHOST_SystemCocoa *systemCocoa, const STR_String& title, @@ -548,13 +549,9 @@ GHOST_WindowCocoa::GHOST_WindowCocoa( GHOST_TDrawingContextType type, const bool stereoVisual, const GHOST_TUns16 numOfAASamples ) : - GHOST_Window(width, height, state, GHOST_kDrawingContextTypeNone, stereoVisual, false, numOfAASamples), + GHOST_Window(width, height, state, stereoVisual, false, numOfAASamples), m_customCursor(0) { - NSOpenGLPixelFormatAttribute pixelFormatAttrsWindow[40]; - NSOpenGLPixelFormat *pixelFormat = nil; - int i; - m_systemCocoa = systemCocoa; m_fullScreen = false; m_immediateDraw = false; @@ -572,8 +569,9 @@ GHOST_WindowCocoa::GHOST_WindowCocoa( rect.size.height = height; m_window = [[CocoaWindow alloc] initWithContentRect:rect - styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask - backing:NSBackingStoreBuffered defer:NO]; + styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask + backing:NSBackingStoreBuffered defer:NO]; + if (m_window == nil) { [pool drain]; return; @@ -586,104 +584,10 @@ GHOST_WindowCocoa::GHOST_WindowCocoa( minSize.height = 240; [m_window setContentMinSize:minSize]; - setTitle(title); - - - // Pixel Format Attributes for the windowed NSOpenGLContext - i=0; - pixelFormatAttrsWindow[i++] = NSOpenGLPFADoubleBuffer; - - // Guarantees the back buffer contents to be valid after a call to NSOpenGLContext object's flushBuffer - // needed for 'Draw Overlap' drawing method - pixelFormatAttrsWindow[i++] = NSOpenGLPFABackingStore; - - // Force software OpenGL, for debugging - if (getenv("BLENDER_SOFTWAREGL")) { - pixelFormatAttrsWindow[i++] = NSOpenGLPFARendererID; - pixelFormatAttrsWindow[i++] = kCGLRendererAppleSWID; - } - else - pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccelerated; - - //pixelFormatAttrsWindow[i++] = NSOpenGLPFAAllowOfflineRenderers,; // Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway - - pixelFormatAttrsWindow[i++] = NSOpenGLPFADepthSize; - pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32; - - pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccumSize; - pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32; - - if (stereoVisual) pixelFormatAttrsWindow[i++] = NSOpenGLPFAStereo; - - if (numOfAASamples > 0) { - // Multisample anti-aliasing - pixelFormatAttrsWindow[i++] = NSOpenGLPFAMultisample; - - pixelFormatAttrsWindow[i++] = NSOpenGLPFASampleBuffers; - pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 1; - - pixelFormatAttrsWindow[i++] = NSOpenGLPFASamples; - pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) numOfAASamples; - - pixelFormatAttrsWindow[i++] = NSOpenGLPFANoRecovery; - } - - pixelFormatAttrsWindow[i] = (NSOpenGLPixelFormatAttribute) 0; - - pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttrsWindow]; - - - //Fall back to no multisampling if Antialiasing init failed - if (pixelFormat == nil) { - i=0; - pixelFormatAttrsWindow[i++] = NSOpenGLPFADoubleBuffer; - - // Guarantees the back buffer contents to be valid after a call to NSOpenGLContext object's flushBuffer - // needed for 'Draw Overlap' drawing method - pixelFormatAttrsWindow[i++] = NSOpenGLPFABackingStore; - - // Force software OpenGL, for debugging - if (getenv("BLENDER_SOFTWAREGL")) { - pixelFormatAttrsWindow[i++] = NSOpenGLPFARendererID; - pixelFormatAttrsWindow[i++] = kCGLRendererAppleSWID; - } - else - pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccelerated; - - //pixelFormatAttrsWindow[i++] = NSOpenGLPFAAllowOfflineRenderers,; // Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway - - pixelFormatAttrsWindow[i++] = NSOpenGLPFADepthSize; - pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32; - - pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccumSize; - pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32; - - if (stereoVisual) pixelFormatAttrsWindow[i++] = NSOpenGLPFAStereo; - - pixelFormatAttrsWindow[i] = (NSOpenGLPixelFormatAttribute) 0; - - pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttrsWindow]; - - } - - if (numOfAASamples > 0) { //Set m_numOfAASamples to the actual value - GLint gli; - [pixelFormat getValues:&gli forAttribute:NSOpenGLPFASamples forVirtualScreen:0]; - if (m_numOfAASamples != (GHOST_TUns16)gli) { - m_numOfAASamples = (GHOST_TUns16)gli; - printf("GHOST_Window could be created with anti-aliasing of only %i samples\n",m_numOfAASamples); - } - } - //Creates the OpenGL View inside the window - m_openGLView = [[CocoaOpenGLView alloc] initWithFrame:rect - pixelFormat:pixelFormat]; - - [m_openGLView setSystemAndWindowCocoa:systemCocoa windowCocoa:this]; - - [pixelFormat release]; + m_openGLView = [[CocoaOpenGLView alloc] initWithFrame:rect]; - m_openGLContext = [m_openGLView openGLContext]; //This context will be replaced by the proper one just after + [m_openGLView setSystemAndWindowCocoa:systemCocoa windowCocoa:this]; [m_window setContentView:m_openGLView]; [m_window setInitialFirstResponder:m_openGLView]; @@ -694,6 +598,7 @@ GHOST_WindowCocoa::GHOST_WindowCocoa( updateDrawingContext(); activateDrawingContext(); + // XXX jwilkins: This seems like it belongs in GHOST_ContextCGL, but probably not GHOST_ContextEGL if (m_systemCocoa->m_nativePixel) { if ([m_openGLView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) { [m_openGLView setWantsBestResolutionOpenGLSurface:YES]; @@ -703,6 +608,8 @@ GHOST_WindowCocoa::GHOST_WindowCocoa( } } + setTitle(title); + m_tablet.Active = GHOST_kTabletModeNone; CocoaWindowDelegate *windowDelegate = [[CocoaWindowDelegate alloc] init]; @@ -717,7 +624,7 @@ GHOST_WindowCocoa::GHOST_WindowCocoa( #endif [m_window registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, - NSStringPboardType, NSTIFFPboardType, nil]]; + NSStringPboardType, NSTIFFPboardType, nil]]; #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 if (state != GHOST_kWindowStateFullScreen) { @@ -728,15 +635,18 @@ GHOST_WindowCocoa::GHOST_WindowCocoa( if (state == GHOST_kWindowStateFullScreen) setState(GHOST_kWindowStateFullScreen); - //Starting with 10.9 (darwin 13.x.x), we always use Lion fullscreen, since it - //now has proper multi-monitor support for fullscreen - char darwin_ver[10]; - size_t len = sizeof(darwin_ver); - sysctlbyname("kern.osrelease", &darwin_ver, &len, NULL, 0); - if(darwin_ver[0] == '1' && darwin_ver[1] >= '3') { + // Starting with 10.9 (darwin 13.x.x), we can use Lion fullscreen, + // since it now has better multi-monitor support + // if the screens are spawned, additional screens get useless, + // so we only use lionStyleFullScreen when screens have separate spaces + + if ([NSScreen respondsToSelector:@selector(screensHaveSeparateSpaces)] && [NSScreen screensHaveSeparateSpaces]) { + // implies we are on >= OSX 10.9 m_lionStyleFullScreen = true; } + [NSApp activateIgnoringOtherApps:YES]; // raise application to front, important for new blender instance animation play case + [pool drain]; } @@ -750,6 +660,8 @@ GHOST_WindowCocoa::~GHOST_WindowCocoa() m_customCursor = nil; } + releaseNativeHandles(); + [m_openGLView release]; if (m_window) { @@ -774,7 +686,7 @@ GHOST_WindowCocoa::~GHOST_WindowCocoa() bool GHOST_WindowCocoa::getValid() const { - return (m_window != 0); + return GHOST_Window::getValid() && m_window != 0 && m_openGLView != 0; } void* GHOST_WindowCocoa::getOSWindow() const @@ -877,7 +789,7 @@ void GHOST_WindowCocoa::getClientBounds(GHOST_Rect& bounds) const //Max window contents as screen size (excluding title bar...) NSRect contentRect = [CocoaWindow contentRectForFrameRect:screenSize - styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)]; + styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)]; rect = [m_window contentRectForFrameRect:[m_window frame]]; @@ -962,7 +874,12 @@ GHOST_TWindowState GHOST_WindowCocoa::getState() const if (masks & NSFullScreenWindowMask) { // Lion style fullscreen - state = GHOST_kWindowStateFullScreen; + if (!m_immediateDraw) { + state = GHOST_kWindowStateFullScreen; + } + else { + state = GHOST_kWindowStateNormal; + } } else #endif @@ -976,7 +893,12 @@ GHOST_TWindowState GHOST_WindowCocoa::getState() const state = GHOST_kWindowStateMaximized; } else { - state = GHOST_kWindowStateNormal; + if (m_immediateDraw) { + state = GHOST_kWindowStateFullScreen; + } + else { + state = GHOST_kWindowStateNormal; + } } [pool drain]; return state; @@ -1119,10 +1041,10 @@ GHOST_TSuccess GHOST_WindowCocoa::setState(GHOST_TWindowState state) } //Create a fullscreen borderless window CocoaWindow *tmpWindow = [[CocoaWindow alloc] - initWithContentRect:[[m_window screen] frame] - styleMask:NSBorderlessWindowMask - backing:NSBackingStoreBuffered - defer:YES]; + initWithContentRect:[[m_window screen] frame] + styleMask:NSBorderlessWindowMask + backing:NSBackingStoreBuffered + defer:YES]; //Copy current window parameters [tmpWindow setTitle:[m_window title]]; [tmpWindow setRepresentedFilename:[m_window representedFilename]]; @@ -1189,10 +1111,10 @@ GHOST_TSuccess GHOST_WindowCocoa::setState(GHOST_TWindowState state) } //Create a fullscreen borderless window CocoaWindow *tmpWindow = [[CocoaWindow alloc] - initWithContentRect:[[m_window screen] frame] - styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask) - backing:NSBackingStoreBuffered - defer:YES]; + initWithContentRect:[[m_window screen] frame] + styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask) + backing:NSBackingStoreBuffered + defer:YES]; //Copy current window parameters [tmpWindow setTitle:[m_window title]]; [tmpWindow setRepresentedFilename:[m_window representedFilename]]; @@ -1200,7 +1122,7 @@ GHOST_TSuccess GHOST_WindowCocoa::setState(GHOST_TWindowState state) [tmpWindow setDelegate:[m_window delegate]]; [tmpWindow setSystemAndWindowCocoa:[m_window systemCocoa] windowCocoa:this]; [tmpWindow registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, - NSStringPboardType, NSTIFFPboardType, nil]]; + NSStringPboardType, NSTIFFPboardType, nil]]; //Forbid to resize the window below the blender defined minimum one [tmpWindow setContentMinSize:NSMakeSize(320, 240)]; @@ -1266,131 +1188,95 @@ GHOST_TSuccess GHOST_WindowCocoa::setOrder(GHOST_TWindowOrder order) #pragma mark Drawing context -/*#define WAIT_FOR_VSYNC 1*/ - -GHOST_TSuccess GHOST_WindowCocoa::swapBuffers() -{ - if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) { - if (m_openGLContext != nil) { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - [m_openGLContext flushBuffer]; - [pool drain]; - return GHOST_kSuccess; - } - } - return GHOST_kFailure; -} - -GHOST_TSuccess GHOST_WindowCocoa::updateDrawingContext() -{ - if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) { - if (m_openGLContext != nil) { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - [m_openGLContext update]; - [pool drain]; - return GHOST_kSuccess; - } - } - return GHOST_kFailure; -} +GHOST_Context *GHOST_WindowCocoa::newDrawingContext(GHOST_TDrawingContextType type) +{ + if (type == GHOST_kDrawingContextTypeOpenGL) { +#if !defined(WITH_GL_EGL) + +#if defined(WITH_GL_PROFILE_CORE) + GHOST_Context *context = new GHOST_ContextCGL( + m_wantStereoVisual, + m_wantNumOfAASamples, + m_window, + m_openGLView, + GL_CONTEXT_CORE_PROFILE_BIT, + 3, 2, + GHOST_OPENGL_CGL_CONTEXT_FLAGS, + GHOST_OPENGL_CGL_RESET_NOTIFICATION_STRATEGY); +#elif defined(WITH_GL_PROFILE_ES20) + GHOST_Context *context = new GHOST_ContextCGL( + m_wantStereoVisual, + m_wantNumOfAASamples, + m_window, + m_openGLView, + CGL_CONTEXT_ES2_PROFILE_BIT_EXT, + 2, 0, + GHOST_OPENGL_CGL_CONTEXT_FLAGS, + GHOST_OPENGL_CGL_RESET_NOTIFICATION_STRATEGY); +#elif defined(WITH_GL_PROFILE_COMPAT) + GHOST_Context *context = new GHOST_ContextCGL( + m_wantStereoVisual, + m_wantNumOfAASamples, + m_window, + m_openGLView, + 0, // profile bit + 0, 0, + GHOST_OPENGL_CGL_CONTEXT_FLAGS, + GHOST_OPENGL_CGL_RESET_NOTIFICATION_STRATEGY); +#else +# error +#endif -GHOST_TSuccess GHOST_WindowCocoa::activateDrawingContext() -{ - if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) { - if (m_openGLContext != nil) { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - [m_openGLContext makeCurrentContext]; - - // Disable AA by default - if (m_numOfAASamples > 0) glDisable(GL_MULTISAMPLE_ARB); - [pool drain]; - return GHOST_kSuccess; - } - } - return GHOST_kFailure; -} +#else +#if defined(WITH_GL_PROFILE_CORE) + GHOST_Context *context = new GHOST_ContextEGL( + m_wantStereoVisual, + m_wantNumOfAASamples, + m_window, + m_openGLView, + 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_window, + m_openGLView, + 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_openGLView, + 0, // profile bit + 0, 0, + GHOST_OPENGL_EGL_CONTEXT_FLAGS, + GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY, + EGL_OPENGL_API); +#else +# error +#endif -GHOST_TSuccess GHOST_WindowCocoa::installDrawingContext(GHOST_TDrawingContextType type) -{ - GHOST_TSuccess success = GHOST_kFailure; - - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - NSOpenGLPixelFormat *pixelFormat; - NSOpenGLContext *tmpOpenGLContext; - - /***** Multithreaded opengl code : uncomment for enabling - CGLContextObj cglCtx; - */ - - switch (type) { - case GHOST_kDrawingContextTypeOpenGL: - if (!getValid()) break; - - pixelFormat = [m_openGLView pixelFormat]; - tmpOpenGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat - shareContext:s_firstOpenGLcontext]; - if (tmpOpenGLContext == nil) { - success = GHOST_kFailure; - break; - } - - //Switch openGL to multhreaded mode - /******* Multithreaded opengl code : uncomment for enabling - cglCtx = (CGLContextObj)[tmpOpenGLContext CGLContextObj]; - if (CGLEnable(cglCtx, kCGLCEMPEngine) == kCGLNoError) - printf("\nSwitched openGL to multithreaded mode\n"); - */ - - if (!s_firstOpenGLcontext) s_firstOpenGLcontext = tmpOpenGLContext; -#ifdef WAIT_FOR_VSYNC - { - GLint swapInt = 1; - /* wait for vsync, to avoid tearing artifacts */ - [tmpOpenGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; - } #endif - [m_openGLView setOpenGLContext:tmpOpenGLContext]; - [tmpOpenGLContext setView:m_openGLView]; - - m_openGLContext = tmpOpenGLContext; - break; - - case GHOST_kDrawingContextTypeNone: - success = GHOST_kSuccess; - break; - - default: - break; + if (context->initializeDrawingContext()) + return context; + else + delete context; } - [pool drain]; - return success; -} - -GHOST_TSuccess GHOST_WindowCocoa::removeDrawingContext() -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - switch (m_drawingContextType) { - case GHOST_kDrawingContextTypeOpenGL: - if (m_openGLContext) { - [m_openGLView clearGLContext]; - if (s_firstOpenGLcontext == m_openGLContext) s_firstOpenGLcontext = nil; - m_openGLContext = nil; - } - [pool drain]; - return GHOST_kSuccess; - case GHOST_kDrawingContextTypeNone: - [pool drain]; - return GHOST_kSuccess; - break; - default: - [pool drain]; - return GHOST_kFailure; - } + return NULL; } +#pragma mark invalidate GHOST_TSuccess GHOST_WindowCocoa::invalidate() { @@ -1442,6 +1328,17 @@ GHOST_TSuccess GHOST_WindowCocoa::setProgressBar(float progress) return GHOST_kSuccess; } +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 +static void postNotification() +{ + NSUserNotification *notification = [[NSUserNotification alloc] init]; + notification.title = @"Blender progress notification"; + notification.informativeText = @"Calculation is finished"; + notification.soundName = NSUserNotificationDefaultSoundName; + [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification]; + [notification release]; +} +#endif GHOST_TSuccess GHOST_WindowCocoa::endProgressBar() { @@ -1455,14 +1352,23 @@ GHOST_TSuccess GHOST_WindowCocoa::endProgressBar() [[NSImage imageNamed:@"NSApplicationIcon"] drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; [dockIcon unlockFocus]; [NSApp setApplicationIconImage:dockIcon]; + + + // With OSX 10.8 and later, we can use notifications to inform the user when the progress reached 100% + // Atm. just fire this when the progressbar ends, the behavior is controlled in the NotificationCenter + // If Blender is not frontmost window, a message pops up with sound, in any case an entry in notifications +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 + if ([NSUserNotificationCenter respondsToSelector:@selector(defaultUserNotificationCenter)]) { + postNotification(); + } +#endif + [dockIcon release]; [pool drain]; return GHOST_kSuccess; } - - #pragma mark Cursor handling void GHOST_WindowCocoa::loadCursor(bool visible, GHOST_TStandardCursor cursor) const @@ -1650,15 +1556,15 @@ GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 *bitmap cursorImageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil - pixelsWide:sizex - pixelsHigh:sizey - bitsPerSample:1 - samplesPerPixel:2 - hasAlpha:YES - isPlanar:YES - colorSpaceName:NSDeviceWhiteColorSpace + pixelsWide:sizex + pixelsHigh:sizey + bitsPerSample:1 + samplesPerPixel:2 + hasAlpha:YES + isPlanar:YES + colorSpaceName:NSDeviceWhiteColorSpace bytesPerRow:(sizex/8 + (sizex%8 >0 ?1:0)) - bitsPerPixel:1]; + bitsPerPixel:1]; cursorBitmap = (GHOST_TUns16*)[cursorImageRep bitmapData]; @@ -1686,7 +1592,7 @@ GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 *bitmap //foreground and background color parameter is not handled for now (10.6) m_customCursor = [[NSCursor alloc] initWithImage:cursorImage - hotSpot:hotSpotPoint]; + hotSpot:hotSpotPoint]; [cursorImageRep release]; [cursorImage release]; diff --git a/intern/ghost/intern/GHOST_WindowManager.cpp b/intern/ghost/intern/GHOST_WindowManager.cpp index 1f20e01ca73..83490cecce5 100644 --- a/intern/ghost/intern/GHOST_WindowManager.cpp +++ b/intern/ghost/intern/GHOST_WindowManager.cpp @@ -121,7 +121,7 @@ GHOST_IWindow *GHOST_WindowManager::getFullScreenWindow(void) const GHOST_TSuccess GHOST_WindowManager::beginFullScreen(GHOST_IWindow *window, - bool stereoVisual) + bool /*stereoVisual*/) { GHOST_TSuccess success = GHOST_kFailure; GHOST_ASSERT(window, "GHOST_WindowManager::beginFullScreen(): invalid window"); diff --git a/intern/ghost/intern/GHOST_WindowManager.h b/intern/ghost/intern/GHOST_WindowManager.h index 8297e4d24d2..e868a9b0416 100644 --- a/intern/ghost/intern/GHOST_WindowManager.h +++ b/intern/ghost/intern/GHOST_WindowManager.h @@ -55,7 +55,7 @@ public: /** * Destructor. */ - virtual ~GHOST_WindowManager(); + ~GHOST_WindowManager(); /** * Add a window to our list. @@ -63,67 +63,67 @@ public: * \param window Pointer to the window to be added. * \return Indication of success. */ - virtual GHOST_TSuccess addWindow(GHOST_IWindow *window); + GHOST_TSuccess addWindow(GHOST_IWindow *window); /** * Remove a window from our list. * \param window Pointer to the window to be removed. * \return Indication of success. */ - virtual GHOST_TSuccess removeWindow(const GHOST_IWindow *window); + GHOST_TSuccess removeWindow(const GHOST_IWindow *window); /** * Returns whether the window is in our list. * \param window Pointer to the window to query. * \return A boolean indicator. */ - virtual bool getWindowFound(const GHOST_IWindow *window) const; + bool getWindowFound(const GHOST_IWindow *window) const; /** * Returns whether one of the windows is fullscreen. * \return A boolean indicator. */ - virtual bool getFullScreen(void) const; + bool getFullScreen(void) const; /** * Returns pointer to the full-screen window. * \return The full-screen window (NULL if not in full-screen). */ - virtual GHOST_IWindow *getFullScreenWindow(void) const; + GHOST_IWindow *getFullScreenWindow(void) const; /** * Activates fullscreen mode for a window. * \param window The window displayed fullscreen. * \return Indication of success. */ - virtual GHOST_TSuccess beginFullScreen(GHOST_IWindow *window, const bool stereoVisual); + GHOST_TSuccess beginFullScreen(GHOST_IWindow *window, const bool stereoVisual); /** * Closes fullscreen mode down. * \return Indication of success. */ - virtual GHOST_TSuccess endFullScreen(void); + GHOST_TSuccess endFullScreen(void); /** * Sets new window as active window (the window receiving events). * There can be only one window active which should be in the current window list. * \param window The new active window. */ - virtual GHOST_TSuccess setActiveWindow(GHOST_IWindow *window); + GHOST_TSuccess setActiveWindow(GHOST_IWindow *window); /** * Returns the active window (the window receiving events). * There can be only one window active which should be in the current window list. * \return window The active window (or NULL if there is none). */ - virtual GHOST_IWindow *getActiveWindow(void) const; + GHOST_IWindow *getActiveWindow(void) const; /** * Set this window to be inactive (not receiving events). * \param window The window to deactivate. */ - virtual void setWindowInactive(const GHOST_IWindow *window); + void setWindowInactive(const GHOST_IWindow *window); /** @@ -140,7 +140,7 @@ public: * \param osWindow The OS window object/handle * \return The associated window, null if none corresponds */ - virtual GHOST_IWindow *getWindowAssociatedWithOSWindow(void *osWindow); + GHOST_IWindow *getWindowAssociatedWithOSWindow(void *osWindow); /** * Return true if any windows has a modified status diff --git a/intern/ghost/intern/GHOST_WindowNULL.h b/intern/ghost/intern/GHOST_WindowNULL.h index c848d773bd6..80a1274a2a9 100644 --- a/intern/ghost/intern/GHOST_WindowNULL.h +++ b/intern/ghost/intern/GHOST_WindowNULL.h @@ -53,7 +53,7 @@ public: const bool stereoVisual, const GHOST_TUns16 numOfAASamples ) : - GHOST_Window(width, height, state, type, stereoVisual, false, numOfAASamples), + GHOST_Window(width, height, state, stereoVisual, false, numOfAASamples), m_system(system) { setTitle(title); @@ -91,6 +91,12 @@ protected: private: GHOST_SystemNULL *m_system; + + /** + * \param type The type of rendering context create. + * \return Indication of success. + */ + GHOST_Context *newDrawingContext(GHOST_TDrawingContextType type) {return NULL;} }; diff --git a/intern/ghost/intern/GHOST_WindowSDL.cpp b/intern/ghost/intern/GHOST_WindowSDL.cpp index 4a1086144f4..1335c38d977 100644 --- a/intern/ghost/intern/GHOST_WindowSDL.cpp +++ b/intern/ghost/intern/GHOST_WindowSDL.cpp @@ -26,10 +26,11 @@ #include "GHOST_WindowSDL.h" #include "SDL_mouse.h" -#include <GL/glew.h> -#include <assert.h> +#include "glew-mx.h" + +#include "GHOST_ContextSDL.h" -static SDL_GLContext s_firstContext = NULL; +#include <assert.h> GHOST_WindowSDL::GHOST_WindowSDL(GHOST_SystemSDL *system, const STR_String& title, @@ -44,39 +45,29 @@ GHOST_WindowSDL::GHOST_WindowSDL(GHOST_SystemSDL *system, const bool exclusive, const GHOST_TUns16 numOfAASamples ) - : - GHOST_Window(width, height, state, type, stereoVisual, exclusive, numOfAASamples), - m_system(system), - m_invalid_window(false), - m_sdl_custom_cursor(NULL) + : GHOST_Window(width, height, state, stereoVisual, exclusive, numOfAASamples), + m_system(system), + m_valid_setup(false), + m_invalid_window(false), + m_sdl_custom_cursor(NULL) { - SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); - - if (numOfAASamples) { - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, numOfAASamples); - } /* creating the window _must_ come after setting attributes */ - m_sdl_win = SDL_CreateWindow(title, - left, - top, - width, - height, - SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN); - - - - m_sdl_glcontext = SDL_GL_CreateContext(m_sdl_win); + m_sdl_win = SDL_CreateWindow( + title, + left, top, + width, height, + SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN); + + /* now set up the rendering context. */ + if (setDrawingContextType(type) == GHOST_kSuccess) { + m_valid_setup = true; + GHOST_PRINT("Created window\n"); + } - //fprintf(stderr, "Ignoring Xlib error: error code %d request code %d\n", - // theEvent->error_code, theEvent->request_code); + if (exclusive) { + SDL_RaiseWindow(m_sdl_win); + } setTitle(title); } @@ -87,64 +78,38 @@ GHOST_WindowSDL::~GHOST_WindowSDL() SDL_FreeCursor(m_sdl_custom_cursor); } - if (m_sdl_glcontext != s_firstContext) { - SDL_GL_DeleteContext(m_sdl_glcontext); - } + releaseNativeHandles(); SDL_DestroyWindow(m_sdl_win); } -GHOST_TSuccess -GHOST_WindowSDL::installDrawingContext(GHOST_TDrawingContextType type) +GHOST_Context * +GHOST_WindowSDL::newDrawingContext(GHOST_TDrawingContextType type) { - // only support openGL for now. - GHOST_TSuccess success; - switch (type) { - case GHOST_kDrawingContextTypeOpenGL: - m_sdl_glcontext = SDL_GL_CreateContext(m_sdl_win); - - if (m_sdl_glcontext != NULL) { - if (!s_firstContext) { - s_firstContext = m_sdl_glcontext; - } - - success = (SDL_GL_MakeCurrent(m_sdl_win, m_sdl_glcontext) < 0) ? - GHOST_kFailure : GHOST_kSuccess; - } - else { - success = GHOST_kFailure; - } - - break; - - case GHOST_kDrawingContextTypeNone: - success = GHOST_kSuccess; - break; - - default: - success = GHOST_kFailure; + if (type == GHOST_kDrawingContextTypeOpenGL) { + GHOST_Context *context = new GHOST_ContextSDL( + m_wantStereoVisual, + m_wantNumOfAASamples, + m_sdl_win, + 0, // profile bit + 0, 0, + GHOST_OPENGL_SDL_CONTEXT_FLAGS, + GHOST_OPENGL_SDL_RESET_NOTIFICATION_STRATEGY); + + if (context->initializeDrawingContext()) + return context; + else + delete context; } - return success; + + return NULL; } GHOST_TSuccess GHOST_WindowSDL::invalidate(void) { - // So the idea of this function is to generate an expose event - // for the window. - // Unfortunately X does not handle expose events for you and - // it is the client's job to refresh the dirty part of the window. - // We need to queue up invalidate calls and generate GHOST events - // for them in the system. - - // We implement this by setting a boolean in this class to concatenate - // all such calls into a single event for this window. - - // At the same time we queue the dirty windows in the system class - // and generate events for them at the next processEvents call. - if (m_invalid_window == false) { m_system->addDirtyWindow(this); m_invalid_window = true; @@ -153,52 +118,6 @@ GHOST_WindowSDL::invalidate(void) return GHOST_kSuccess; } - -GHOST_TSuccess -GHOST_WindowSDL::swapBuffers() -{ - if (getDrawingContextType() == GHOST_kDrawingContextTypeOpenGL) { - SDL_GL_SwapWindow(m_sdl_win); - return GHOST_kSuccess; - } - else { - return GHOST_kFailure; - } -} - - -GHOST_TSuccess -GHOST_WindowSDL::activateDrawingContext() -{ - if (m_sdl_glcontext != NULL) { - int status = SDL_GL_MakeCurrent(m_sdl_win, m_sdl_glcontext); - (void)status; - /* Disable AA by default */ - if (m_numOfAASamples > 0) { - glDisable(GL_MULTISAMPLE_ARB); - } - return GHOST_kSuccess; - } - return GHOST_kFailure; -} - - -GHOST_TSuccess -GHOST_WindowSDL::removeDrawingContext() -{ - GHOST_TSuccess success; - - if (m_sdl_glcontext != NULL) { - SDL_GL_DeleteContext(m_sdl_glcontext); - success = GHOST_kSuccess; - } - else { - success = GHOST_kFailure; - } - return success; -} - - GHOST_TSuccess GHOST_WindowSDL::setState(GHOST_TWindowState state) { @@ -236,6 +155,12 @@ GHOST_WindowSDL::getState() const return GHOST_kWindowStateNormal; } +bool +GHOST_WindowSDL:: +getValid() const +{ + return GHOST_Window::getValid() && m_valid_setup; +} void GHOST_WindowSDL::setTitle(const STR_String& title) @@ -638,16 +563,3 @@ GHOST_WindowSDL::setWindowCursorVisibility(bool visible) SDL_ShowCursor(visible); return GHOST_kSuccess; } - -GHOST_TSuccess -GHOST_WindowSDL::setSwapInterval(int interval) -{ - SDL_GL_SetSwapInterval(interval); - return GHOST_kSuccess; -} - -int -GHOST_WindowSDL::getSwapInterval() -{ - return SDL_GL_GetSwapInterval(); -} diff --git a/intern/ghost/intern/GHOST_WindowSDL.h b/intern/ghost/intern/GHOST_WindowSDL.h index 0c11ce19b50..5f658e8ad01 100644 --- a/intern/ghost/intern/GHOST_WindowSDL.h +++ b/intern/ghost/intern/GHOST_WindowSDL.h @@ -30,6 +30,7 @@ #include "GHOST_Window.h" #include "GHOST_SystemSDL.h" + #include <map> extern "C" { @@ -47,10 +48,10 @@ class GHOST_WindowSDL : public GHOST_Window { private: GHOST_SystemSDL *m_system; + bool m_valid_setup; bool m_invalid_window; SDL_Window *m_sdl_win; - SDL_GLContext m_sdl_glcontext; SDL_Cursor *m_sdl_custom_cursor; public: @@ -93,17 +94,18 @@ public: m_invalid_window = false; } - bool getValid() const - { - return (m_sdl_win != NULL); - } + bool getValid() const; void getWindowBounds(GHOST_Rect& bounds) const; void getClientBounds(GHOST_Rect& bounds) const; protected: - GHOST_TSuccess installDrawingContext(GHOST_TDrawingContextType type); - GHOST_TSuccess removeDrawingContext(); + + /** + * \param type The type of rendering context create. + * \return Indication of success. + */ + GHOST_Context *newDrawingContext(GHOST_TDrawingContextType type); GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode); @@ -151,12 +153,6 @@ protected: GHOST_TInt32& outX, GHOST_TInt32& outY) const; GHOST_TSuccess - swapBuffers(); - - GHOST_TSuccess - activateDrawingContext(); - - GHOST_TSuccess setState(GHOST_TWindowState state); GHOST_TWindowState @@ -172,9 +168,6 @@ protected: GHOST_TSuccess beginFullScreen() const { return GHOST_kFailure; } GHOST_TSuccess endFullScreen() const { return GHOST_kFailure; } - - GHOST_TSuccess setSwapInterval(int interval); - int getSwapInterval(); }; diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index 8e7e824186c..54689a96b66 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -29,90 +29,40 @@ * \ingroup GHOST */ -#include <string.h> +#define _USE_MATH_DEFINES + #include "GHOST_WindowWin32.h" #include "GHOST_SystemWin32.h" #include "GHOST_DropTargetWin32.h" +#include "GHOST_ContextNone.h" #include "utfconv.h" #include "utf_winfunc.h" -// Need glew for some defines -#include <GL/glew.h> -#include <GL/wglew.h> +#if defined(WITH_GL_EGL) +# include "GHOST_ContextEGL.h" +#else +# include "GHOST_ContextWGL.h" +#endif + + #include <math.h> +#include <string.h> +#include <assert.h> -// MSVC6 still doesn't define M_PI -#ifndef M_PI -# define M_PI 3.1415926536 -#endif -// Some more multisample defines -#define WGL_SAMPLE_BUFFERS_ARB 0x2041 -#define WGL_SAMPLES_ARB 0x2042 const wchar_t *GHOST_WindowWin32::s_windowClassName = L"GHOST_WindowClass"; const int GHOST_WindowWin32::s_maxTitleLength = 128; -HGLRC GHOST_WindowWin32::s_firsthGLRc = NULL; -HDC GHOST_WindowWin32::s_firstHDC = NULL; -static int WeightPixelFormat(PIXELFORMATDESCRIPTOR &pfd); -static int EnumPixelFormats(HDC hdc); -/* - * Color and depth bit values are not to be trusted. - * For instance, on TNT2: - * When the screen color depth is set to 16 bit, we get 5 color bits - * and 16 depth bits. - * When the screen color depth is set to 32 bit, we get 8 color bits - * and 24 depth bits. - * Just to be safe, we request high quality settings. - */ -static PIXELFORMATDESCRIPTOR sPreferredFormat = { - sizeof(PIXELFORMATDESCRIPTOR), /* size */ - 1, /* version */ - PFD_SUPPORT_OPENGL | - PFD_DRAW_TO_WINDOW | - PFD_SWAP_COPY | /* support swap copy */ - PFD_DOUBLEBUFFER, /* support double-buffering */ - PFD_TYPE_RGBA, /* color type */ - 32, /* prefered color depth */ - 0, 0, 0, 0, 0, 0, /* color bits (ignored) */ - 0, /* no alpha buffer */ - 0, /* alpha bits (ignored) */ - 0, /* no accumulation buffer */ - 0, 0, 0, 0, /* accum bits (ignored) */ - 32, /* depth buffer */ - 0, /* no stencil buffer */ - 0, /* no auxiliary buffers */ - PFD_MAIN_PLANE, /* main layer */ - 0, /* reserved */ - 0, 0, 0 /* no layer, visible, damage masks */ -}; - -/* Intel videocards don't work fine with multiple contexts and - * have to share the same context for all windows. - * But if we just share context for all windows it could work incorrect - * with multiple videocards configuration. Suppose, that Intel videocards - * can't be in multiple-devices configuration. */ -static int is_crappy_intel_card(void) -{ - static short is_crappy = -1; - - if (is_crappy == -1) { - const char *vendor = (const char *)glGetString(GL_VENDOR); - is_crappy = (strstr(vendor, "Intel") != NULL); - } - return is_crappy; -} /* force NVidia Optimus to used dedicated graphics */ extern "C" { __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; } -GHOST_WindowWin32::GHOST_WindowWin32( - GHOST_SystemWin32 *system, +GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, const STR_String &title, GHOST_TInt32 left, GHOST_TInt32 top, @@ -120,17 +70,15 @@ GHOST_WindowWin32::GHOST_WindowWin32( GHOST_TUns32 height, GHOST_TWindowState state, GHOST_TDrawingContextType type, - const bool stereoVisual, - const GHOST_TUns16 numOfAASamples, + bool wantStereoVisual, bool warnOld, + GHOST_TUns16 wantNumOfAASamples, GHOST_TEmbedderWindowID parentwindowhwnd, - GHOST_TSuccess msEnabled, - int msPixelFormat) - : GHOST_Window(width, height, state, GHOST_kDrawingContextTypeNone, - stereoVisual, false, numOfAASamples), + bool is_debug) + : GHOST_Window(width, height, state, + wantStereoVisual, false, wantNumOfAASamples), m_inLiveResize(false), m_system(system), m_hDC(0), - m_hGlRc(0), m_hasMouseCaptured(false), m_hasGrabMouse(false), m_nPressedButtons(0), @@ -139,19 +87,9 @@ GHOST_WindowWin32::GHOST_WindowWin32( m_tabletData(NULL), m_tablet(0), m_maxPressure(0), - m_multisample(numOfAASamples), - m_multisampleEnabled(msEnabled), - m_msPixelFormat(msPixelFormat), - //For recreation - m_title(title), - m_left(left), - m_top(top), - m_width(width), - m_height(height), m_normal_state(GHOST_kWindowStateNormal), - m_stereo(stereoVisual), - m_nextWindow(NULL), - m_parentWindowHwnd(parentwindowhwnd) + m_parentWindowHwnd(parentwindowhwnd), + m_debug_context(is_debug) { OSVERSIONINFOEX versionInfo; bool hasMinVersionForTaskbar = false; @@ -160,6 +98,13 @@ GHOST_WindowWin32::GHOST_WindowWin32( versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); +#if !defined(WITH_GL_EGL) + if (!warnOld) + GHOST_ContextWGL::unSetWarningOld(); +#else + (void)(warnOld); +#endif + if (!GetVersionEx((OSVERSIONINFO *)&versionInfo)) { versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (GetVersionEx((OSVERSIONINFO *)&versionInfo)) { @@ -286,35 +231,32 @@ GHOST_WindowWin32::GHOST_WindowWin32( // Store the device context m_hDC = ::GetDC(m_hWnd); - if (!s_firstHDC) { - s_firstHDC = m_hDC; - } - - // Show the window - int nCmdShow; - switch (state) { - case GHOST_kWindowStateMaximized: - nCmdShow = SW_SHOWMAXIMIZED; - break; - case GHOST_kWindowStateMinimized: - nCmdShow = SW_SHOWMINIMIZED; - break; - case GHOST_kWindowStateNormal: - default: - nCmdShow = SW_SHOWNORMAL; - break; - } - GHOST_TSuccess success; - success = setDrawingContextType(type); + GHOST_TSuccess success = setDrawingContextType(type); if (success) { + // Show the window + int nCmdShow; + switch (state) { + case GHOST_kWindowStateMaximized: + nCmdShow = SW_SHOWMAXIMIZED; + break; + case GHOST_kWindowStateMinimized: + nCmdShow = SW_SHOWMINIMIZED; + break; + case GHOST_kWindowStateNormal: + default: + nCmdShow = SW_SHOWNORMAL; + break; + } + ::ShowWindow(m_hWnd, nCmdShow); // Force an initial paint of the window ::UpdateWindow(m_hWnd); } else { //invalidate the window - m_hWnd = 0; + ::DestroyWindow(m_hWnd); + m_hWnd = NULL; } } @@ -415,14 +357,8 @@ GHOST_WindowWin32::~GHOST_WindowWin32() m_customCursor = NULL; } - ::wglMakeCurrent(NULL, NULL); - m_multisampleEnabled = GHOST_kFailure; - m_multisample = 0; - setDrawingContextType(GHOST_kDrawingContextTypeNone); - - if (m_hDC && m_hDC != s_firstHDC) { + if (m_hWnd != NULL && m_hDC != NULL && releaseNativeHandles()) { ::ReleaseDC(m_hWnd, m_hDC); - m_hDC = 0; } if (m_hWnd) { @@ -438,14 +374,9 @@ GHOST_WindowWin32::~GHOST_WindowWin32() } } -GHOST_Window *GHOST_WindowWin32::getNextWindow() -{ - return m_nextWindow; -} - bool GHOST_WindowWin32::getValid() const { - return m_hWnd != 0; + return GHOST_Window::getValid() && m_hWnd != 0 && m_hDC != 0; } HWND GHOST_WindowWin32::getHWND() const @@ -678,49 +609,6 @@ GHOST_TSuccess GHOST_WindowWin32::setOrder(GHOST_TWindowOrder order) } -GHOST_TSuccess GHOST_WindowWin32::swapBuffers() -{ - HDC hDC = m_hDC; - - if (is_crappy_intel_card()) - hDC = ::wglGetCurrentDC(); - - return ::SwapBuffers(hDC) == TRUE ? GHOST_kSuccess : GHOST_kFailure; -} - -GHOST_TSuccess GHOST_WindowWin32::setSwapInterval(int interval) -{ - if (!WGL_EXT_swap_control) - return GHOST_kFailure; - return wglSwapIntervalEXT(interval) == TRUE ? GHOST_kSuccess : GHOST_kFailure; -} - -int GHOST_WindowWin32::getSwapInterval() -{ - if (WGL_EXT_swap_control) - return wglGetSwapIntervalEXT(); - - return 0; -} - -GHOST_TSuccess GHOST_WindowWin32::activateDrawingContext() -{ - GHOST_TSuccess success; - if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) { - if (m_hDC && m_hGlRc) { - success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure; - } - else { - success = GHOST_kFailure; - } - } - else { - success = GHOST_kSuccess; - } - return success; -} - - GHOST_TSuccess GHOST_WindowWin32::invalidate() { GHOST_TSuccess success; @@ -733,257 +621,93 @@ GHOST_TSuccess GHOST_WindowWin32::invalidate() return success; } -GHOST_TSuccess GHOST_WindowWin32::initMultisample(PIXELFORMATDESCRIPTOR pfd) -{ - int pixelFormat; - bool success = FALSE; - UINT numFormats; - HDC hDC = GetDC(getHWND()); - float fAttributes[] = {0, 0}; - UINT nMaxFormats = 1; - - // The attributes to look for - int iAttributes[] = { - WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, - WGL_SUPPORT_OPENGL_ARB, GL_TRUE, - WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, - WGL_COLOR_BITS_ARB, pfd.cColorBits, - WGL_DEPTH_BITS_ARB, pfd.cDepthBits, -#ifdef GHOST_OPENGL_ALPHA - WGL_ALPHA_BITS_ARB, pfd.cAlphaBits, -#endif - WGL_STENCIL_BITS_ARB, pfd.cStencilBits, - WGL_DOUBLE_BUFFER_ARB, GL_TRUE, - WGL_SAMPLE_BUFFERS_ARB, GL_TRUE, - WGL_SAMPLES_ARB, m_multisample, - 0, 0 - }; - - // Get the function - PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB"); - - if (!wglChoosePixelFormatARB) { - m_multisampleEnabled = GHOST_kFailure; - return GHOST_kFailure; - } - - // iAttributes[17] is the initial multisample. If not valid try to use the closest valid value under it. - while (iAttributes[17] > 0) { - // See if the format is valid - success = wglChoosePixelFormatARB(hDC, iAttributes, fAttributes, nMaxFormats, &pixelFormat, &numFormats); - GHOST_PRINTF("WGL_SAMPLES_ARB = %i --> success = %i, %i formats\n", iAttributes[17], success, numFormats); - - if (success && numFormats >= 1 && m_multisampleEnabled == GHOST_kFailure) { - GHOST_PRINTF("valid pixel format with %i multisamples\n", iAttributes[17]); - m_multisampleEnabled = GHOST_kSuccess; - m_msPixelFormat = pixelFormat; - } - iAttributes[17] -= 1; - success = GHOST_kFailure; - } - if (m_multisampleEnabled == GHOST_kSuccess) { - return GHOST_kSuccess; - } - GHOST_PRINT("no available pixel format\n"); - return GHOST_kFailure; -} -GHOST_TSuccess GHOST_WindowWin32::installDrawingContext(GHOST_TDrawingContextType type) +GHOST_Context *GHOST_WindowWin32::newDrawingContext(GHOST_TDrawingContextType type) { - GHOST_TSuccess success; - switch (type) { - case GHOST_kDrawingContextTypeOpenGL: - { - // If this window has multisample enabled, use the supplied format - if (m_multisampleEnabled) - { - if (SetPixelFormat(m_hDC, m_msPixelFormat, &sPreferredFormat) == FALSE) - { - success = GHOST_kFailure; - break; - } - - // Create the context - m_hGlRc = ::wglCreateContext(m_hDC); - if (m_hGlRc) { - if (::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE) { - if (s_firsthGLRc) { - if (is_crappy_intel_card()) { - if (::wglMakeCurrent(NULL, NULL) == TRUE) { - ::wglDeleteContext(m_hGlRc); - m_hGlRc = s_firsthGLRc; - } - else { - ::wglDeleteContext(m_hGlRc); - m_hGlRc = NULL; - } - } - else { - ::wglCopyContext(s_firsthGLRc, m_hGlRc, GL_ALL_ATTRIB_BITS); - ::wglShareLists(s_firsthGLRc, m_hGlRc); - } - } - else { - s_firsthGLRc = m_hGlRc; - } - - if (m_hGlRc) { - success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure; - } - else { - success = GHOST_kFailure; - } - } - else { - success = GHOST_kFailure; - } - } - else { - success = GHOST_kFailure; - } - - if (success == GHOST_kFailure) { - printf("Failed to get a context....\n"); - } - } - else { - if (m_stereoVisual) - sPreferredFormat.dwFlags |= PFD_STEREO; - - // Attempt to match device context pixel format to the preferred format - int iPixelFormat = EnumPixelFormats(m_hDC); - if (iPixelFormat == 0) { - success = GHOST_kFailure; - break; - } - if (::SetPixelFormat(m_hDC, iPixelFormat, &sPreferredFormat) == FALSE) { - success = GHOST_kFailure; - break; - } - // For debugging only: retrieve the pixel format chosen - PIXELFORMATDESCRIPTOR preferredFormat; - ::DescribePixelFormat(m_hDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &preferredFormat); - - // Create the context - m_hGlRc = ::wglCreateContext(m_hDC); - if (m_hGlRc) { - if (::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE) { - if (s_firsthGLRc) { - if (is_crappy_intel_card()) { - if (::wglMakeCurrent(NULL, NULL) == TRUE) { - ::wglDeleteContext(m_hGlRc); - m_hGlRc = s_firsthGLRc; - } - else { - ::wglDeleteContext(m_hGlRc); - m_hGlRc = NULL; - } - } - else { - ::wglShareLists(s_firsthGLRc, m_hGlRc); - } - } - else { - s_firsthGLRc = m_hGlRc; - } - - if (m_hGlRc) { - success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure; - } - else { - success = GHOST_kFailure; - } - } - else { - success = GHOST_kFailure; - } - } - else { - success = GHOST_kFailure; - } - - if (success == GHOST_kFailure) { - printf("Failed to get a context....\n"); - } - - // Attempt to enable multisample - if (m_multisample && WGL_ARB_multisample && !m_multisampleEnabled && !is_crappy_intel_card()) - { - success = initMultisample(preferredFormat); - - if (success) - { - - // Make sure we don't screw up the context - if (m_hGlRc == s_firsthGLRc) - s_firsthGLRc = NULL; - m_drawingContextType = GHOST_kDrawingContextTypeOpenGL; - removeDrawingContext(); - - // Create a new window - GHOST_TWindowState new_state = getState(); - - m_nextWindow = new GHOST_WindowWin32((GHOST_SystemWin32 *)GHOST_ISystem::getSystem(), - m_title, - m_left, - m_top, - m_width, - m_height, - new_state, - type, - m_stereo, - m_multisample, - m_parentWindowHwnd, - m_multisampleEnabled, - m_msPixelFormat); - - // Return failure so we can trash this window. - success = GHOST_kFailure; - break; - } - else { - m_multisampleEnabled = GHOST_kSuccess; - printf("Multisample failed to initialize\n"); - success = GHOST_kSuccess; - } - } - } + if (type == GHOST_kDrawingContextTypeOpenGL) { +#if !defined(WITH_GL_EGL) + +#if defined(WITH_GL_PROFILE_CORE) + GHOST_Context *context = new GHOST_ContextWGL( + m_wantStereoVisual, + m_wantNumOfAASamples, + m_hWnd, + m_hDC, + WGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + 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_wantNumOfAASamples, + m_hWnd, + m_hDC, + WGL_CONTEXT_ES2_PROFILE_BIT_EXT, + 2, 0, + GHOST_OPENGL_WGL_CONTEXT_FLAGS, + GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY); +#elif defined(WITH_GL_PROFILE_COMPAT) + GHOST_Context *context = new GHOST_ContextWGL( + m_wantStereoVisual, + m_wantNumOfAASamples, + m_hWnd, + m_hDC, + 0, // profile bit + 0, 0, + GHOST_OPENGL_WGL_CONTEXT_FLAGS, + GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY); +#else +# error +#endif - } - break; +#else - case GHOST_kDrawingContextTypeNone: - success = GHOST_kSuccess; - break; +#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, + 0, // profile bit + 0, 0, + GHOST_OPENGL_EGL_CONTEXT_FLAGS, + GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY, + EGL_OPENGL_API); +#else +# error +#endif - default: - success = GHOST_kFailure; +#endif + if (context->initializeDrawingContext()) + return context; + else + delete context; } - return success; -} -GHOST_TSuccess GHOST_WindowWin32::removeDrawingContext() -{ - GHOST_TSuccess success; - switch (m_drawingContextType) { - case GHOST_kDrawingContextTypeOpenGL: - // we shouldn't remove the drawing context if it's the first OpenGL context - // If we do, we get corrupted drawing. See #19997 - if (m_hGlRc && m_hGlRc != s_firsthGLRc) { - success = ::wglDeleteContext(m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure; - m_hGlRc = 0; - } - else { - success = GHOST_kFailure; - } - break; - case GHOST_kDrawingContextTypeNone: - success = GHOST_kSuccess; - break; - default: - success = GHOST_kFailure; - } - return success; + return NULL; } void GHOST_WindowWin32::lostMouseCapture() @@ -1334,93 +1058,16 @@ GHOST_TSuccess GHOST_WindowWin32::endProgressBar() return GHOST_kFailure; } -/* Ron Fosner's code for weighting pixel formats and forcing software. - * See http://www.opengl.org/resources/faq/technical/weight.cpp */ -static int WeightPixelFormat(PIXELFORMATDESCRIPTOR &pfd) +#ifdef WITH_INPUT_IME +void GHOST_WindowWin32::beginIME(GHOST_TInt32 x, GHOST_TInt32 y, GHOST_TInt32 w, GHOST_TInt32 h, int completed) { - int weight = 0; - - /* assume desktop color depth is 32 bits per pixel */ - - /* cull unusable pixel formats */ - /* if no formats can be found, can we determine why it was rejected? */ - if (!(pfd.dwFlags & PFD_SUPPORT_OPENGL) || - !(pfd.dwFlags & PFD_DRAW_TO_WINDOW) || - !(pfd.dwFlags & PFD_DOUBLEBUFFER) || /* Blender _needs_ this */ - (pfd.cDepthBits <= 8) || - !(pfd.iPixelType == PFD_TYPE_RGBA)) - { - return 0; - } - - weight = 1; /* it's usable */ - - /* the bigger the depth buffer the better */ - /* give no weight to a 16-bit depth buffer, because those are crap */ - weight += pfd.cDepthBits - 16; - - weight += pfd.cColorBits - 8; - -#ifdef GHOST_OPENGL_ALPHA - if (pfd.cAlphaBits > 0) - weight ++; -#endif - - /* want swap copy capability -- it matters a lot */ - if (pfd.dwFlags & PFD_SWAP_COPY) weight += 16; - - /* but if it's a generic (not accelerated) view, it's really bad */ - if (pfd.dwFlags & PFD_GENERIC_FORMAT) weight /= 10; - - return weight; + m_imeImput.BeginIME(m_hWnd, GHOST_Rect(x, y - h, x, y), (bool)completed); } -/* A modification of Ron Fosner's replacement for ChoosePixelFormat */ -/* returns 0 on error, else returns the pixel format number to be used */ -static int EnumPixelFormats(HDC hdc) + +void GHOST_WindowWin32::endIME() { - int iPixelFormat; - int i, n, w, weight = 0; - PIXELFORMATDESCRIPTOR pfd; - - /* we need a device context to do anything */ - if (!hdc) return 0; - - iPixelFormat = 1; /* careful! PFD numbers are 1 based, not zero based */ - - /* obtain detailed information about - * the device context's first pixel format */ - n = 1 + ::DescribePixelFormat(hdc, iPixelFormat, - sizeof(PIXELFORMATDESCRIPTOR), &pfd); - - /* choose a pixel format using the useless Windows function in case - * we come up empty handed */ - iPixelFormat = ::ChoosePixelFormat(hdc, &sPreferredFormat); - - if (!iPixelFormat) return 0; /* couldn't find one to use */ - - for (i = 1; i <= n; i++) { /* not the idiom, but it's right */ - ::DescribePixelFormat(hdc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd); - w = WeightPixelFormat(pfd); - // be strict on stereo - if (!((sPreferredFormat.dwFlags ^ pfd.dwFlags) & PFD_STEREO)) { - if (w > weight) { - weight = w; - iPixelFormat = i; - } - } - } - if (weight == 0) { - // we could find the correct stereo setting, just find any suitable format - for (i = 1; i <= n; i++) { /* not the idiom, but it's right */ - ::DescribePixelFormat(hdc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd); - w = WeightPixelFormat(pfd); - if (w > weight) { - weight = w; - iPixelFormat = i; - } - } - } - return iPixelFormat; + m_imeImput.EndIME(m_hWnd); } +#endif /* WITH_INPUT_IME */ diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h index cb6c09b335c..3666fa753f3 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.h +++ b/intern/ghost/intern/GHOST_WindowWin32.h @@ -39,13 +39,9 @@ #include "GHOST_Window.h" #include "GHOST_TaskbarWin32.h" - -#ifndef __MINGW64__ -#define _WIN32_WINNT 0x501 // require Windows XP or newer +#ifdef WITH_INPUT_IME +# include "GHOST_ImeWin32.h" #endif -#define WIN32_LEAN_AND_MEAN -#include <windows.h> - #include <wintab.h> #define PACKETDATA (PK_BUTTONS | PK_NORMAL_PRESSURE | PK_ORIENTATION | PK_CURSOR) @@ -80,11 +76,11 @@ public: * \param height The height the window. * \param state The state the window is initially opened with. * \param type The type of drawing context installed in this window. - * \param stereoVisual Stereo visual for quad buffered stereo. - * \param numOfAASamples Number of samples used for AA (zero if no AA) + * \param wantStereoVisual Stereo visual for quad buffered stereo. + * \param wantNumOfAASamples Number of samples used for AA (zero if no AA) + * \param parentWindowHwnd */ - GHOST_WindowWin32( - GHOST_SystemWin32 *system, + GHOST_WindowWin32(GHOST_SystemWin32 *system, const STR_String& title, GHOST_TInt32 left, GHOST_TInt32 top, @@ -92,88 +88,80 @@ public: GHOST_TUns32 height, GHOST_TWindowState state, GHOST_TDrawingContextType type = GHOST_kDrawingContextTypeNone, - const bool stereoVisual = false, - const GHOST_TUns16 numOfAASamples = 0, + bool wantStereoVisual = false, + bool warnOld = false, + GHOST_TUns16 wantNumOfAASamples = 0, GHOST_TEmbedderWindowID parentWindowHwnd = 0, - GHOST_TSuccess msEnabled = GHOST_kFailure, - int msPixelFormat = 0 - ); + bool is_debug = false); /** * Destructor. * Closes the window and disposes resources allocated. */ - virtual ~GHOST_WindowWin32(); - - /** - * Returns the window to replace this one if it's getting replaced - * \return The window replacing this one. - */ - - GHOST_Window *getNextWindow(); + ~GHOST_WindowWin32(); /** * Returns indication as to whether the window is valid. * \return The validity of the window. */ - virtual bool getValid() const; + bool getValid() const; /** * Access to the handle of the window. * \return The handle of the window. */ - virtual HWND getHWND() const; + HWND getHWND() const; /** * Sets the title displayed in the title bar. * \param title The title to display in the title bar. */ - virtual void setTitle(const STR_String& title); + void setTitle(const STR_String& title); /** * Returns the title displayed in the title bar. * \param title The title displayed in the title bar. */ - virtual void getTitle(STR_String& title) const; + void getTitle(STR_String& title) const; /** * Returns the window rectangle dimensions. * The dimensions are given in screen coordinates that are relative to the upper-left corner of the screen. * \param bounds The bounding rectangle of the window. */ - virtual void getWindowBounds(GHOST_Rect& bounds) const; + void getWindowBounds(GHOST_Rect& bounds) const; /** * Returns the client rectangle dimensions. * The left and top members of the rectangle are always zero. * \param bounds The bounding rectangle of the client area of the window. */ - virtual void getClientBounds(GHOST_Rect& bounds) const; + void getClientBounds(GHOST_Rect& bounds) const; /** * Resizes client rectangle width. * \param width The new width of the client area of the window. */ - virtual GHOST_TSuccess setClientWidth(GHOST_TUns32 width); + GHOST_TSuccess setClientWidth(GHOST_TUns32 width); /** * Resizes client rectangle height. * \param height The new height of the client area of the window. */ - virtual GHOST_TSuccess setClientHeight(GHOST_TUns32 height); + GHOST_TSuccess setClientHeight(GHOST_TUns32 height); /** * Resizes client rectangle. * \param width The new width of the client area of the window. * \param height The new height of the client area of the window. */ - virtual GHOST_TSuccess setClientSize(GHOST_TUns32 width, GHOST_TUns32 height); + GHOST_TSuccess setClientSize(GHOST_TUns32 width, GHOST_TUns32 height); /** * Returns the state of the window (normal, minimized, maximized). * \return The state of the window. */ - virtual GHOST_TWindowState getState() const; + GHOST_TWindowState getState() const; /** * Converts a point in screen coordinates to client rectangle coordinates @@ -182,7 +170,7 @@ public: * \param outX The x-coordinate in the client rectangle. * \param outY The y-coordinate in the client rectangle. */ - virtual void screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const; + void screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const; /** * Converts a point in screen coordinates to client rectangle coordinates @@ -191,70 +179,38 @@ public: * \param outX The x-coordinate on the screen. * \param outY The y-coordinate on the screen. */ - virtual void clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const; + void clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const; /** * Sets the state of the window (normal, minimized, maximized). * \param state The state of the window. * \return Indication of success. */ - virtual GHOST_TSuccess setState(GHOST_TWindowState state); + GHOST_TSuccess setState(GHOST_TWindowState state); /** * Sets the order of the window (bottom, top). * \param order The order of the window. * \return Indication of success. */ - virtual GHOST_TSuccess setOrder(GHOST_TWindowOrder order); - - /** - * Swaps front and back buffers of a window. - * \return Indication of success. - */ - virtual GHOST_TSuccess swapBuffers(); - - /** - * Sets the swap interval for swapBuffers. - * \param interval The swap interval to use. - * \return A boolean success indicator. - */ - virtual GHOST_TSuccess setSwapInterval(int interval); - - /** - * Gets the current swap interval for swapBuffers. - * \return An integer. - */ - virtual int getSwapInterval(); - - /** - * Activates the drawing context of this window. - * \return Indication of success. - */ - virtual GHOST_TSuccess activateDrawingContext(); + GHOST_TSuccess setOrder(GHOST_TWindowOrder order); /** * Invalidates the contents of this window. */ - virtual GHOST_TSuccess invalidate(); + GHOST_TSuccess invalidate(); /** * Sets the progress bar value displayed in the window/application icon * \param progress The progress % */ - virtual GHOST_TSuccess setProgressBar(float progress); + GHOST_TSuccess setProgressBar(float progress); /** * Hides the progress bar in the icon */ - virtual GHOST_TSuccess endProgressBar(); + GHOST_TSuccess endProgressBar(); - /** - * Returns the name of the window class. - * \return The name of the window class. - */ - static const wchar_t *getWindowClassName() { - return s_windowClassName; - } /** * Register a mouse click event (should be called @@ -298,50 +254,53 @@ public: /** if the window currently resizing */ bool m_inLiveResize; -protected: - GHOST_TSuccess initMultisample(PIXELFORMATDESCRIPTOR pfd); +#ifdef WITH_INPUT_IME + GHOST_ImeWin32 *getImeInput() {return &m_imeImput;} - /** - * Tries to install a rendering context in this window. - * \param type The type of rendering context installed. - * \return Indication of success. - */ - virtual GHOST_TSuccess installDrawingContext(GHOST_TDrawingContextType type); + void beginIME( + GHOST_TInt32 x, GHOST_TInt32 y, + GHOST_TInt32 w, GHOST_TInt32 h, + int completed); + + void endIME(); +#endif /* WITH_INPUT_IME */ + +private: /** - * Removes the current drawing context. + * \param type The type of rendering context create. * \return Indication of success. */ - virtual GHOST_TSuccess removeDrawingContext(); + GHOST_Context *newDrawingContext(GHOST_TDrawingContextType type); /** * Sets the cursor visibility on the window using * native window system calls. */ - virtual GHOST_TSuccess setWindowCursorVisibility(bool visible); + GHOST_TSuccess setWindowCursorVisibility(bool visible); /** * Sets the cursor grab on the window using native window system calls. * Using registerMouseClickEvent. * \param mode GHOST_TGrabCursorMode. */ - virtual GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode); + GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode); /** * Sets the cursor shape on the window using * native window system calls. */ - virtual GHOST_TSuccess setWindowCursorShape(GHOST_TStandardCursor shape); + GHOST_TSuccess setWindowCursorShape(GHOST_TStandardCursor shape); /** * Sets the cursor shape on the window using * native window system calls. */ - virtual GHOST_TSuccess setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], + GHOST_TSuccess setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], GHOST_TUns8 mask[16][2], int hotX, int hotY); - virtual GHOST_TSuccess setWindowCustomCursorShape( + GHOST_TSuccess setWindowCustomCursorShape( GHOST_TUns8 *bitmap, GHOST_TUns8 *mask, int sizex, @@ -360,12 +319,7 @@ protected: HWND m_hWnd; /** Device context handle. */ HDC m_hDC; - /** OpenGL rendering context. */ - HGLRC m_hGlRc; - /** The first created OpenGL context (for sharing display lists) */ - static HGLRC s_firsthGLRc; - /** The first created device context handle. */ - static HDC s_firstHDC; + /** Flag for if window has captured the mouse */ bool m_hasMouseCaptured; /** Flag if an operator grabs the mouse with WM_cursor_grab_enable/ungrab() @@ -393,29 +347,16 @@ protected: LONG m_maxPressure; LONG m_maxAzimuth, m_maxAltitude; - /** Preferred number of samples */ - GHOST_TUns16 m_multisample; - - /** Check if multisample is supported */ - GHOST_TSuccess m_multisampleEnabled; - - /** The pixelFormat to use for multisample */ - int m_msPixelFormat; - - /** We need to following to recreate the window */ - const STR_String& m_title; - GHOST_TInt32 m_left; - GHOST_TInt32 m_top; - GHOST_TUns32 m_width; - GHOST_TUns32 m_height; GHOST_TWindowState m_normal_state; - bool m_stereo; - - /** The GHOST_System passes this to wm if this window is being replaced */ - GHOST_Window *m_nextWindow; /** Hwnd to parent window */ GHOST_TEmbedderWindowID m_parentWindowHwnd; + +#ifdef WITH_INPUT_IME + /** Handle input method editors event */ + GHOST_ImeWin32 m_imeImput; +#endif + bool m_debug_context; }; #endif // __GHOST_WINDOWWIN32_H__ diff --git a/intern/ghost/intern/GHOST_WindowX11.cpp b/intern/ghost/intern/GHOST_WindowX11.cpp index 86c940b7396..fe99a8bee1e 100644 --- a/intern/ghost/intern/GHOST_WindowX11.cpp +++ b/intern/ghost/intern/GHOST_WindowX11.cpp @@ -29,8 +29,10 @@ * \ingroup GHOST */ - -#include <GL/glxew.h> +/* For standard X11 cursors */ +#include <X11/cursorfont.h> +#include <X11/Xatom.h> +#include <X11/Xutil.h> #include "GHOST_WindowX11.h" #include "GHOST_SystemX11.h" @@ -38,20 +40,25 @@ #include "GHOST_Debug.h" #ifdef WITH_XDND -#include "GHOST_DropTargetX11.h" +# include "GHOST_DropTargetX11.h" #endif -/* For standard X11 cursors */ -#include <X11/cursorfont.h> -#include <X11/Xatom.h> +#if defined(WITH_GL_EGL) +# include "GHOST_ContextEGL.h" +#else +# include "GHOST_ContextGLX.h" +#endif #if defined(__sun__) || defined(__sun) || defined(__sparc) || defined(__sparc__) || defined(_AIX) -#include <strings.h> +# include <strings.h> #endif #include <cstring> #include <cstdio> +/* gethostname */ +#include <unistd.h> + #include <algorithm> #include <string> @@ -65,16 +72,11 @@ typedef struct { long input_mode; } MotifWmHints; -// Workaround for MESA bug #54080 -// https://bugs.freedesktop.org/show_bug.cgi?id=54080() -#define SWAP_INTERVALS_WORKAROUND - -#ifdef SWAP_INTERVALS_WORKAROUND -static bool g_swap_interval_disabled = false; -#endif // SWAP_INTERVALS_WORKAROUND - #define MWM_HINTS_DECORATIONS (1L << 1) +#ifndef HOST_NAME_MAX +# define HOST_NAME_MAX 64 +#endif // #define GHOST_X11_GRAB @@ -156,14 +158,79 @@ static long BLENDER_ICON_48x48x32[] = { 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, 4671303, }; +static XVisualInfo *x11_visualinfo_from_glx( + Display *display, + bool stereoVisual, GHOST_TUns16 *r_numOfAASamples) +{ + XVisualInfo *visualInfo = NULL; + GHOST_TUns16 numOfAASamples = *r_numOfAASamples; + GHOST_TUns16 actualSamples; + + /* Set up the minimum attributes that we require and see if + * X can find us a visual matching those requirements. */ + int glx_major, glx_minor; /* GLX version: major.minor */ + + if (!glXQueryVersion(display, &glx_major, &glx_minor)) { + fprintf(stderr, + "%s:%d: X11 glXQueryVersion() failed, " + "verify working openGL system!\n", + __FILE__, __LINE__); + + return NULL; + } + + /* GLX >= 1.4 required for multi-sample */ + if ((glx_major > 1) || (glx_major == 1 && glx_minor >= 4)) { + actualSamples = numOfAASamples; + } + else { + numOfAASamples = 0; + actualSamples = 0; + } + + /* Find the display with highest samples, starting at level requested */ + for (;;) { + int glx_attribs[64]; + + GHOST_X11_GL_GetAttributes(glx_attribs, 64, actualSamples, stereoVisual, false); + + visualInfo = glXChooseVisual(display, DefaultScreen(display), glx_attribs); + + /* Any sample level or even zero, which means oversampling disabled, is good + * but we need a valid visual to continue */ + if (visualInfo != NULL) { + if (actualSamples < numOfAASamples) { + fprintf(stderr, + "Warning! Unable to find a multisample pixel format that supports exactly %d samples. " + "Substituting one that uses %d samples.\n", + numOfAASamples, actualSamples); + } + break; + } + + if (actualSamples == 0) { + /* All options exhausted, cannot continue */ + fprintf(stderr, + "%s:%d: X11 glXChooseVisual() failed, " + "verify working openGL system!\n", + __FILE__, __LINE__); + + return NULL; + } + else { + --actualSamples; + } + } + + *r_numOfAASamples = actualSamples; -GLXContext GHOST_WindowX11::s_firstContext = NULL; + return visualInfo; +} GHOST_WindowX11:: -GHOST_WindowX11( - GHOST_SystemX11 *system, +GHOST_WindowX11(GHOST_SystemX11 *system, Display *display, - const STR_String& title, + const STR_String &title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -173,109 +240,51 @@ GHOST_WindowX11( GHOST_TDrawingContextType type, const bool stereoVisual, const bool exclusive, - const GHOST_TUns16 numOfAASamples) - : GHOST_Window(width, height, state, type, stereoVisual, exclusive, numOfAASamples), - m_context(NULL), + const GHOST_TUns16 numOfAASamples, const bool is_debug) + : GHOST_Window(width, height, state, stereoVisual, exclusive, numOfAASamples), m_display(display), + m_visualInfo(NULL), m_normal_state(GHOST_kWindowStateNormal), m_system(system), - m_valid_setup(false), m_invalid_window(false), m_empty_cursor(None), m_custom_cursor(None), - m_visible_cursor(None) + m_visible_cursor(None), +#ifdef WITH_XDND + m_dropTarget(NULL), +#endif +#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) + m_xic(NULL), +#endif + m_valid_setup(false), + m_is_debug_context(is_debug) { - - /* Set up the minimum attributes that we require and see if - * X can find us a visual matching those requirements. */ - - int attributes[40], i, samples; - int natom; - int glxVersionMajor, glxVersionMinor; /* As in GLX major.minor */ - - m_visual = NULL; - - if (!glXQueryVersion(m_display, &glxVersionMajor, &glxVersionMinor)) { - printf("%s:%d: X11 glXQueryVersion() failed, verify working openGL system!\n", __FILE__, __LINE__); - - /* exit if this is the first window */ - if (s_firstContext == NULL) { - printf("initial window could not find the GLX extension, exit!\n"); - exit(1); - } - - return; + if (type == GHOST_kDrawingContextTypeOpenGL) { + m_visualInfo = x11_visualinfo_from_glx(m_display, stereoVisual, &m_wantNumOfAASamples); } - - /* Find the display with highest samples, starting at level requested */ - for (samples = m_numOfAASamples; samples >= 0; samples--) { - i = 0; /* Reusing attributes array, so reset counter */ - - if (m_stereoVisual) - attributes[i++] = GLX_STEREO; - - attributes[i++] = GLX_RGBA; - attributes[i++] = GLX_DOUBLEBUFFER; - attributes[i++] = GLX_RED_SIZE; attributes[i++] = 1; - attributes[i++] = GLX_BLUE_SIZE; attributes[i++] = 1; - attributes[i++] = GLX_GREEN_SIZE; attributes[i++] = 1; - attributes[i++] = GLX_DEPTH_SIZE; attributes[i++] = 1; -#ifdef GHOST_OPENGL_ALPHA - attributes[i++] = GLX_ALPHA_SIZE; attributes[i++] = 1; -#endif - /* GLX >= 1.4 required for multi-sample */ - if (samples && (glxVersionMajor >= 1) && (glxVersionMinor >= 4)) { - attributes[i++] = GLX_SAMPLE_BUFFERS; attributes[i++] = 1; - attributes[i++] = GLX_SAMPLES; attributes[i++] = samples; - } - attributes[i] = None; - - m_visual = glXChooseVisual(m_display, DefaultScreen(m_display), attributes); - - /* Any sample level or even zero, which means oversampling disabled, is good - * but we need a valid visual to continue */ - if (m_visual == NULL) { - if (samples == 0) { - /* All options exhausted, cannot continue */ - printf("%s:%d: X11 glXChooseVisual() failed, verify working openGL system!\n", __FILE__, __LINE__); - - if (s_firstContext == NULL) { - printf("initial window could not find the GLX extension, exit!\n"); - exit(1); - } - - return; - } - } - else { - if (m_numOfAASamples && (m_numOfAASamples > samples)) { - printf("%s:%d: oversampling requested %i but using %i samples\n", - __FILE__, __LINE__, m_numOfAASamples, samples); - } - break; - } + else { + XVisualInfo tmp = {0}; + int n; + m_visualInfo = XGetVisualInfo(m_display, 0, &tmp, &n); } - /* Create a bunch of attributes needed to create an X window. */ - + /* caller needs to check 'getValid()' */ + if (m_visualInfo == NULL) { + fprintf(stderr, "initial window could not find the GLX extension\n"); + return; + } - /* First create a colormap for the window and visual. - * This seems pretty much a legacy feature as we are in rgba mode anyway. */ + unsigned int xattributes_valuemask = 0; XSetWindowAttributes xattributes; - unsigned int xattributes_valuemask = (CWBorderPixel | CWColormap | CWEventMask); memset(&xattributes, 0, sizeof(xattributes)); - xattributes.colormap = XCreateColormap(m_display, - RootWindow(m_display, m_visual->screen), - m_visual->visual, - AllocNone - ); - + xattributes_valuemask |= CWBorderPixel; xattributes.border_pixel = 0; /* Specify which events we are interested in hearing. */ + xattributes_valuemask |= CWEventMask; xattributes.event_mask = ExposureMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask | @@ -289,24 +298,28 @@ GHOST_WindowX11( xattributes.override_redirect = True; } + xattributes_valuemask |= CWColormap; + xattributes.colormap = XCreateColormap( + m_display, + RootWindow(m_display, m_visualInfo->screen), + m_visualInfo->visual, + AllocNone + ); + /* create the window! */ if (parentWindow == 0) { - m_window = XCreateWindow(m_display, - RootWindow(m_display, m_visual->screen), - left, - top, - width, - height, - 0, /* no border. */ - m_visual->depth, - InputOutput, - m_visual->visual, - xattributes_valuemask, - &xattributes - ); + m_window = XCreateWindow( + m_display, + RootWindow(m_display, m_visualInfo->screen), + left, top, width, height, + 0, /* no border. */ + m_visualInfo->depth, + InputOutput, + m_visualInfo->visual, + xattributes_valuemask, + &xattributes); } else { - Window root_return; int x_return, y_return; unsigned int w_return, h_return, border_w_return, depth_return; @@ -320,19 +333,16 @@ GHOST_WindowX11( height = h_return; - m_window = XCreateWindow(m_display, - parentWindow, /* reparent against embedder */ - left, - top, - width, - height, - 0, /* no border. */ - m_visual->depth, - InputOutput, - m_visual->visual, - xattributes_valuemask, - &xattributes - ); + m_window = XCreateWindow( + m_display, + parentWindow, /* reparent against embedder */ + left, top, width, height, + 0, /* no border. */ + m_visualInfo->depth, + InputOutput, + m_visualInfo->visual, + xattributes_valuemask, + &xattributes); XSelectInput(m_display, parentWindow, SubstructureNotifyMask); @@ -384,36 +394,43 @@ GHOST_WindowX11( m_post_state = GHOST_kWindowStateNormal; } + /* Create some hints for the window manager on how * we want this window treated. */ + { + XSizeHints *xsizehints = XAllocSizeHints(); + xsizehints->flags = PPosition | PSize | PMinSize | PMaxSize; + xsizehints->x = left; + xsizehints->y = top; + xsizehints->width = width; + xsizehints->height = height; + xsizehints->min_width = 320; /* size hints, could be made apart of the ghost api */ + xsizehints->min_height = 240; /* limits are also arbitrary, but should not allow 1x1 window */ + xsizehints->max_width = 65535; + xsizehints->max_height = 65535; + XSetWMNormalHints(m_display, m_window, xsizehints); + XFree(xsizehints); + } + + + /* XClassHint, title */ + { + XClassHint *xclasshint = XAllocClassHint(); + const int len = title.Length() + 1; + char *wmclass = (char *)malloc(sizeof(char) * len); + memcpy(wmclass, title.ReadPtr(), len * sizeof(char)); + xclasshint->res_name = wmclass; + xclasshint->res_class = wmclass; + XSetClassHint(m_display, m_window, xclasshint); + free(wmclass); + XFree(xclasshint); + } - XSizeHints *xsizehints = XAllocSizeHints(); - xsizehints->flags = PPosition | PSize | PMinSize | PMaxSize; - xsizehints->x = left; - xsizehints->y = top; - xsizehints->width = width; - xsizehints->height = height; - xsizehints->min_width = 320; /* size hints, could be made apart of the ghost api */ - xsizehints->min_height = 240; /* limits are also arbitrary, but should not allow 1x1 window */ - xsizehints->max_width = 65535; - xsizehints->max_height = 65535; - XSetWMNormalHints(m_display, m_window, xsizehints); - XFree(xsizehints); - - XClassHint *xclasshint = XAllocClassHint(); - const int len = title.Length() + 1; - char *wmclass = (char *)malloc(sizeof(char) * len); - strncpy(wmclass, (const char *)title, sizeof(char) * len); - xclasshint->res_name = wmclass; - xclasshint->res_class = wmclass; - XSetClassHint(m_display, m_window, xclasshint); - free(wmclass); - XFree(xclasshint); /* The basic for a good ICCCM "work" */ if (m_system->m_atom.WM_PROTOCOLS) { Atom atoms[2]; - natom = 0; + int natom = 0; if (m_system->m_atom.WM_DELETE_WINDOW) { atoms[natom] = m_system->m_atom.WM_DELETE_WINDOW; @@ -431,27 +448,48 @@ GHOST_WindowX11( } } -#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) - m_xic = NULL; -#endif - /* Set the window hints */ - XWMHints *xwmhints = XAllocWMHints(); - xwmhints->initial_state = NormalState; - xwmhints->input = True; - xwmhints->flags = InputHint | StateHint; - XSetWMHints(display, m_window, xwmhints); - XFree(xwmhints); - /* done setting the hints */ + { + XWMHints *xwmhints = XAllocWMHints(); + xwmhints->initial_state = NormalState; + xwmhints->input = True; + xwmhints->flags = InputHint | StateHint; + XSetWMHints(display, m_window, xwmhints); + XFree(xwmhints); + } + /* set the icon */ - Atom _NET_WM_ICON = XInternAtom(m_display, "_NET_WM_ICON", False); - XChangeProperty(m_display, m_window, _NET_WM_ICON, XA_CARDINAL, - 32, PropModeReplace, (unsigned char *)BLENDER_ICON_48x48x32, - BLENDER_ICON_48x48x32[0] * BLENDER_ICON_48x48x32[1] + 2); - /* done setting the icon */ + { + Atom _NET_WM_ICON = XInternAtom(m_display, "_NET_WM_ICON", False); + XChangeProperty(m_display, m_window, _NET_WM_ICON, XA_CARDINAL, + 32, PropModeReplace, (unsigned char *)BLENDER_ICON_48x48x32, + BLENDER_ICON_48x48x32[0] * BLENDER_ICON_48x48x32[1] + 2); + } - setTitle(title); + /* set the process ID (_NET_WM_PID) */ + { + Atom _NET_WM_PID = XInternAtom(m_display, "_NET_WM_PID", False); + pid_t pid = getpid(); + XChangeProperty(m_display, m_window, _NET_WM_PID, XA_CARDINAL, + 32, PropModeReplace, (unsigned char *)&pid, 1); + } + + + /* set the hostname (WM_CLIENT_MACHINE) */ + { + char hostname[HOST_NAME_MAX]; + char *text_array[1]; + XTextProperty text_prop; + + gethostname(hostname, sizeof(hostname)); + hostname[sizeof(hostname) - 1] = '\0'; + text_array[0] = hostname; + + XStringListToTextProperty(text_array, 1, &text_prop); + XSetWMClientMachine(m_display, m_window, &text_prop); + XFree(text_prop.value); + } #ifdef WITH_X11_XINPUT initXInputDevices(); @@ -459,12 +497,15 @@ GHOST_WindowX11( m_tabletData.Active = GHOST_kTabletModeNone; #endif + /* now set up the rendering context. */ - if (installDrawingContext(type) == GHOST_kSuccess) { + if (setDrawingContextType(type) == GHOST_kSuccess) { m_valid_setup = true; GHOST_PRINT("Created window\n"); } + setTitle(title); + if (exclusive) { XMapRaised(m_display, m_window); } @@ -477,7 +518,7 @@ GHOST_WindowX11( } #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) -static void destroyICCallback(XIC xic, XPointer ptr, XPointer data) +static void destroyICCallback(XIC /*xic*/, XPointer ptr, XPointer /*data*/) { GHOST_PRINT("XIM input context destroyed\n"); @@ -525,23 +566,32 @@ void GHOST_WindowX11::initXInputDevices() if (version && (version != (XExtensionVersion *)NoSuchExtension)) { if (version->present) { GHOST_SystemX11::GHOST_TabletX11 &xtablet = m_system->GetXTablet(); - XEventClass xevents[10], ev; + XEventClass xevents[8], ev; int dcount = 0; + /* With modern XInput (xlib 1.6.2 at least and/or evdev 2.9.0) and some 'no-name' tablets + * like 'UC-LOGIC Tablet WP5540U', we also need to 'select' ButtonPress for motion event, + * otherwise we do not get any tablet motion event once pen is pressed... See T43367. + */ + if (xtablet.StylusDevice) { DeviceMotionNotify(xtablet.StylusDevice, xtablet.MotionEvent, ev); if (ev) xevents[dcount++] = ev; + DeviceButtonPress(xtablet.StylusDevice, xtablet.PressEvent, ev); + if (ev) xevents[dcount++] = ev; ProximityIn(xtablet.StylusDevice, xtablet.ProxInEvent, ev); if (ev) xevents[dcount++] = ev; ProximityOut(xtablet.StylusDevice, xtablet.ProxOutEvent, ev); if (ev) xevents[dcount++] = ev; } if (xtablet.EraserDevice) { - DeviceMotionNotify(xtablet.EraserDevice, xtablet.MotionEvent, ev); + DeviceMotionNotify(xtablet.EraserDevice, xtablet.MotionEventEraser, ev); if (ev) xevents[dcount++] = ev; - ProximityIn(xtablet.EraserDevice, xtablet.ProxInEvent, ev); + DeviceButtonPress(xtablet.EraserDevice, xtablet.PressEventEraser, ev); if (ev) xevents[dcount++] = ev; - ProximityOut(xtablet.EraserDevice, xtablet.ProxOutEvent, ev); + ProximityIn(xtablet.EraserDevice, xtablet.ProxInEventEraser, ev); + if (ev) xevents[dcount++] = ev; + ProximityOut(xtablet.EraserDevice, xtablet.ProxOutEventEraser, ev); if (ev) xevents[dcount++] = ev; } @@ -564,7 +614,7 @@ bool GHOST_WindowX11:: getValid() const { - return m_valid_setup; + return GHOST_Window::getValid() && m_valid_setup; } void @@ -685,7 +735,7 @@ screenToClient( Window temp; XTranslateCoordinates(m_display, - RootWindow(m_display, m_visual->screen), + RootWindow(m_display, m_visualInfo->screen), m_window, inX, inY, &ax, &ay, @@ -708,7 +758,7 @@ clientToScreen( XTranslateCoordinates( m_display, m_window, - RootWindow(m_display, m_visual->screen), + RootWindow(m_display, m_visualInfo->screen), inX, inY, &ax, &ay, &temp); @@ -731,7 +781,7 @@ void GHOST_WindowX11::icccmSetState(int state) xev.xclient.format = 32; xev.xclient.message_type = m_system->m_atom.WM_CHANGE_STATE; xev.xclient.data.l[0] = state; - XSendEvent(m_display, RootWindow(m_display, DefaultScreen(m_display)), + XSendEvent(m_display, RootWindow(m_display, m_visualInfo->screen), False, SubstructureNotifyMask | SubstructureRedirectMask, &xev); } @@ -777,7 +827,7 @@ void GHOST_WindowX11::netwmMaximized(bool set) xev.xclient.data.l[2] = m_system->m_atom._NET_WM_STATE_MAXIMIZED_VERT; xev.xclient.data.l[3] = 0; xev.xclient.data.l[4] = 0; - XSendEvent(m_display, RootWindow(m_display, DefaultScreen(m_display)), + XSendEvent(m_display, RootWindow(m_display, m_visualInfo->screen), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); } @@ -833,7 +883,7 @@ void GHOST_WindowX11::netwmFullScreen(bool set) xev.xclient.data.l[2] = 0; xev.xclient.data.l[3] = 0; xev.xclient.data.l[4] = 0; - XSendEvent(m_display, RootWindow(m_display, DefaultScreen(m_display)), + XSendEvent(m_display, RootWindow(m_display, m_visualInfo->screen), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); } @@ -1052,7 +1102,7 @@ setOrder( xev.xclient.data.l[3] = 0; xev.xclient.data.l[4] = 0; - root = RootWindow(m_display, m_visual->screen), + root = RootWindow(m_display, m_visualInfo->screen), eventmask = SubstructureRedirectMask | SubstructureNotifyMask; XSendEvent(m_display, root, False, eventmask, &xev); @@ -1079,34 +1129,6 @@ setOrder( GHOST_TSuccess GHOST_WindowX11:: -swapBuffers() -{ - if (getDrawingContextType() == GHOST_kDrawingContextTypeOpenGL) { - glXSwapBuffers(m_display, m_window); - return GHOST_kSuccess; - } - else { - return GHOST_kFailure; - } -} - -GHOST_TSuccess -GHOST_WindowX11:: -activateDrawingContext() -{ - if (m_context != NULL) { - glXMakeCurrent(m_display, m_window, m_context); - /* Disable AA by default */ - if (m_numOfAASamples > 0) { - glDisable(GL_MULTISAMPLE_ARB); - } - return GHOST_kSuccess; - } - return GHOST_kFailure; -} - -GHOST_TSuccess -GHOST_WindowX11:: invalidate() { /* So the idea of this function is to generate an expose event @@ -1151,15 +1173,6 @@ validate() GHOST_WindowX11:: ~GHOST_WindowX11() { - static Atom Primary_atom, Clipboard_atom; - Window p_owner, c_owner; - /*Change the owner of the Atoms to None if we are the owner*/ - Primary_atom = XInternAtom(m_display, "PRIMARY", False); - Clipboard_atom = XInternAtom(m_display, "CLIPBOARD", False); - - p_owner = XGetSelectionOwner(m_display, Primary_atom); - c_owner = XGetSelectionOwner(m_display, Clipboard_atom); - std::map<unsigned int, Cursor>::iterator it = m_standard_cursors.begin(); for (; it != m_standard_cursors.end(); ++it) { XFreeCursor(m_display, it->second); @@ -1172,17 +1185,29 @@ GHOST_WindowX11:: XFreeCursor(m_display, m_custom_cursor); } - if (m_context != s_firstContext) { - glXDestroyContext(m_display, m_context); + if (m_valid_setup) { + static Atom Primary_atom, Clipboard_atom; + Window p_owner, c_owner; + /*Change the owner of the Atoms to None if we are the owner*/ + Primary_atom = XInternAtom(m_display, "PRIMARY", False); + Clipboard_atom = XInternAtom(m_display, "CLIPBOARD", False); + + + p_owner = XGetSelectionOwner(m_display, Primary_atom); + c_owner = XGetSelectionOwner(m_display, Clipboard_atom); + + if (p_owner == m_window) { + XSetSelectionOwner(m_display, Primary_atom, None, CurrentTime); + } + if (c_owner == m_window) { + XSetSelectionOwner(m_display, Clipboard_atom, None, CurrentTime); + } } - if (p_owner == m_window) { - XSetSelectionOwner(m_display, Primary_atom, None, CurrentTime); - } - if (c_owner == m_window) { - XSetSelectionOwner(m_display, Clipboard_atom, None, CurrentTime); + if (m_visualInfo) { + XFree(m_visualInfo); } - + #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) if (m_xic) { XDestroyIC(m_xic); @@ -1193,88 +1218,103 @@ GHOST_WindowX11:: delete m_dropTarget; #endif - XDestroyWindow(m_display, m_window); - XFree(m_visual); -} - + releaseNativeHandles(); + if (m_valid_setup) { + XDestroyWindow(m_display, m_window); + } +} -/** - * Tries to install a rendering context in this window. - * \param type The type of rendering context installed. - * \return Indication as to whether installation has succeeded. - */ -GHOST_TSuccess -GHOST_WindowX11:: -installDrawingContext( - GHOST_TDrawingContextType type) +GHOST_Context *GHOST_WindowX11::newDrawingContext(GHOST_TDrawingContextType type) { - /* only support openGL for now. */ - GHOST_TSuccess success; - switch (type) { - case GHOST_kDrawingContextTypeOpenGL: - { -#ifdef WITH_X11_XINPUT - /* use our own event handlers to avoid exiting blender, - * this would happen for eg: - * if you open blender, unplug a tablet, then open a new window. */ - XErrorHandler old_handler = XSetErrorHandler(GHOST_X11_ApplicationErrorHandler); - XIOErrorHandler old_handler_io = XSetIOErrorHandler(GHOST_X11_ApplicationIOErrorHandler); + if (type == GHOST_kDrawingContextTypeOpenGL) { +#if !defined(WITH_GL_EGL) + +#if defined(WITH_GL_PROFILE_CORE) + GHOST_Context *context = new GHOST_ContextGLX( + m_wantStereoVisual, + m_wantNumOfAASamples, + m_window, + m_display, + m_visualInfo, + 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, + 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); +#elif defined(WITH_GL_PROFILE_COMPAT) + GHOST_Context *context = new GHOST_ContextGLX( + m_wantStereoVisual, + m_wantNumOfAASamples, + m_window, + m_display, + m_visualInfo, + 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); +#else +# error #endif - m_context = glXCreateContext(m_display, m_visual, s_firstContext, True); - if (m_context != NULL) { - if (!s_firstContext) { - s_firstContext = m_context; - } - glXMakeCurrent(m_display, m_window, m_context); - glClearColor(0.447, 0.447, 0.447, 0); - glClear(GL_COLOR_BUFFER_BIT); - success = GHOST_kSuccess; - } - else { - success = GHOST_kFailure; - } +#else + +#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( + 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 -#ifdef WITH_X11_XINPUT - /* Restore handler */ - (void) XSetErrorHandler(old_handler); - (void) XSetIOErrorHandler(old_handler_io); #endif - break; - } - case GHOST_kDrawingContextTypeNone: - { - success = GHOST_kSuccess; - break; - } - default: - success = GHOST_kFailure; + if (context->initializeDrawingContext()) + return context; + else + delete context; } - return success; -} - - - -/** - * Removes the current drawing context. - * \return Indication as to whether removal has succeeded. - */ -GHOST_TSuccess -GHOST_WindowX11:: -removeDrawingContext() -{ - GHOST_TSuccess success; - if (m_context != NULL) { - glXDestroyContext(m_display, m_context); - success = GHOST_kSuccess; - } - else { - success = GHOST_kFailure; - } - return success; + return NULL; } @@ -1336,13 +1376,13 @@ getEmptyCursor( ) { if (!m_empty_cursor) { Pixmap blank; - XColor dummy; + XColor dummy = {0}; char data[1] = {0}; /* make a blank cursor */ blank = XCreateBitmapFromData( m_display, - RootWindow(m_display, DefaultScreen(m_display)), + RootWindow(m_display, m_visualInfo->screen), data, 1, 1 ); @@ -1458,10 +1498,10 @@ setWindowCustomCursorShape( int sizey, int hotX, int hotY, - int fg_color, - int bg_color) + int /*fg_color*/, + int /*bg_color*/) { - Colormap colormap = DefaultColormap(m_display, DefaultScreen(m_display)); + Colormap colormap = DefaultColormap(m_display, m_visualInfo->screen); Pixmap bitmap_pix, mask_pix; XColor fg, bg; @@ -1530,72 +1570,3 @@ endFullScreen() const return GHOST_kSuccess; } - -GHOST_TSuccess -GHOST_WindowX11:: -setSwapInterval(int interval) { - if (!GLX_EXT_swap_control || !glXSwapIntervalEXT -#ifdef SWAP_INTERVALS_WORKAROUND - || g_swap_interval_disabled -#endif // SWAP_INTERVALS_WORKAROUND - ) - { - return GHOST_kFailure; - } - glXSwapIntervalEXT(m_display, m_window, interval); - return GHOST_kSuccess; -} - -#ifdef SWAP_INTERVALS_WORKAROUND -static int QueryDrawable_ApplicationErrorHandler(Display *display, XErrorEvent *theEvent) -{ - fprintf(stderr, "Ignoring Xlib error: error code %d request code %d\n", - theEvent->error_code, theEvent->request_code); - if (!g_swap_interval_disabled) { - fprintf(stderr, "Disabling SWAP INTERVALS extension\n"); - g_swap_interval_disabled = true; - } - return 0; -} - -static int QueryDrawable_ApplicationIOErrorHandler(Display *display) -{ - fprintf(stderr, "Ignoring Xlib error: error IO\n"); - if (!g_swap_interval_disabled) { - fprintf(stderr, "Disabling SWAP INTERVALS extension\n"); - g_swap_interval_disabled = true; - } - return 0; -} -#endif // SWAP_INTERVALS_WORKAROUND - -int -GHOST_WindowX11:: -getSwapInterval() { - if (GLX_EXT_swap_control) { -#ifdef SWAP_INTERVALS_WORKAROUND - /* XXX: Current MESA driver will give GLXBadDrawable for all - * the glXQueryDrawable requests with direct contexts. - * - * To prevent crashes and unexpected behaviors, we will - * disable swap intervals extension if query fails here. - * (because if we will override interval without having - * old value we couldn't restore it properly). - */ - XErrorHandler old_handler = XSetErrorHandler(QueryDrawable_ApplicationErrorHandler); - XIOErrorHandler old_handler_io = XSetIOErrorHandler(QueryDrawable_ApplicationIOErrorHandler); -#endif // SWAP_INTERVALS_WORKAROUND - - unsigned int value = 0; - glXQueryDrawable(m_display, m_window, GLX_SWAP_INTERVAL_EXT, &value); - -#ifdef SWAP_INTERVALS_WORKAROUND - /* Restore handler */ - (void) XSetErrorHandler(old_handler); - (void) XSetIOErrorHandler(old_handler_io); -#endif // SWAP_INTERVALS_WORKAROUND - - return (int)value; - } - return 0; -} diff --git a/intern/ghost/intern/GHOST_WindowX11.h b/intern/ghost/intern/GHOST_WindowX11.h index faaa05efe82..5beb7b43032 100644 --- a/intern/ghost/intern/GHOST_WindowX11.h +++ b/intern/ghost/intern/GHOST_WindowX11.h @@ -35,7 +35,7 @@ #include "GHOST_Window.h" #include <X11/Xlib.h> -#include <GL/glx.h> +#include <X11/Xutil.h> // For tablets #ifdef WITH_X11_XINPUT # include <X11/extensions/XInput.h> @@ -88,7 +88,8 @@ public: GHOST_TDrawingContextType type = GHOST_kDrawingContextTypeNone, const bool stereoVisual = false, const bool exclusive = false, - const GHOST_TUns16 numOfAASamples = 0 + const GHOST_TUns16 numOfAASamples = 0, + const bool is_debug = false ); bool @@ -160,13 +161,6 @@ public: ); GHOST_TSuccess - swapBuffers( - ); - - GHOST_TSuccess - activateDrawingContext( - ); - GHOST_TSuccess invalidate( ); @@ -235,37 +229,12 @@ public: GHOST_TSuccess endFullScreen() const; - /** - * Sets the swap interval for swapBuffers. - * \param interval The swap interval to use. - * \return A boolean success indicator. - */ - virtual GHOST_TSuccess setSwapInterval(int interval); - - /** - * Gets the current swap interval for swapBuffers. - * \return An integer. - */ - virtual int getSwapInterval(); - protected: /** - * Tries to install a rendering context in this window. - * \param type The type of rendering context installed. - * \return Indication as to whether installation has succeeded. + * \param type The type of rendering context create. + * \return Indication of success. */ - GHOST_TSuccess - installDrawingContext( - GHOST_TDrawingContextType type - ); - - /** - * Removes the current drawing context. - * \return Indication as to whether removal has succeeded. - */ - GHOST_TSuccess - removeDrawingContext( - ); + GHOST_Context *newDrawingContext(GHOST_TDrawingContextType type); /** * Sets the cursor visibility on the window using @@ -279,7 +248,6 @@ protected: /** * Sets the cursor grab on the window using * native window system calls. - * \param warp Only used when grab is enabled, hides the mouse and allows dragging outside the screen. */ GHOST_TSuccess setWindowCursorGrab( @@ -350,21 +318,15 @@ private: void initXInputDevices(); #endif - GLXContext m_context; Window m_window; - Display *m_display; - XVisualInfo *m_visual; - GHOST_TWindowState m_normal_state; + Display *m_display; + XVisualInfo *m_visualInfo; - /** The first created OpenGL context (for sharing display lists) */ - static GLXContext s_firstContext; + GHOST_TWindowState m_normal_state; - /// A pointer to the typed system class. - + /** A pointer to the typed system class. */ GHOST_SystemX11 *m_system; - bool m_valid_setup; - /** Used to concatenate calls to invalidate() on this window. */ bool m_invalid_window; @@ -392,6 +354,9 @@ private: XIC m_xic; #endif + bool m_valid_setup; + bool m_is_debug_context; + void icccmSetState(int state); int icccmGetState() const; |