diff options
Diffstat (limited to 'intern/ghost/intern')
45 files changed, 3184 insertions, 544 deletions
diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp index af2a13945d1..843684b6d2e 100644 --- a/intern/ghost/intern/GHOST_C-api.cpp +++ b/intern/ghost/intern/GHOST_C-api.cpp @@ -24,6 +24,7 @@ */ #include <stdlib.h> +#include <string.h> #include "GHOST_C-api.h" #include "GHOST_IEvent.h" @@ -150,25 +151,6 @@ GHOST_TSuccess GHOST_DisposeOpenGLContext(GHOST_SystemHandle systemhandle, return system->disposeContext(context); } -#ifdef WIN32 -GHOST_ContextHandle GHOST_CreateDirectXContext(GHOST_SystemHandle systemhandle) -{ - GHOST_ISystem *system = (GHOST_ISystem *)systemhandle; - - return (GHOST_ContextHandle)system->createOffscreenContext(GHOST_kDrawingContextTypeD3D); -} - -GHOST_TSuccess GHOST_DisposeDirectXContext(GHOST_SystemHandle systemhandle, - GHOST_ContextHandle contexthandle) -{ - GHOST_ISystem *system = (GHOST_ISystem *)systemhandle; - GHOST_IContext *context = (GHOST_IContext *)contexthandle; - - return system->disposeContext(context); -} - -#endif - GHOST_WindowHandle GHOST_CreateWindow(GHOST_SystemHandle systemhandle, const char *title, GHOST_TInt32 left, @@ -546,17 +528,15 @@ void GHOST_SetTitle(GHOST_WindowHandle windowhandle, const char *title) char *GHOST_GetTitle(GHOST_WindowHandle windowhandle) { GHOST_IWindow *window = (GHOST_IWindow *)windowhandle; - STR_String title; - - window->getTitle(title); + std::string title = window->getTitle(); - char *ctitle = (char *)malloc(title.Length() + 1); + char *ctitle = (char *)malloc(title.size() + 1); if (ctitle == NULL) { return NULL; } - strcpy(ctitle, title.Ptr()); + strcpy(ctitle, title.c_str()); return ctitle; } @@ -697,7 +677,7 @@ GHOST_TSuccess GHOST_ActivateOpenGLContext(GHOST_ContextHandle contexthandle) return context->activateDrawingContext(); } else { - GHOST_PRINT("GHOST_ActivateOpenGLContext: Context not valid"); + GHOST_PRINTF("%s: Context not valid\n", __func__); return GHOST_kFailure; } } @@ -716,13 +696,6 @@ unsigned int GHOST_GetContextDefaultOpenGLFramebuffer(GHOST_ContextHandle contex return context->getDefaultFramebuffer(); } -int GHOST_isUpsideDownContext(GHOST_ContextHandle contexthandle) -{ - GHOST_IContext *context = (GHOST_IContext *)contexthandle; - - return context->isUpsideDown(); -} - unsigned int GHOST_GetDefaultOpenGLFramebuffer(GHOST_WindowHandle windowhandle) { GHOST_IWindow *window = (GHOST_IWindow *)windowhandle; @@ -743,11 +716,6 @@ void GHOST_SetTabletAPI(GHOST_SystemHandle systemhandle, GHOST_TTabletAPI api) system->setTabletAPI(api); } -const GHOST_TabletData *GHOST_GetTabletData(GHOST_WindowHandle windowhandle) -{ - return &((GHOST_IWindow *)windowhandle)->GetTabletData(); -} - GHOST_TInt32 GHOST_GetWidthRectangle(GHOST_RectangleHandle rectanglehandle) { return ((GHOST_Rect *)rectanglehandle)->getWidth(); @@ -991,4 +959,12 @@ void GHOST_XrDrawViewFunc(GHOST_XrContextHandle xr_contexthandle, GHOST_XrDrawVi GHOST_XR_CAPI_CALL(xr_context->setDrawViewFunc(draw_view_fn), xr_context); } +int GHOST_XrSessionNeedsUpsideDownDrawing(const GHOST_XrContextHandle xr_contexthandle) +{ + GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; + + GHOST_XR_CAPI_CALL_RET(xr_context->needsUpsideDownDrawing(), xr_context); + return 0; /* Only reached if exception is thrown. */ +} + #endif diff --git a/intern/ghost/intern/GHOST_Context.h b/intern/ghost/intern/GHOST_Context.h index 0bd6f63d07e..411a7de5c79 100644 --- a/intern/ghost/intern/GHOST_Context.h +++ b/intern/ghost/intern/GHOST_Context.h @@ -120,9 +120,9 @@ class GHOST_Context : public GHOST_IContext { } /** - * Returns if the window is rendered upside down compared to OpenGL. + * Returns if the context is rendered upside down compared to OpenGL. */ - inline bool isUpsideDown() const + virtual inline bool isUpsideDown() const { return false; } diff --git a/intern/ghost/intern/GHOST_Debug.h b/intern/ghost/intern/GHOST_Debug.h index 0163197e14a..5b5c2688297 100644 --- a/intern/ghost/intern/GHOST_Debug.h +++ b/intern/ghost/intern/GHOST_Debug.h @@ -33,15 +33,11 @@ #endif #ifdef WITH_GHOST_DEBUG -# define GHOST_DEBUG // spit ghost events to stdout -#endif // WITH_GHOST_DEBUG - -#ifdef GHOST_DEBUG # include <iostream> # include <stdio.h> //for printf() -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG # define GHOST_PRINT(x) \ { \ std::cout << x; \ @@ -52,10 +48,10 @@ printf(x, __VA_ARGS__); \ } \ (void)0 -#else // GHOST_DEBUG +#else // WITH_GHOST_DEBUG # define GHOST_PRINT(x) # define GHOST_PRINTF(x, ...) -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG #ifdef WITH_ASSERT_ABORT # include <stdio.h> //for fprintf() @@ -70,7 +66,7 @@ } \ } \ (void)0 -#elif defined(GHOST_DEBUG) +#elif defined(WITH_GHOST_DEBUG) # define GHOST_ASSERT(x, info) \ { \ if (!(x)) { \ @@ -80,8 +76,8 @@ } \ } \ (void)0 -#else // GHOST_DEBUG +#else // WITH_GHOST_DEBUG # define GHOST_ASSERT(x, info) ((void)0) -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG #endif // __GHOST_DEBUG_H__ diff --git a/intern/ghost/intern/GHOST_DisplayManagerWin32.cpp b/intern/ghost/intern/GHOST_DisplayManagerWin32.cpp index aabaffc7732..3557c4cd0c5 100644 --- a/intern/ghost/intern/GHOST_DisplayManagerWin32.cpp +++ b/intern/ghost/intern/GHOST_DisplayManagerWin32.cpp @@ -80,13 +80,13 @@ GHOST_TSuccess GHOST_DisplayManagerWin32::getDisplaySetting(GHOST_TUns8 display, GHOST_TSuccess success; DEVMODE dm; if (::EnumDisplaySettings(display_device.DeviceName, index, &dm)) { -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG printf("display mode: width=%d, height=%d, bpp=%d, frequency=%d\n", dm.dmPelsWidth, dm.dmPelsHeight, dm.dmBitsPerPel, dm.dmDisplayFrequency); -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG setting.xPixels = dm.dmPelsWidth; setting.yPixels = dm.dmPelsHeight; setting.bpp = dm.dmBitsPerPel; @@ -142,16 +142,16 @@ GHOST_TSuccess GHOST_DisplayManagerWin32::setCurrentDisplaySetting( * dm.dmSize = sizeof(DEVMODE); * dm.dmDriverExtra = 0; */ -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG printf("display change: Requested settings:\n"); printf(" dmBitsPerPel=%d\n", dm.dmBitsPerPel); printf(" dmPelsWidth=%d\n", dm.dmPelsWidth); printf(" dmPelsHeight=%d\n", dm.dmPelsHeight); printf(" dmDisplayFrequency=%d\n", dm.dmDisplayFrequency); -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG LONG status = ::ChangeDisplaySettings(&dm, CDS_FULLSCREEN); -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG switch (status) { case DISP_CHANGE_SUCCESSFUL: printf("display change: The settings change was successful.\n"); @@ -182,6 +182,6 @@ GHOST_TSuccess GHOST_DisplayManagerWin32::setCurrentDisplaySetting( printf("display change: Return value invalid\n"); break; } -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG return status == DISP_CHANGE_SUCCESSFUL ? GHOST_kSuccess : GHOST_kFailure; } diff --git a/intern/ghost/intern/GHOST_DropTargetWin32.cpp b/intern/ghost/intern/GHOST_DropTargetWin32.cpp index 9f8ce3b5095..fe11d9a28f2 100644 --- a/intern/ghost/intern/GHOST_DropTargetWin32.cpp +++ b/intern/ghost/intern/GHOST_DropTargetWin32.cpp @@ -28,10 +28,10 @@ #include "utf_winfunc.h" #include "utfconv.h" -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG // utility void printLastError(void); -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG GHOST_DropTargetWin32::GHOST_DropTargetWin32(GHOST_WindowWin32 *window, GHOST_SystemWin32 *system) : m_window(window), m_system(system) @@ -209,9 +209,9 @@ void *GHOST_DropTargetWin32::getGhostData(IDataObject *pDataObject) // return getDropDataAsBitmap(pDataObject); break; default: -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG ::printf("\nGHOST_kDragnDropTypeUnknown"); -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG return NULL; break; } @@ -284,10 +284,10 @@ void *GHOST_DropTargetWin32::getDropDataAsString(IDataObject *pDataObject) // Free memory ::GlobalUnlock(stgmed.hGlobal); ::ReleaseStgMedium(&stgmed); -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG ::printf("\n<converted droped unicode string>\n%s\n</droped converted unicode string>\n", tmp_string); -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG return tmp_string; } } @@ -336,9 +336,9 @@ int GHOST_DropTargetWin32::WideCharToANSI(LPCWSTR in, char *&out) NULL); if (!size) { -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG ::printLastError(); -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG return 0; } @@ -351,16 +351,16 @@ int GHOST_DropTargetWin32::WideCharToANSI(LPCWSTR in, char *&out) size = ::WideCharToMultiByte(CP_ACP, 0x00000400, in, -1, (LPSTR)out, size, NULL, NULL); if (!size) { -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG ::printLastError(); -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG ::free(out); out = NULL; } return size; } -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG void printLastError(void) { LPTSTR s; @@ -378,4 +378,4 @@ void printLastError(void) LocalFree(s); } } -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG diff --git a/intern/ghost/intern/GHOST_EventButton.h b/intern/ghost/intern/GHOST_EventButton.h index e854bc23e5a..4247ae150a4 100644 --- a/intern/ghost/intern/GHOST_EventButton.h +++ b/intern/ghost/intern/GHOST_EventButton.h @@ -37,17 +37,19 @@ class GHOST_EventButton : public GHOST_Event { * Constructor. * \param time The time this event was generated. * \param type The type of this event. - * \param window: The window of this event. - * \param button: The state of the buttons were at the time of the event. + * \param window The window of this event. + * \param button The state of the buttons were at the time of the event. + * \param tablet The tablet data associated with this event. */ GHOST_EventButton(GHOST_TUns64 time, GHOST_TEventType type, GHOST_IWindow *window, - GHOST_TButtonMask button) + GHOST_TButtonMask button, + const GHOST_TabletData &tablet) : GHOST_Event(time, type, window) { m_buttonEventData.button = button; - m_buttonEventData.tablet = window->GetTabletData(); + m_buttonEventData.tablet = tablet; m_data = &m_buttonEventData; } diff --git a/intern/ghost/intern/GHOST_EventCursor.h b/intern/ghost/intern/GHOST_EventCursor.h index 41597db216a..8ba657fd9fa 100644 --- a/intern/ghost/intern/GHOST_EventCursor.h +++ b/intern/ghost/intern/GHOST_EventCursor.h @@ -38,17 +38,19 @@ class GHOST_EventCursor : public GHOST_Event { * \param type The type of this event. * \param x The x-coordinate of the location the cursor was at the time of the event. * \param y The y-coordinate of the location the cursor was at the time of the event. + * \param tablet The tablet data associated with this event. */ GHOST_EventCursor(GHOST_TUns64 msec, GHOST_TEventType type, GHOST_IWindow *window, GHOST_TInt32 x, - GHOST_TInt32 y) + GHOST_TInt32 y, + const GHOST_TabletData &tablet) : GHOST_Event(msec, type, window) { m_cursorEventData.x = x; m_cursorEventData.y = y; - m_cursorEventData.tablet = window->GetTabletData(); + m_cursorEventData.tablet = tablet; m_data = &m_cursorEventData; } diff --git a/intern/ghost/intern/GHOST_EventKey.h b/intern/ghost/intern/GHOST_EventKey.h index 24e20b20659..8f59c555914 100644 --- a/intern/ghost/intern/GHOST_EventKey.h +++ b/intern/ghost/intern/GHOST_EventKey.h @@ -25,6 +25,8 @@ #ifndef __GHOST_EVENTKEY_H__ #define __GHOST_EVENTKEY_H__ +#include <string.h> + #include "GHOST_Event.h" /** diff --git a/intern/ghost/intern/GHOST_EventPrinter.h b/intern/ghost/intern/GHOST_EventPrinter.h index fad9ec3cc69..ead16525ec6 100644 --- a/intern/ghost/intern/GHOST_EventPrinter.h +++ b/intern/ghost/intern/GHOST_EventPrinter.h @@ -27,8 +27,6 @@ #include "GHOST_IEventConsumer.h" -#include "STR_String.h" - /** * An Event consumer that prints all the events to standard out. * Really useful when debugging. diff --git a/intern/ghost/intern/GHOST_ISystem.cpp b/intern/ghost/intern/GHOST_ISystem.cpp index 914f6712676..7c12bfe0306 100644 --- a/intern/ghost/intern/GHOST_ISystem.cpp +++ b/intern/ghost/intern/GHOST_ISystem.cpp @@ -27,20 +27,22 @@ #include "GHOST_ISystem.h" -#ifdef WITH_X11 +#if defined(WITH_HEADLESS) +# include "GHOST_SystemNULL.h" +#elif defined(WITH_GHOST_X11) && defined(WITH_GHOST_WAYLAND) +# include "GHOST_SystemWayland.h" # include "GHOST_SystemX11.h" -#else -# ifdef WITH_HEADLESS -# include "GHOST_SystemNULL.h" -# elif defined(WITH_GHOST_SDL) -# include "GHOST_SystemSDL.h" -# elif defined(WIN32) -# include "GHOST_SystemWin32.h" -# else -# ifdef __APPLE__ -# include "GHOST_SystemCocoa.h" -# endif -# endif +# include <stdexcept> +#elif defined(WITH_GHOST_X11) +# include "GHOST_SystemX11.h" +#elif defined(WITH_GHOST_WAYLAND) +# include "GHOST_SystemWayland.h" +#elif defined(WITH_GHOST_SDL) +# include "GHOST_SystemSDL.h" +#elif defined(WIN32) +# include "GHOST_SystemWin32.h" +#elif defined(__APPLE__) +# include "GHOST_SystemCocoa.h" #endif GHOST_ISystem *GHOST_ISystem::m_system = NULL; @@ -49,20 +51,29 @@ GHOST_TSuccess GHOST_ISystem::createSystem() { GHOST_TSuccess success; if (!m_system) { -#ifdef WITH_X11 - m_system = new GHOST_SystemX11(); -#else -# ifdef WITH_HEADLESS +#if defined(WITH_HEADLESS) m_system = new GHOST_SystemNULL(); -# elif defined(WITH_GHOST_SDL) +#elif defined(WITH_GHOST_X11) && defined(WITH_GHOST_WAYLAND) + /* Special case, try Wayland, fall back to X11. */ + try { + m_system = new GHOST_SystemWayland(); + } + catch (const std::runtime_error &) { + /* fallback to X11. */ + } + if (!m_system) { + m_system = new GHOST_SystemX11(); + } +#elif defined(WITH_GHOST_X11) + m_system = new GHOST_SystemX11(); +#elif defined(WITH_GHOST_WAYLAND) + m_system = new GHOST_SystemWayland(); +#elif defined(WITH_GHOST_SDL) m_system = new GHOST_SystemSDL(); -# elif defined(WIN32) +#elif defined(WIN32) m_system = new GHOST_SystemWin32(); -# else -# ifdef __APPLE__ +#elif defined(__APPLE__) m_system = new GHOST_SystemCocoa(); -# endif -# endif #endif success = m_system != NULL ? GHOST_kSuccess : GHOST_kFailure; } diff --git a/intern/ghost/intern/GHOST_IXrGraphicsBinding.h b/intern/ghost/intern/GHOST_IXrGraphicsBinding.h index 25281d3d0ba..5794a682023 100644 --- a/intern/ghost/intern/GHOST_IXrGraphicsBinding.h +++ b/intern/ghost/intern/GHOST_IXrGraphicsBinding.h @@ -33,7 +33,7 @@ class GHOST_IXrGraphicsBinding { public: union { -#if defined(WITH_X11) +#if defined(WITH_GHOST_X11) XrGraphicsBindingOpenGLXlibKHR glx; #elif defined(WIN32) XrGraphicsBindingOpenGLWin32KHR wgl; @@ -61,6 +61,7 @@ class GHOST_IXrGraphicsBinding { uint32_t image_count) = 0; virtual void submitToSwapchainImage(XrSwapchainImageBaseHeader *swapchain_image, const GHOST_XrDrawViewInfo *draw_info) = 0; + virtual bool needsUpsideDownDrawing(GHOST_Context &ghost_ctx) const = 0; protected: /* Use GHOST_XrGraphicsBindingCreateFromType! */ @@ -68,6 +69,6 @@ class GHOST_IXrGraphicsBinding { }; std::unique_ptr<GHOST_IXrGraphicsBinding> GHOST_XrGraphicsBindingCreateFromType( - GHOST_TXrGraphicsBinding type); + GHOST_TXrGraphicsBinding type, GHOST_Context *ghost_ctx); #endif /* __GHOST_IXRGRAPHICSBINDING_H__ */ diff --git a/intern/ghost/intern/GHOST_NDOFManager.cpp b/intern/ghost/intern/GHOST_NDOFManager.cpp index a9fbadab37a..dda78c0ac5b 100644 --- a/intern/ghost/intern/GHOST_NDOFManager.cpp +++ b/intern/ghost/intern/GHOST_NDOFManager.cpp @@ -19,6 +19,8 @@ #include "GHOST_EventKey.h" #include "GHOST_EventNDOF.h" #include "GHOST_WindowManager.h" + +#include <limits.h> #include <math.h> #include <stdio.h> // for error/info reporting #include <string.h> // for memory functions diff --git a/intern/ghost/intern/GHOST_System.cpp b/intern/ghost/intern/GHOST_System.cpp index 97704435f04..b0d2adff4bc 100644 --- a/intern/ghost/intern/GHOST_System.cpp +++ b/intern/ghost/intern/GHOST_System.cpp @@ -23,8 +23,8 @@ #include "GHOST_System.h" +#include <chrono> #include <stdio.h> /* just for printf */ -#include <time.h> #include "GHOST_DisplayManager.h" #include "GHOST_EventManager.h" @@ -58,12 +58,9 @@ GHOST_System::~GHOST_System() GHOST_TUns64 GHOST_System::getMilliSeconds() const { - GHOST_TUns64 millis = ::clock(); - if (CLOCKS_PER_SEC != 1000) { - millis *= 1000; - millis /= CLOCKS_PER_SEC; - } - return millis; + return std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); } GHOST_ITimerTask *GHOST_System::installTimer(GHOST_TUns64 delay, @@ -122,16 +119,6 @@ GHOST_TSuccess GHOST_System::disposeWindow(GHOST_IWindow *window) return success; } -GHOST_IContext *GHOST_System::createOffscreenContext(GHOST_TDrawingContextType type) -{ - switch (type) { - case GHOST_kDrawingContextTypeOpenGL: - return createOffscreenContext(); - default: - return NULL; - } -} - bool GHOST_System::validWindow(GHOST_IWindow *window) { return m_windowManager->getWindowFound(window); @@ -319,12 +306,12 @@ GHOST_TSuccess GHOST_System::init() m_windowManager = new GHOST_WindowManager(); m_eventManager = new GHOST_EventManager(); -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG if (m_eventManager) { m_eventPrinter = new GHOST_EventPrinter(); m_eventManager->addConsumer(m_eventPrinter); } -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG if (m_timerManager && m_windowManager && m_eventManager) { return GHOST_kSuccess; @@ -377,7 +364,7 @@ GHOST_TSuccess GHOST_System::createFullScreenWindow(GHOST_Window **window, GHOST_ASSERT(m_displayManager, "GHOST_System::createFullScreenWindow(): invalid display manager"); // GHOST_PRINT("GHOST_System::createFullScreenWindow(): creating full-screen window\n"); - *window = (GHOST_Window *)createWindow(STR_String(""), + *window = (GHOST_Window *)createWindow("", 0, 0, settings.xPixels, diff --git a/intern/ghost/intern/GHOST_System.h b/intern/ghost/intern/GHOST_System.h index 2eec6ca43b3..c2d712c11cd 100644 --- a/intern/ghost/intern/GHOST_System.h +++ b/intern/ghost/intern/GHOST_System.h @@ -31,9 +31,9 @@ #include "GHOST_Debug.h" #include "GHOST_EventManager.h" #include "GHOST_ModifierKeys.h" -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG # include "GHOST_EventPrinter.h" -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG class GHOST_DisplayManager; class GHOST_Event; @@ -119,12 +119,6 @@ class GHOST_System : public GHOST_ISystem { virtual GHOST_IContext *createOffscreenContext() = 0; /** - * Overload to allow requesting a different context type. By default only OpenGL is supported. - * However by explicitly overloading this a system may add support for others. - */ - GHOST_IContext *createOffscreenContext(GHOST_TDrawingContextType type); - - /** * Returns whether a window is valid. * \param window Pointer to the window to be checked. * \return Indication of validity. @@ -225,7 +219,7 @@ class GHOST_System : public GHOST_ISystem { ***************************************************************************************/ /** - * Returns the state of a modifier key (ouside the message queue). + * Returns the state of a modifier key (outside the message queue). * \param mask The modifier key state to retrieve. * \param isDown The state of a modifier key (true == pressed). * \return Indication of success. @@ -233,7 +227,7 @@ class GHOST_System : public GHOST_ISystem { GHOST_TSuccess getModifierKeyState(GHOST_TModifierKeyMask mask, bool &isDown) const; /** - * Returns the state of a mouse button (ouside the message queue). + * Returns the state of a mouse button (outside the message queue). * \param mask The button state to retrieve. * \param isDown Button state. * \return Indication of success. @@ -253,8 +247,8 @@ class GHOST_System : public GHOST_ISystem { ***************************************************************************************/ /** - * Sets 3D mouse deadzone - * \param deadzone: Deadzone of the 3D mouse (both for rotation and pan) relative to full range + * Sets 3D mouse dead-zone + * \param deadzone: Dead-zone of the 3D mouse (both for rotation and pan) relative to full range. */ void setNDOFDeadZone(float deadzone); #endif @@ -301,7 +295,7 @@ class GHOST_System : public GHOST_ISystem { virtual GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const = 0; /** - * Returns the state of the mouse buttons (ouside the message queue). + * Returns the state of the mouse buttons (outside the message queue). * \param buttons The state of the buttons. * \return Indication of success. */ @@ -396,9 +390,9 @@ class GHOST_System : public GHOST_ISystem { #endif /** Prints all the events. */ -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG GHOST_EventPrinter *m_eventPrinter; -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG /** Settings of the display before the display went fullscreen. */ GHOST_DisplaySetting m_preFullScreenSetting; diff --git a/intern/ghost/intern/GHOST_SystemCocoa.h b/intern/ghost/intern/GHOST_SystemCocoa.h index 1e44c3e31d4..bbd6f1d8995 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.h +++ b/intern/ghost/intern/GHOST_SystemCocoa.h @@ -100,7 +100,7 @@ class GHOST_SystemCocoa : public GHOST_System { * \param parentWindow Parent (embedder) window * \return The new window (or 0 if creation failed). */ - GHOST_IWindow *createWindow(const STR_String &title, + GHOST_IWindow *createWindow(const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -198,7 +198,7 @@ class GHOST_SystemCocoa : public GHOST_System { GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const; /** - * Returns the state of the mouse buttons (ouside the message queue). + * Returns the state of the mouse buttons (outside the message queue). * \param buttons The state of the buttons. * \return Indication of success. */ diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index 9becff40995..5592078e20e 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -698,7 +698,7 @@ void GHOST_SystemCocoa::getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns3 getMainDisplayDimensions(width, height); } -GHOST_IWindow *GHOST_SystemCocoa::createWindow(const STR_String &title, +GHOST_IWindow *GHOST_SystemCocoa::createWindow(const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -817,7 +817,8 @@ GHOST_TSuccess GHOST_SystemCocoa::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 CGAssociateMouseAndMouseCursorPosition(true); // Force mouse move event (not pushed by Cocoa) - pushEvent(new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, x, y)); + pushEvent(new GHOST_EventCursor( + getMilliSeconds(), GHOST_kEventCursorMove, window, x, y, window->GetCocoaTabletData())); m_outsideLoopEventProcessed = true; return GHOST_kSuccess; @@ -1062,14 +1063,7 @@ GHOST_TSuccess GHOST_SystemCocoa::handleWindowEvent(GHOST_TEventType eventType, } 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 - } + pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window)); break; case GHOST_kEventWindowActivate: m_windowManager->setActiveWindow(window); @@ -1098,8 +1092,11 @@ GHOST_TSuccess GHOST_SystemCocoa::handleWindowEvent(GHOST_TEventType eventType, 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, GHOST_kButtonMaskLeft)); + pushEvent(new GHOST_EventButton(getMilliSeconds(), + GHOST_kEventButtonUp, + window, + GHOST_kButtonMaskLeft, + GHOST_TABLET_DATA_NONE)); // m_ignoreWindowSizedMessages = true; } break; @@ -1437,7 +1434,7 @@ GHOST_TSuccess GHOST_SystemCocoa::handleTabletEvent(void *eventPtr, short eventT case NSEventTypeTabletProximity: /* Reset tablet data when device enters proximity or leaves. */ - ct = GHOST_TABLET_DATA_DEFAULT; + ct = GHOST_TABLET_DATA_NONE; if ([event isEnteringProximity]) { /* Pointer is entering tablet area proximity. */ switch ([event pointingDeviceType]) { @@ -1504,38 +1501,52 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr) switch ([event type]) { case NSEventTypeLeftMouseDown: handleTabletEvent(event); // Update window tablet state to be included in event. - pushEvent(new GHOST_EventButton( - [event timestamp] * 1000, GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft)); + pushEvent(new GHOST_EventButton([event timestamp] * 1000, + GHOST_kEventButtonDown, + window, + GHOST_kButtonMaskLeft, + window -> GetCocoaTabletData())); break; case NSEventTypeRightMouseDown: handleTabletEvent(event); // Update window tablet state to be included in event. - pushEvent(new GHOST_EventButton( - [event timestamp] * 1000, GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight)); + pushEvent(new GHOST_EventButton([event timestamp] * 1000, + GHOST_kEventButtonDown, + window, + GHOST_kButtonMaskRight, + window -> GetCocoaTabletData())); break; case NSEventTypeOtherMouseDown: handleTabletEvent(event); // Handle tablet events combined with mouse events pushEvent(new GHOST_EventButton([event timestamp] * 1000, GHOST_kEventButtonDown, window, - convertButton([event buttonNumber]))); + convertButton([event buttonNumber]), + window -> GetCocoaTabletData())); break; case NSEventTypeLeftMouseUp: handleTabletEvent(event); // Update window tablet state to be included in event. - pushEvent(new GHOST_EventButton( - [event timestamp] * 1000, GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft)); + pushEvent(new GHOST_EventButton([event timestamp] * 1000, + GHOST_kEventButtonUp, + window, + GHOST_kButtonMaskLeft, + window -> GetCocoaTabletData())); break; case NSEventTypeRightMouseUp: handleTabletEvent(event); // Update window tablet state to be included in event. - pushEvent(new GHOST_EventButton( - [event timestamp] * 1000, GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight)); + pushEvent(new GHOST_EventButton([event timestamp] * 1000, + GHOST_kEventButtonUp, + window, + GHOST_kButtonMaskRight, + window -> GetCocoaTabletData())); break; case NSEventTypeOtherMouseUp: handleTabletEvent(event); // Update window tablet state to be included in event. pushEvent(new GHOST_EventButton([event timestamp] * 1000, GHOST_kEventButtonUp, window, - convertButton([event buttonNumber]))); + convertButton([event buttonNumber]), + window -> GetCocoaTabletData())); break; case NSEventTypeLeftMouseDragged: @@ -1568,8 +1579,12 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr) window->setCursorGrabAccum(x_accum, y_accum); window->clientToScreenIntern(x_warp + x_accum, y_warp + y_accum, x, y); - pushEvent(new GHOST_EventCursor( - [event timestamp] * 1000, GHOST_kEventCursorMove, window, x, y)); + pushEvent(new GHOST_EventCursor([event timestamp] * 1000, + GHOST_kEventCursorMove, + window, + x, + y, + window -> GetCocoaTabletData())); break; } case GHOST_kGrabWrap: // Wrap cursor at area/window boundaries @@ -1614,8 +1629,12 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr) // Generate event GHOST_TInt32 x, y; window->clientToScreenIntern(x_mouse + x_accum, y_mouse + y_accum, x, y); - pushEvent(new GHOST_EventCursor( - [event timestamp] * 1000, GHOST_kEventCursorMove, window, x, y)); + pushEvent(new GHOST_EventCursor([event timestamp] * 1000, + GHOST_kEventCursorMove, + window, + x, + y, + window -> GetCocoaTabletData())); break; } default: { @@ -1624,8 +1643,12 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr) GHOST_TInt32 x, y; window->clientToScreenIntern(mousePos.x, mousePos.y, x, y); - pushEvent(new GHOST_EventCursor( - [event timestamp] * 1000, GHOST_kEventCursorMove, window, x, y)); + pushEvent(new GHOST_EventCursor([event timestamp] * 1000, + GHOST_kEventCursorMove, + window, + x, + y, + window -> GetCocoaTabletData())); break; } } diff --git a/intern/ghost/intern/GHOST_SystemNULL.h b/intern/ghost/intern/GHOST_SystemNULL.h index 68a726f2be8..186cb92d1aa 100644 --- a/intern/ghost/intern/GHOST_SystemNULL.h +++ b/intern/ghost/intern/GHOST_SystemNULL.h @@ -106,7 +106,7 @@ class GHOST_SystemNULL : public GHOST_System { return GHOST_kFailure; } - GHOST_IWindow *createWindow(const STR_String &title, + GHOST_IWindow *createWindow(const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp index 5dae0ce504b..b32ec4306e8 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.cpp +++ b/intern/ghost/intern/GHOST_SystemSDL.cpp @@ -49,7 +49,7 @@ GHOST_SystemSDL::~GHOST_SystemSDL() SDL_Quit(); } -GHOST_IWindow *GHOST_SystemSDL::createWindow(const STR_String &title, +GHOST_IWindow *GHOST_SystemSDL::createWindow(const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -390,22 +390,31 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) SDL_WarpMouseInWindow(sdl_win, x_new - x_win, y_new - y_win); } - g_event = new GHOST_EventCursor( - getMilliSeconds(), GHOST_kEventCursorMove, window, x_new, y_new); + g_event = new GHOST_EventCursor(getMilliSeconds(), + GHOST_kEventCursorMove, + window, + x_new, + y_new, + GHOST_TABLET_DATA_NONE); } else { g_event = new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, x_root + x_accum, - y_root + y_accum); + y_root + y_accum, + GHOST_TABLET_DATA_NONE); } } else #endif { - g_event = new GHOST_EventCursor( - getMilliSeconds(), GHOST_kEventCursorMove, window, x_root, y_root); + g_event = new GHOST_EventCursor(getMilliSeconds(), + GHOST_kEventCursorMove, + window, + x_root, + y_root, + GHOST_TABLET_DATA_NONE); } break; } @@ -435,7 +444,8 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) else break; - g_event = new GHOST_EventButton(getMilliSeconds(), type, window, gbmask); + g_event = new GHOST_EventButton( + getMilliSeconds(), type, window, gbmask, GHOST_TABLET_DATA_NONE); break; } case SDL_MOUSEWHEEL: { diff --git a/intern/ghost/intern/GHOST_SystemSDL.h b/intern/ghost/intern/GHOST_SystemSDL.h index 1994781530b..8feec9de61d 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.h +++ b/intern/ghost/intern/GHOST_SystemSDL.h @@ -80,7 +80,7 @@ class GHOST_SystemSDL : public GHOST_System { private: GHOST_TSuccess init(); - GHOST_IWindow *createWindow(const STR_String &title, + GHOST_IWindow *createWindow(const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp new file mode 100644 index 00000000000..31110694ea6 --- /dev/null +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -0,0 +1,1778 @@ +/* + * 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. + */ + +/** \file + * \ingroup GHOST + */ + +#include "GHOST_SystemWayland.h" +#include "GHOST_Event.h" +#include "GHOST_EventButton.h" +#include "GHOST_EventCursor.h" +#include "GHOST_EventDragnDrop.h" +#include "GHOST_EventKey.h" +#include "GHOST_EventWheel.h" +#include "GHOST_TimerManager.h" +#include "GHOST_WindowManager.h" + +#include "GHOST_ContextEGL.h" + +#include <EGL/egl.h> +#include <wayland-egl.h> + +#include <algorithm> +#include <atomic> +#include <stdexcept> +#include <thread> +#include <unordered_map> +#include <unordered_set> + +#include <pointer-constraints-client-protocol.h> +#include <relative-pointer-client-protocol.h> +#include <wayland-cursor.h> +#include <xkbcommon/xkbcommon.h> + +#include <fcntl.h> +#include <linux/input-event-codes.h> +#include <sys/mman.h> +#include <unistd.h> + +#include <cstring> + +struct output_t { + struct wl_output *output; + int32_t width, height; + int transform; + int scale; + std::string make; + std::string model; +}; + +struct buffer_t { + void *data; + size_t size; +}; + +struct cursor_t { + bool visible; + struct wl_surface *surface = nullptr; + struct wl_buffer *buffer; + struct wl_cursor_image image; + struct buffer_t *file_buffer = nullptr; +}; + +struct data_offer_t { + std::unordered_set<std::string> types; + uint32_t source_actions; + uint32_t dnd_action; + struct wl_data_offer *id; + std::atomic<bool> in_use; + struct { + int x, y; + } dnd; +}; + +struct data_source_t { + struct wl_data_source *data_source; + /** Last device that was active. */ + uint32_t source_serial; + char *buffer_out; +}; + +struct key_repeat_payload_t { + GHOST_SystemWayland *system; + GHOST_IWindow *window; + GHOST_TKey key; + GHOST_TEventKeyData key_data; +}; + +struct input_t { + GHOST_SystemWayland *system; + + std::string name; + struct wl_seat *seat; + struct wl_pointer *pointer = nullptr; + struct wl_keyboard *keyboard = nullptr; + + uint32_t pointer_serial; + int x, y; + GHOST_Buttons buttons; + struct cursor_t cursor; + + struct zwp_relative_pointer_v1 *relative_pointer; + struct zwp_locked_pointer_v1 *locked_pointer; + + struct xkb_context *xkb_context; + struct xkb_state *xkb_state; + struct { + /* Key repetition in character per second. */ + int32_t rate; + /* Time (milliseconds) after which to start repeating keys. */ + int32_t delay; + /* Timer for key repeats. */ + GHOST_ITimerTask *timer = nullptr; + } key_repeat; + + struct wl_surface *focus_pointer = nullptr; + struct wl_surface *focus_keyboard = nullptr; + + struct wl_data_device *data_device = nullptr; + struct data_offer_t *data_offer_dnd; /* Drag & Drop. */ + struct data_offer_t *data_offer_copy_paste; /* Copy & Paste. */ + + struct data_source_t *data_source; +}; + +struct display_t { + GHOST_SystemWayland *system; + + struct wl_display *display; + struct wl_compositor *compositor = nullptr; + struct xdg_wm_base *xdg_shell = nullptr; + struct wl_shm *shm = nullptr; + std::vector<output_t *> outputs; + std::vector<input_t *> inputs; + struct wl_cursor_theme *cursor_theme = nullptr; + struct wl_data_device_manager *data_device_manager = nullptr; + struct zwp_relative_pointer_manager_v1 *relative_pointer_manager = nullptr; + struct zwp_pointer_constraints_v1 *pointer_constraints = nullptr; + + std::vector<struct wl_surface *> os_surfaces; + std::vector<struct wl_egl_window *> os_egl_windows; +}; + +static void display_destroy(display_t *d) +{ + if (d->data_device_manager) { + wl_data_device_manager_destroy(d->data_device_manager); + } + + for (output_t *output : d->outputs) { + wl_output_destroy(output->output); + delete output; + } + + for (input_t *input : d->inputs) { + if (input->data_source) { + free(input->data_source->buffer_out); + if (input->data_source->data_source) { + wl_data_source_destroy(input->data_source->data_source); + } + delete input->data_source; + } + if (input->data_offer_copy_paste) { + wl_data_offer_destroy(input->data_offer_copy_paste->id); + delete input->data_offer_copy_paste; + } + if (input->data_device) { + wl_data_device_release(input->data_device); + } + if (input->pointer) { + if (input->cursor.file_buffer) { + munmap(input->cursor.file_buffer->data, input->cursor.file_buffer->size); + delete input->cursor.file_buffer; + } + if (input->cursor.surface) { + wl_surface_destroy(input->cursor.surface); + } + if (input->pointer) { + wl_pointer_destroy(input->pointer); + } + } + if (input->keyboard) { + if (input->key_repeat.timer) { + delete static_cast<key_repeat_payload_t *>(input->key_repeat.timer->getUserData()); + input->system->removeTimer(input->key_repeat.timer); + input->key_repeat.timer = nullptr; + } + wl_keyboard_destroy(input->keyboard); + } + if (input->xkb_state) { + xkb_state_unref(input->xkb_state); + } + if (input->xkb_context) { + xkb_context_unref(input->xkb_context); + } + wl_seat_destroy(input->seat); + delete input; + } + + if (d->cursor_theme) { + wl_cursor_theme_destroy(d->cursor_theme); + } + + if (d->shm) { + wl_shm_destroy(d->shm); + } + + if (d->relative_pointer_manager) { + zwp_relative_pointer_manager_v1_destroy(d->relative_pointer_manager); + } + + if (d->pointer_constraints) { + zwp_pointer_constraints_v1_destroy(d->pointer_constraints); + } + + for (wl_egl_window *os_egl_window : d->os_egl_windows) { + wl_egl_window_destroy(os_egl_window); + } + + for (wl_surface *os_surface : d->os_surfaces) { + wl_surface_destroy(os_surface); + } + + if (d->compositor) { + wl_compositor_destroy(d->compositor); + } + + if (d->xdg_shell) { + xdg_wm_base_destroy(d->xdg_shell); + } + + if (eglGetDisplay) { + ::eglTerminate(eglGetDisplay(EGLNativeDisplayType(d->display))); + } + + if (d->display) { + wl_display_disconnect(d->display); + } + + delete d; +} + +static GHOST_TKey xkb_map_gkey(const xkb_keysym_t &sym) +{ + + GHOST_TKey gkey; + if (sym >= XKB_KEY_0 && sym <= XKB_KEY_9) { + gkey = GHOST_TKey(sym); + } + else if (sym >= XKB_KEY_KP_0 && sym <= XKB_KEY_KP_9) { + gkey = GHOST_TKey(GHOST_kKeyNumpad0 + sym - XKB_KEY_KP_0); + } + else if (sym >= XKB_KEY_A && sym <= XKB_KEY_Z) { + gkey = GHOST_TKey(sym); + } + else if (sym >= XKB_KEY_a && sym <= XKB_KEY_z) { + gkey = GHOST_TKey(sym - XKB_KEY_a + XKB_KEY_A); + } + else if (sym >= XKB_KEY_F1 && sym <= XKB_KEY_F24) { + gkey = GHOST_TKey(GHOST_kKeyF1 + sym - XKB_KEY_F1); + } + else { + +#define GXMAP(k, x, y) \ + case x: \ + k = y; \ + break + + switch (sym) { + GXMAP(gkey, XKB_KEY_BackSpace, GHOST_kKeyBackSpace); + GXMAP(gkey, XKB_KEY_Tab, GHOST_kKeyTab); + GXMAP(gkey, XKB_KEY_Linefeed, GHOST_kKeyLinefeed); + GXMAP(gkey, XKB_KEY_Clear, GHOST_kKeyClear); + GXMAP(gkey, XKB_KEY_Return, GHOST_kKeyEnter); + + GXMAP(gkey, XKB_KEY_Escape, GHOST_kKeyEsc); + GXMAP(gkey, XKB_KEY_space, GHOST_kKeySpace); + GXMAP(gkey, XKB_KEY_apostrophe, GHOST_kKeyQuote); + GXMAP(gkey, XKB_KEY_comma, GHOST_kKeyComma); + GXMAP(gkey, XKB_KEY_minus, GHOST_kKeyMinus); + GXMAP(gkey, XKB_KEY_plus, GHOST_kKeyPlus); + GXMAP(gkey, XKB_KEY_period, GHOST_kKeyPeriod); + GXMAP(gkey, XKB_KEY_slash, GHOST_kKeySlash); + + GXMAP(gkey, XKB_KEY_semicolon, GHOST_kKeySemicolon); + GXMAP(gkey, XKB_KEY_equal, GHOST_kKeyEqual); + + GXMAP(gkey, XKB_KEY_bracketleft, GHOST_kKeyLeftBracket); + GXMAP(gkey, XKB_KEY_bracketright, GHOST_kKeyRightBracket); + GXMAP(gkey, XKB_KEY_backslash, GHOST_kKeyBackslash); + GXMAP(gkey, XKB_KEY_grave, GHOST_kKeyAccentGrave); + + GXMAP(gkey, XKB_KEY_Shift_L, GHOST_kKeyLeftShift); + GXMAP(gkey, XKB_KEY_Shift_R, GHOST_kKeyRightShift); + GXMAP(gkey, XKB_KEY_Control_L, GHOST_kKeyLeftControl); + GXMAP(gkey, XKB_KEY_Control_R, GHOST_kKeyRightControl); + GXMAP(gkey, XKB_KEY_Alt_L, GHOST_kKeyLeftAlt); + GXMAP(gkey, XKB_KEY_Alt_R, GHOST_kKeyRightAlt); + GXMAP(gkey, XKB_KEY_Super_L, GHOST_kKeyOS); + GXMAP(gkey, XKB_KEY_Super_R, GHOST_kKeyOS); + GXMAP(gkey, XKB_KEY_Menu, GHOST_kKeyApp); + + GXMAP(gkey, XKB_KEY_Caps_Lock, GHOST_kKeyCapsLock); + GXMAP(gkey, XKB_KEY_Num_Lock, GHOST_kKeyNumLock); + GXMAP(gkey, XKB_KEY_Scroll_Lock, GHOST_kKeyScrollLock); + + GXMAP(gkey, XKB_KEY_Left, GHOST_kKeyLeftArrow); + GXMAP(gkey, XKB_KEY_Right, GHOST_kKeyRightArrow); + GXMAP(gkey, XKB_KEY_Up, GHOST_kKeyUpArrow); + GXMAP(gkey, XKB_KEY_Down, GHOST_kKeyDownArrow); + + GXMAP(gkey, XKB_KEY_Print, GHOST_kKeyPrintScreen); + GXMAP(gkey, XKB_KEY_Pause, GHOST_kKeyPause); + + GXMAP(gkey, XKB_KEY_Insert, GHOST_kKeyInsert); + GXMAP(gkey, XKB_KEY_Delete, GHOST_kKeyDelete); + GXMAP(gkey, XKB_KEY_Home, GHOST_kKeyHome); + GXMAP(gkey, XKB_KEY_End, GHOST_kKeyEnd); + GXMAP(gkey, XKB_KEY_Page_Up, GHOST_kKeyUpPage); + GXMAP(gkey, XKB_KEY_Page_Down, GHOST_kKeyDownPage); + + GXMAP(gkey, XKB_KEY_KP_Decimal, GHOST_kKeyNumpadPeriod); + GXMAP(gkey, XKB_KEY_KP_Enter, GHOST_kKeyNumpadEnter); + GXMAP(gkey, XKB_KEY_KP_Add, GHOST_kKeyNumpadPlus); + GXMAP(gkey, XKB_KEY_KP_Subtract, GHOST_kKeyNumpadMinus); + GXMAP(gkey, XKB_KEY_KP_Multiply, GHOST_kKeyNumpadAsterisk); + GXMAP(gkey, XKB_KEY_KP_Divide, GHOST_kKeyNumpadSlash); + + GXMAP(gkey, XKB_KEY_XF86AudioPlay, GHOST_kKeyMediaPlay); + GXMAP(gkey, XKB_KEY_XF86AudioStop, GHOST_kKeyMediaStop); + GXMAP(gkey, XKB_KEY_XF86AudioPrev, GHOST_kKeyMediaFirst); + GXMAP(gkey, XKB_KEY_XF86AudioNext, GHOST_kKeyMediaLast); + default: + GHOST_PRINT("unhandled key: " << std::hex << std::showbase << sym << std::dec << " (" + << sym << ")" << std::endl); + gkey = GHOST_kKeyUnknown; + } +#undef GXMAP + } + + return gkey; +} + +static const int default_cursor_size = 24; + +static const std::unordered_map<GHOST_TStandardCursor, std::string> cursors = { + {GHOST_kStandardCursorDefault, "left_ptr"}, + {GHOST_kStandardCursorRightArrow, "right_ptr"}, + {GHOST_kStandardCursorLeftArrow, "left_ptr"}, + {GHOST_kStandardCursorInfo, ""}, + {GHOST_kStandardCursorDestroy, ""}, + {GHOST_kStandardCursorHelp, "question_arrow"}, + {GHOST_kStandardCursorWait, "watch"}, + {GHOST_kStandardCursorText, "xterm"}, + {GHOST_kStandardCursorCrosshair, "crosshair"}, + {GHOST_kStandardCursorCrosshairA, ""}, + {GHOST_kStandardCursorCrosshairB, ""}, + {GHOST_kStandardCursorCrosshairC, ""}, + {GHOST_kStandardCursorPencil, ""}, + {GHOST_kStandardCursorUpArrow, "sb_up_arrow"}, + {GHOST_kStandardCursorDownArrow, "sb_down_arrow"}, + {GHOST_kStandardCursorVerticalSplit, ""}, + {GHOST_kStandardCursorHorizontalSplit, ""}, + {GHOST_kStandardCursorEraser, ""}, + {GHOST_kStandardCursorKnife, ""}, + {GHOST_kStandardCursorEyedropper, ""}, + {GHOST_kStandardCursorZoomIn, ""}, + {GHOST_kStandardCursorZoomOut, ""}, + {GHOST_kStandardCursorMove, "move"}, + {GHOST_kStandardCursorNSEWScroll, ""}, + {GHOST_kStandardCursorNSScroll, ""}, + {GHOST_kStandardCursorEWScroll, ""}, + {GHOST_kStandardCursorStop, ""}, + {GHOST_kStandardCursorUpDown, "sb_v_double_arrow"}, + {GHOST_kStandardCursorLeftRight, "sb_h_double_arrow"}, + {GHOST_kStandardCursorTopSide, "top_side"}, + {GHOST_kStandardCursorBottomSide, "bottom_side"}, + {GHOST_kStandardCursorLeftSide, "left_side"}, + {GHOST_kStandardCursorRightSide, "right_side"}, + {GHOST_kStandardCursorTopLeftCorner, "top_left_corner"}, + {GHOST_kStandardCursorTopRightCorner, "top_right_corner"}, + {GHOST_kStandardCursorBottomRightCorner, "bottom_right_corner"}, + {GHOST_kStandardCursorBottomLeftCorner, "bottom_left_corner"}, + {GHOST_kStandardCursorCopy, "copy"}, +}; + +static constexpr const char *mime_text_plain = "text/plain"; +static constexpr const char *mime_text_utf8 = "text/plain;charset=utf-8"; +static constexpr const char *mime_text_uri = "text/uri-list"; + +static const std::unordered_map<std::string, GHOST_TDragnDropTypes> mime_dnd = { + {mime_text_plain, GHOST_kDragnDropTypeString}, + {mime_text_utf8, GHOST_kDragnDropTypeString}, + {mime_text_uri, GHOST_kDragnDropTypeFilenames}, +}; + +static const std::vector<std::string> mime_preference_order = { + mime_text_uri, + mime_text_utf8, + mime_text_plain, +}; + +static const std::vector<std::string> mime_send = { + "UTF8_STRING", + "COMPOUND_TEXT", + "TEXT", + "STRING", + "text/plain;charset=utf-8", + "text/plain", +}; + +/* -------------------------------------------------------------------- */ +/** \name Interface Callbacks + * + * These callbacks are registered for Wayland interfaces and called when + * an event is received from the compositor. + * \{ */ + +static void relative_pointer_relative_motion( + void *data, + struct zwp_relative_pointer_v1 * /*zwp_relative_pointer_v1*/, + uint32_t /*utime_hi*/, + uint32_t /*utime_lo*/, + wl_fixed_t dx, + wl_fixed_t dy, + wl_fixed_t /*dx_unaccel*/, + wl_fixed_t /*dy_unaccel*/) +{ + input_t *input = static_cast<input_t *>(data); + + input->x += wl_fixed_to_int(dx); + input->y += wl_fixed_to_int(dy); + + GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>( + wl_surface_get_user_data(input->focus_pointer)); + + input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(), + GHOST_kEventCursorMove, + win, + input->x, + input->y, + GHOST_TABLET_DATA_NONE)); +} + +static const zwp_relative_pointer_v1_listener relative_pointer_listener = { + relative_pointer_relative_motion, +}; + +static void dnd_events(const input_t *const input, const GHOST_TEventType event) +{ + const GHOST_TUns64 time = input->system->getMilliSeconds(); + GHOST_IWindow *const window = static_cast<GHOST_WindowWayland *>( + wl_surface_get_user_data(input->focus_pointer)); + for (const std::string &type : mime_preference_order) { + input->system->pushEvent(new GHOST_EventDragnDrop(time, + event, + mime_dnd.at(type), + window, + input->data_offer_dnd->dnd.x, + input->data_offer_dnd->dnd.y, + nullptr)); + } +} + +static std::string read_pipe(data_offer_t *data_offer, const std::string mime_receive) +{ + int pipefd[2]; + pipe(pipefd); + wl_data_offer_receive(data_offer->id, mime_receive.c_str(), pipefd[1]); + close(pipefd[1]); + + std::string data; + ssize_t len; + char buffer[4096]; + while ((len = read(pipefd[0], buffer, sizeof(buffer))) > 0) { + data.insert(data.end(), buffer, buffer + len); + } + close(pipefd[0]); + data_offer->in_use.store(false); + + return data; +} + +/** + * A target accepts an offered mime type. + * + * Sent when a target accepts pointer_focus or motion events. If + * a target does not accept any of the offered types, type is NULL. + */ +static void data_source_target(void * /*data*/, + struct wl_data_source * /*wl_data_source*/, + const char * /*mime_type*/) +{ + /* pass */ +} + +static void data_source_send(void *data, + struct wl_data_source * /*wl_data_source*/, + const char * /*mime_type*/, + int32_t fd) +{ + const char *const buffer = static_cast<char *>(data); + write(fd, buffer, strlen(buffer) + 1); + close(fd); +} + +static void data_source_cancelled(void * /*data*/, struct wl_data_source *wl_data_source) +{ + wl_data_source_destroy(wl_data_source); +} + +/** + * The drag-and-drop operation physically finished. + * + * The user performed the drop action. This event does not + * indicate acceptance, #wl_data_source.cancelled may still be + * emitted afterwards if the drop destination does not accept any mime type. + */ +static void data_source_dnd_drop_performed(void * /*data*/, + struct wl_data_source * /*wl_data_source*/) +{ + /* pass */ +} + +/** + * The drag-and-drop operation concluded. + * + * The drop destination finished interoperating with this data + * source, so the client is now free to destroy this data source + * and free all associated data. + */ +static void data_source_dnd_finished(void * /*data*/, struct wl_data_source * /*wl_data_source*/) +{ + /* pass */ +} + +/** + * Notify the selected action. + * + * This event indicates the action selected by the compositor + * after matching the source/destination side actions. Only one + * action (or none) will be offered here. + */ +static void data_source_action(void * /*data*/, + struct wl_data_source * /*wl_data_source*/, + uint32_t /*dnd_action*/) +{ + /* pass */ +} + +static const struct wl_data_source_listener data_source_listener = { + data_source_target, + data_source_send, + data_source_cancelled, + data_source_dnd_drop_performed, + data_source_dnd_finished, + data_source_action, +}; + +static void data_offer_offer(void *data, + struct wl_data_offer * /*wl_data_offer*/, + const char *mime_type) +{ + static_cast<data_offer_t *>(data)->types.insert(mime_type); +} + +static void data_offer_source_actions(void *data, + struct wl_data_offer * /*wl_data_offer*/, + uint32_t source_actions) +{ + static_cast<data_offer_t *>(data)->source_actions = source_actions; +} + +static void data_offer_action(void *data, + struct wl_data_offer * /*wl_data_offer*/, + uint32_t dnd_action) +{ + static_cast<data_offer_t *>(data)->dnd_action = dnd_action; +} + +static const struct wl_data_offer_listener data_offer_listener = { + data_offer_offer, + data_offer_source_actions, + data_offer_action, +}; + +static void data_device_data_offer(void * /*data*/, + struct wl_data_device * /*wl_data_device*/, + struct wl_data_offer *id) +{ + data_offer_t *data_offer = new data_offer_t; + data_offer->id = id; + wl_data_offer_add_listener(id, &data_offer_listener, data_offer); +} + +static void data_device_enter(void *data, + struct wl_data_device * /*wl_data_device*/, + uint32_t serial, + struct wl_surface * /*surface*/, + wl_fixed_t x, + wl_fixed_t y, + struct wl_data_offer *id) +{ + input_t *input = static_cast<input_t *>(data); + input->data_offer_dnd = static_cast<data_offer_t *>(wl_data_offer_get_user_data(id)); + data_offer_t *data_offer = input->data_offer_dnd; + + data_offer->in_use.store(true); + data_offer->dnd.x = wl_fixed_to_int(x); + data_offer->dnd.y = wl_fixed_to_int(y); + + wl_data_offer_set_actions(id, + WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | + WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE, + WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY); + + for (const std::string &type : mime_preference_order) { + wl_data_offer_accept(id, serial, type.c_str()); + } + + dnd_events(input, GHOST_kEventDraggingEntered); +} + +static void data_device_leave(void *data, struct wl_data_device * /*wl_data_device*/) +{ + input_t *input = static_cast<input_t *>(data); + + dnd_events(input, GHOST_kEventDraggingExited); + + if (input->data_offer_dnd && !input->data_offer_dnd->in_use.load()) { + wl_data_offer_destroy(input->data_offer_dnd->id); + delete input->data_offer_dnd; + input->data_offer_dnd = nullptr; + } +} + +static void data_device_motion(void *data, + struct wl_data_device * /*wl_data_device*/, + uint32_t /*time*/, + wl_fixed_t x, + wl_fixed_t y) +{ + input_t *input = static_cast<input_t *>(data); + input->data_offer_dnd->dnd.x = wl_fixed_to_int(x); + input->data_offer_dnd->dnd.y = wl_fixed_to_int(y); + dnd_events(input, GHOST_kEventDraggingUpdated); +} + +static void data_device_drop(void *data, struct wl_data_device * /*wl_data_device*/) +{ + input_t *input = static_cast<input_t *>(data); + data_offer_t *data_offer = input->data_offer_dnd; + + const std::string mime_receive = *std::find_first_of(mime_preference_order.begin(), + mime_preference_order.end(), + data_offer->types.begin(), + data_offer->types.end()); + + auto read_uris = [](input_t *const input, + data_offer_t *data_offer, + const std::string mime_receive) { + const int x = data_offer->dnd.x; + const int y = data_offer->dnd.y; + + const std::string data = read_pipe(data_offer, mime_receive); + + wl_data_offer_finish(data_offer->id); + wl_data_offer_destroy(data_offer->id); + + delete data_offer; + data_offer = nullptr; + + GHOST_SystemWayland *const system = input->system; + + if (mime_receive == mime_text_uri) { + static constexpr const char *file_proto = "file://"; + static constexpr const char *crlf = "\r\n"; + + std::vector<std::string> uris; + + size_t pos = 0; + while (true) { + pos = data.find(file_proto, pos); + const size_t start = pos + sizeof(file_proto) - 1; + pos = data.find(crlf, pos); + const size_t end = pos; + + if (pos == std::string::npos) { + break; + } + uris.push_back(data.substr(start, end - start)); + } + + GHOST_TStringArray *flist = static_cast<GHOST_TStringArray *>( + malloc(sizeof(GHOST_TStringArray))); + flist->count = int(uris.size()); + flist->strings = static_cast<GHOST_TUns8 **>(malloc(uris.size() * sizeof(GHOST_TUns8 *))); + for (size_t i = 0; i < uris.size(); i++) { + flist->strings[i] = static_cast<GHOST_TUns8 *>( + malloc((uris[i].size() + 1) * sizeof(GHOST_TUns8))); + memcpy(flist->strings[i], uris[i].data(), uris[i].size() + 1); + } + GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>( + wl_surface_get_user_data(input->focus_pointer)); + system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(), + GHOST_kEventDraggingDropDone, + GHOST_kDragnDropTypeFilenames, + win, + x, + y, + flist)); + } + else if (mime_receive == mime_text_plain || mime_receive == mime_text_utf8) { + /* TODO: enable use of internal functions 'txt_insert_buf' and + * 'text_update_edited' to behave like dropped text was pasted. */ + } + wl_display_roundtrip(system->display()); + }; + + std::thread read_thread(read_uris, input, data_offer, mime_receive); + read_thread.detach(); +} + +static void data_device_selection(void *data, + struct wl_data_device * /*wl_data_device*/, + struct wl_data_offer *id) +{ + input_t *input = static_cast<input_t *>(data); + data_offer_t *data_offer = input->data_offer_copy_paste; + + /* Delete old data offer. */ + if (data_offer != nullptr) { + wl_data_offer_destroy(data_offer->id); + delete data_offer; + data_offer = nullptr; + } + + if (id == nullptr) { + return; + } + + /* Get new data offer. */ + data_offer = static_cast<data_offer_t *>(wl_data_offer_get_user_data(id)); + input->data_offer_copy_paste = data_offer; + + std::string mime_receive; + for (const std::string &type : {mime_text_utf8, mime_text_plain}) { + if (data_offer->types.count(type)) { + mime_receive = type; + break; + } + } + + auto read_selection = [](GHOST_SystemWayland *const system, + data_offer_t *data_offer, + const std::string mime_receive) { + const std::string data = read_pipe(data_offer, mime_receive); + system->setSelection(data); + }; + + std::thread read_thread(read_selection, input->system, data_offer, mime_receive); + read_thread.detach(); +} + +static const struct wl_data_device_listener data_device_listener = { + data_device_data_offer, + data_device_enter, + data_device_leave, + data_device_motion, + data_device_drop, + data_device_selection, +}; + +static void cursor_buffer_release(void *data, struct wl_buffer *wl_buffer) +{ + cursor_t *cursor = static_cast<cursor_t *>(data); + + wl_buffer_destroy(wl_buffer); + cursor->buffer = nullptr; +} + +const struct wl_buffer_listener cursor_buffer_listener = { + cursor_buffer_release, +}; + +static void pointer_enter(void *data, + struct wl_pointer * /*wl_pointer*/, + uint32_t serial, + struct wl_surface *surface, + wl_fixed_t surface_x, + wl_fixed_t surface_y) +{ + if (!surface) { + return; + } + input_t *input = static_cast<input_t *>(data); + input->pointer_serial = serial; + input->x = wl_fixed_to_int(surface_x); + input->y = wl_fixed_to_int(surface_y); + input->focus_pointer = surface; + + input->system->pushEvent( + new GHOST_EventCursor(input->system->getMilliSeconds(), + GHOST_kEventCursorMove, + static_cast<GHOST_WindowWayland *>(wl_surface_get_user_data(surface)), + input->x, + input->y, + GHOST_TABLET_DATA_NONE)); +} + +static void pointer_leave(void *data, + struct wl_pointer * /*wl_pointer*/, + uint32_t /*serial*/, + struct wl_surface *surface) +{ + if (surface != nullptr) { + static_cast<input_t *>(data)->focus_pointer = nullptr; + } +} + +static void pointer_motion(void *data, + struct wl_pointer * /*wl_pointer*/, + uint32_t /*time*/, + wl_fixed_t surface_x, + wl_fixed_t surface_y) +{ + input_t *input = static_cast<input_t *>(data); + + GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>( + wl_surface_get_user_data(input->focus_pointer)); + + if (!win) { + return; + } + + input->x = wl_fixed_to_int(surface_x); + input->y = wl_fixed_to_int(surface_y); + + input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(), + GHOST_kEventCursorMove, + win, + wl_fixed_to_int(surface_x), + wl_fixed_to_int(surface_y), + GHOST_TABLET_DATA_NONE)); +} + +static void pointer_button(void *data, + struct wl_pointer * /*wl_pointer*/, + uint32_t serial, + uint32_t /*time*/, + uint32_t button, + uint32_t state) +{ + GHOST_TEventType etype = GHOST_kEventUnknown; + switch (state) { + case WL_POINTER_BUTTON_STATE_RELEASED: + etype = GHOST_kEventButtonUp; + break; + case WL_POINTER_BUTTON_STATE_PRESSED: + etype = GHOST_kEventButtonDown; + break; + } + + GHOST_TButtonMask ebutton = GHOST_kButtonMaskLeft; + switch (button) { + case BTN_LEFT: + ebutton = GHOST_kButtonMaskLeft; + break; + case BTN_MIDDLE: + ebutton = GHOST_kButtonMaskMiddle; + break; + case BTN_RIGHT: + ebutton = GHOST_kButtonMaskRight; + break; + } + + input_t *input = static_cast<input_t *>(data); + GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>( + wl_surface_get_user_data(input->focus_pointer)); + input->data_source->source_serial = serial; + input->buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED); + input->system->pushEvent(new GHOST_EventButton( + input->system->getMilliSeconds(), etype, win, ebutton, GHOST_TABLET_DATA_NONE)); +} + +static void pointer_axis(void *data, + struct wl_pointer * /*wl_pointer*/, + uint32_t /*time*/, + uint32_t axis, + wl_fixed_t value) +{ + if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) { + return; + } + input_t *input = static_cast<input_t *>(data); + GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>( + wl_surface_get_user_data(input->focus_pointer)); + input->system->pushEvent( + new GHOST_EventWheel(input->system->getMilliSeconds(), win, std::signbit(value) ? +1 : -1)); +} + +static const struct wl_pointer_listener pointer_listener = { + pointer_enter, + pointer_leave, + pointer_motion, + pointer_button, + pointer_axis, +}; + +static void keyboard_keymap( + void *data, struct wl_keyboard * /*wl_keyboard*/, uint32_t format, int32_t fd, uint32_t size) +{ + input_t *input = static_cast<input_t *>(data); + + if ((!data) || (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)) { + close(fd); + return; + } + + char *map_str = static_cast<char *>(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0)); + if (map_str == MAP_FAILED) { + close(fd); + throw std::runtime_error("keymap mmap failed: " + std::string(std::strerror(errno))); + } + + struct xkb_keymap *keymap = xkb_keymap_new_from_string( + input->xkb_context, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); + munmap(map_str, size); + close(fd); + + if (!keymap) { + return; + } + + input->xkb_state = xkb_state_new(keymap); + + xkb_keymap_unref(keymap); +} + +/** + * Enter event. + * + * Notification that this seat's keyboard focus is on a certain + * surface. + */ +static void keyboard_enter(void *data, + struct wl_keyboard * /*wl_keyboard*/, + uint32_t /*serial*/, + struct wl_surface *surface, + struct wl_array * /*keys*/) +{ + if (surface != nullptr) { + static_cast<input_t *>(data)->focus_keyboard = surface; + } +} + +/** + * Leave event. + * + * Notification that this seat's keyboard focus is no longer on a + * certain surface. + */ +static void keyboard_leave(void *data, + struct wl_keyboard * /*wl_keyboard*/, + uint32_t /*serial*/, + struct wl_surface *surface) +{ + if (surface != nullptr) { + static_cast<input_t *>(data)->focus_keyboard = nullptr; + } +} + +/** + * A version of #xkb_state_key_get_one_sym which returns the key without any modifiers pressed. + * Needed because #GHOST_TKey uses these values as key-codes. + */ +static xkb_keysym_t xkb_state_key_get_one_sym_without_modifiers(struct xkb_state *xkb_state, + xkb_keycode_t key) +{ + /* Use an empty keyboard state to access key symbol without modifiers. */ + xkb_state_get_keymap(xkb_state); + struct xkb_keymap *keymap = xkb_state_get_keymap(xkb_state); + struct xkb_state *xkb_state_empty = xkb_state_new(keymap); + + /* Enable number-lock. */ + { + const xkb_mod_index_t mod2 = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_NUM); + const xkb_mod_index_t num = xkb_keymap_mod_get_index(keymap, "NumLock"); + if (num != XKB_MOD_INVALID && mod2 != XKB_MOD_INVALID) { + xkb_state_update_mask(xkb_state_empty, (1 << mod2), 0, (1 << num), 0, 0, 0); + } + } + + const xkb_keysym_t sym = xkb_state_key_get_one_sym(xkb_state_empty, key); + xkb_state_unref(xkb_state_empty); + return sym; +} + +static void keyboard_key(void *data, + struct wl_keyboard * /*wl_keyboard*/, + uint32_t serial, + uint32_t /*time*/, + uint32_t key, + uint32_t state) +{ + input_t *input = static_cast<input_t *>(data); + + GHOST_TEventType etype = GHOST_kEventUnknown; + switch (state) { + case WL_KEYBOARD_KEY_STATE_RELEASED: + etype = GHOST_kEventKeyUp; + break; + case WL_KEYBOARD_KEY_STATE_PRESSED: + etype = GHOST_kEventKeyDown; + break; + } + + const xkb_keysym_t sym = xkb_state_key_get_one_sym_without_modifiers(input->xkb_state, key + 8); + + if (sym == XKB_KEY_NoSymbol) { + return; + } + const GHOST_TKey gkey = xkb_map_gkey(sym); + + /* Delete previous timer. */ + if (xkb_keymap_key_repeats(xkb_state_get_keymap(input->xkb_state), key + 8) && + input->key_repeat.timer) { + delete static_cast<key_repeat_payload_t *>(input->key_repeat.timer->getUserData()); + input->system->removeTimer(input->key_repeat.timer); + input->key_repeat.timer = nullptr; + } + + GHOST_TEventKeyData key_data; + + if (etype == GHOST_kEventKeyDown) { + xkb_state_key_get_utf8( + input->xkb_state, key + 8, key_data.utf8_buf, sizeof(GHOST_TEventKeyData::utf8_buf)); + } + else { + key_data.utf8_buf[0] = '\0'; + } + + input->data_source->source_serial = serial; + + GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>( + wl_surface_get_user_data(input->focus_keyboard)); + input->system->pushEvent(new GHOST_EventKey( + input->system->getMilliSeconds(), etype, win, gkey, '\0', key_data.utf8_buf, false)); + + /* Start timer for repeating key, if applicable. */ + if (input->key_repeat.rate > 0 && + xkb_keymap_key_repeats(xkb_state_get_keymap(input->xkb_state), key + 8) && + etype == GHOST_kEventKeyDown) { + + key_repeat_payload_t *payload = new key_repeat_payload_t({ + .system = input->system, + .window = win, + .key = gkey, + .key_data = key_data, + }); + + auto cb = [](GHOST_ITimerTask *task, GHOST_TUns64 /*time*/) { + struct key_repeat_payload_t *payload = static_cast<key_repeat_payload_t *>( + task->getUserData()); + payload->system->pushEvent(new GHOST_EventKey(payload->system->getMilliSeconds(), + GHOST_kEventKeyDown, + payload->window, + payload->key, + '\0', + payload->key_data.utf8_buf, + true)); + }; + input->key_repeat.timer = input->system->installTimer( + input->key_repeat.delay, 1000 / input->key_repeat.rate, cb, payload); + } +} + +static void keyboard_modifiers(void *data, + struct wl_keyboard * /*wl_keyboard*/, + uint32_t /*serial*/, + uint32_t mods_depressed, + uint32_t mods_latched, + uint32_t mods_locked, + uint32_t group) +{ + xkb_state_update_mask(static_cast<input_t *>(data)->xkb_state, + mods_depressed, + mods_latched, + mods_locked, + 0, + 0, + group); +} + +static void keyboard_repeat_info(void *data, + struct wl_keyboard * /*wl_keyboard*/, + int32_t rate, + int32_t delay) +{ + input_t *input = static_cast<input_t *>(data); + + input->key_repeat.rate = rate; + input->key_repeat.delay = delay; +} + +static const struct wl_keyboard_listener keyboard_listener = { + keyboard_keymap, + keyboard_enter, + keyboard_leave, + keyboard_key, + keyboard_modifiers, + keyboard_repeat_info, +}; + +static void seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) +{ + input_t *input = static_cast<input_t *>(data); + input->pointer = nullptr; + input->keyboard = nullptr; + + if (capabilities & WL_SEAT_CAPABILITY_POINTER) { + input->pointer = wl_seat_get_pointer(wl_seat); + input->cursor.surface = wl_compositor_create_surface(input->system->compositor()); + input->cursor.visible = true; + input->cursor.buffer = nullptr; + input->cursor.file_buffer = new buffer_t; + wl_pointer_add_listener(input->pointer, &pointer_listener, data); + } + + if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { + input->keyboard = wl_seat_get_keyboard(wl_seat); + wl_keyboard_add_listener(input->keyboard, &keyboard_listener, data); + } +} + +static void seat_name(void *data, struct wl_seat * /*wl_seat*/, const char *name) +{ + static_cast<input_t *>(data)->name = std::string(name); +} + +static const struct wl_seat_listener seat_listener = { + seat_capabilities, + seat_name, +}; + +static void output_geometry(void *data, + struct wl_output * /*wl_output*/, + int32_t /*x*/, + int32_t /*y*/, + int32_t /*physical_width*/, + int32_t /*physical_height*/, + int32_t /*subpixel*/, + const char *make, + const char *model, + int32_t transform) +{ + output_t *output = static_cast<output_t *>(data); + output->transform = transform; + output->make = std::string(make); + output->model = std::string(model); +} + +static void output_mode(void *data, + struct wl_output * /*wl_output*/, + uint32_t /*flags*/, + int32_t width, + int32_t height, + int32_t /*refresh*/) +{ + output_t *output = static_cast<output_t *>(data); + output->width = width; + output->height = height; +} + +/** + * Sent all information about output. + * + * This event is sent after all other properties have been sent + * after binding to the output object and after any other property + * changes done after that. This allows changes to the output + * properties to be seen as atomic, even if they happen via multiple events. + */ +static void output_done(void * /*data*/, struct wl_output * /*wl_output*/) +{ +} + +static void output_scale(void *data, struct wl_output * /*wl_output*/, int32_t factor) +{ + static_cast<output_t *>(data)->scale = factor; +} + +static const struct wl_output_listener output_listener = { + output_geometry, + output_mode, + output_done, + output_scale, +}; + +static void shell_ping(void * /*data*/, struct xdg_wm_base *xdg_wm_base, uint32_t serial) +{ + xdg_wm_base_pong(xdg_wm_base, serial); +} + +static const struct xdg_wm_base_listener shell_listener = { + shell_ping, +}; + +static void global_add(void *data, + struct wl_registry *wl_registry, + uint32_t name, + const char *interface, + uint32_t /*version*/) +{ + struct display_t *display = static_cast<struct display_t *>(data); + if (!strcmp(interface, wl_compositor_interface.name)) { + display->compositor = static_cast<wl_compositor *>( + wl_registry_bind(wl_registry, name, &wl_compositor_interface, 1)); + } + else if (!strcmp(interface, xdg_wm_base_interface.name)) { + display->xdg_shell = static_cast<xdg_wm_base *>( + wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, 1)); + xdg_wm_base_add_listener(display->xdg_shell, &shell_listener, nullptr); + } + else if (!strcmp(interface, wl_output_interface.name)) { + output_t *output = new output_t; + output->scale = 1; + output->output = static_cast<wl_output *>( + wl_registry_bind(wl_registry, name, &wl_output_interface, 2)); + display->outputs.push_back(output); + wl_output_add_listener(output->output, &output_listener, output); + } + else if (!strcmp(interface, wl_seat_interface.name)) { + input_t *input = new input_t; + input->system = display->system; + input->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + input->xkb_state = nullptr; + input->data_offer_dnd = nullptr; + input->data_offer_copy_paste = nullptr; + input->data_source = new data_source_t; + input->data_source->data_source = nullptr; + input->data_source->buffer_out = nullptr; + input->relative_pointer = nullptr; + input->locked_pointer = nullptr; + input->seat = static_cast<wl_seat *>( + wl_registry_bind(wl_registry, name, &wl_seat_interface, 4)); + display->inputs.push_back(input); + wl_seat_add_listener(input->seat, &seat_listener, input); + } + else if (!strcmp(interface, wl_shm_interface.name)) { + display->shm = static_cast<wl_shm *>( + wl_registry_bind(wl_registry, name, &wl_shm_interface, 1)); + } + else if (!strcmp(interface, wl_data_device_manager_interface.name)) { + display->data_device_manager = static_cast<wl_data_device_manager *>( + wl_registry_bind(wl_registry, name, &wl_data_device_manager_interface, 1)); + } + else if (!strcmp(interface, zwp_relative_pointer_manager_v1_interface.name)) { + display->relative_pointer_manager = static_cast<zwp_relative_pointer_manager_v1 *>( + wl_registry_bind(wl_registry, name, &zwp_relative_pointer_manager_v1_interface, 1)); + } + else if (!strcmp(interface, zwp_pointer_constraints_v1_interface.name)) { + display->pointer_constraints = static_cast<zwp_pointer_constraints_v1 *>( + wl_registry_bind(wl_registry, name, &zwp_pointer_constraints_v1_interface, 1)); + } +} + +/** + * Announce removal of global object. + * + * Notify the client of removed global objects. + * + * This event notifies the client that the global identified by + * name is no longer available. If the client bound to the global + * using the bind request, the client should now destroy that object. + */ +static void global_remove(void * /*data*/, struct wl_registry * /*wl_registry*/, uint32_t /*name*/) +{ +} + +static const struct wl_registry_listener registry_listener = { + global_add, + global_remove, +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Ghost Implementation + * + * Wayland specific implementation of the GHOST_System interface. + * \{ */ + +GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), d(new display_t) +{ + d->system = this; + /* Connect to the Wayland server. */ + d->display = wl_display_connect(nullptr); + if (!d->display) { + display_destroy(d); + throw std::runtime_error("Wayland: unable to connect to display!"); + } + + /* Register interfaces. */ + struct wl_registry *registry = wl_display_get_registry(d->display); + wl_registry_add_listener(registry, ®istry_listener, d); + /* Call callback for registry listener. */ + wl_display_roundtrip(d->display); + /* Call callbacks for registered listeners. */ + wl_display_roundtrip(d->display); + wl_registry_destroy(registry); + + if (!d->xdg_shell) { + display_destroy(d); + throw std::runtime_error("Wayland: unable to access xdg_shell!"); + } + + /* Register data device per seat for IPC between Wayland clients. */ + if (d->data_device_manager) { + for (input_t *input : d->inputs) { + input->data_device = wl_data_device_manager_get_data_device(d->data_device_manager, + input->seat); + wl_data_device_add_listener(input->data_device, &data_device_listener, input); + } + } + + const char *theme = std::getenv("XCURSOR_THEME"); + const char *size = std::getenv("XCURSOR_SIZE"); + const int sizei = size ? std::stoi(size) : default_cursor_size; + + d->cursor_theme = wl_cursor_theme_load(theme, sizei, d->shm); + if (!d->cursor_theme) { + display_destroy(d); + throw std::runtime_error("Wayland: unable to access cursor themes!"); + } +} + +GHOST_SystemWayland::~GHOST_SystemWayland() +{ + display_destroy(d); +} + +bool GHOST_SystemWayland::processEvents(bool waitForEvent) +{ + const bool fired = getTimerManager()->fireTimers(getMilliSeconds()); + + if (waitForEvent) { + wl_display_dispatch(d->display); + } + else { + wl_display_roundtrip(d->display); + } + + return fired || (getEventManager()->getNumEvents() > 0); +} + +int GHOST_SystemWayland::toggleConsole(int /*action*/) +{ + return 0; +} + +GHOST_TSuccess GHOST_SystemWayland::getModifierKeys(GHOST_ModifierKeys &keys) const +{ + if (!d->inputs.empty()) { + static const xkb_state_component mods_all = xkb_state_component( + XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED | XKB_STATE_MODS_LOCKED | + XKB_STATE_MODS_EFFECTIVE); + + keys.set(GHOST_kModifierKeyLeftShift, + xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_SHIFT, mods_all) == + 1); + keys.set(GHOST_kModifierKeyRightShift, + xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_SHIFT, mods_all) == + 1); + keys.set(GHOST_kModifierKeyLeftAlt, + xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "LAlt", mods_all) == 1); + keys.set(GHOST_kModifierKeyRightAlt, + xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "RAlt", mods_all) == 1); + keys.set(GHOST_kModifierKeyLeftControl, + xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "LControl", mods_all) == 1); + keys.set(GHOST_kModifierKeyRightControl, + xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "RControl", mods_all) == 1); + keys.set(GHOST_kModifierKeyOS, + xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "Super", mods_all) == 1); + keys.set(GHOST_kModifierKeyNumMasks, + xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "NumLock", mods_all) == 1); + + return GHOST_kSuccess; + } + return GHOST_kFailure; +} + +GHOST_TSuccess GHOST_SystemWayland::getButtons(GHOST_Buttons &buttons) const +{ + if (!d->inputs.empty()) { + buttons = d->inputs[0]->buttons; + return GHOST_kSuccess; + } + return GHOST_kFailure; +} + +GHOST_TUns8 *GHOST_SystemWayland::getClipboard(bool /*selection*/) const +{ + GHOST_TUns8 *clipboard = static_cast<GHOST_TUns8 *>(malloc((selection.size() + 1))); + memcpy(clipboard, selection.data(), selection.size() + 1); + return clipboard; +} + +void GHOST_SystemWayland::putClipboard(GHOST_TInt8 *buffer, bool /*selection*/) const +{ + if (!d->data_device_manager || d->inputs.empty()) { + return; + } + + data_source_t *data_source = d->inputs[0]->data_source; + + /* Copy buffer. */ + data_source->buffer_out = static_cast<char *>(malloc(strlen(buffer) + 1)); + std::strcpy(data_source->buffer_out, buffer); + + data_source->data_source = wl_data_device_manager_create_data_source(d->data_device_manager); + + wl_data_source_add_listener( + data_source->data_source, &data_source_listener, data_source->buffer_out); + + for (const std::string &type : mime_send) { + wl_data_source_offer(data_source->data_source, type.c_str()); + } + + if (!d->inputs.empty() && d->inputs[0]->data_device) { + wl_data_device_set_selection( + d->inputs[0]->data_device, data_source->data_source, data_source->source_serial); + } +} + +GHOST_TUns8 GHOST_SystemWayland::getNumDisplays() const +{ + return d ? GHOST_TUns8(d->outputs.size()) : 0; +} + +GHOST_TSuccess GHOST_SystemWayland::getCursorPosition(GHOST_TInt32 &x, GHOST_TInt32 &y) const +{ + if (!d->inputs.empty() && (d->inputs[0]->focus_pointer != nullptr)) { + x = d->inputs[0]->x; + y = d->inputs[0]->y; + return GHOST_kSuccess; + } + else { + return GHOST_kFailure; + } +} + +GHOST_TSuccess GHOST_SystemWayland::setCursorPosition(GHOST_TInt32 /*x*/, GHOST_TInt32 /*y*/) +{ + return GHOST_kFailure; +} + +void GHOST_SystemWayland::getMainDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const +{ + if (getNumDisplays() > 0) { + /* We assume first output as main. */ + width = uint32_t(d->outputs[0]->width); + height = uint32_t(d->outputs[0]->height); + } +} + +void GHOST_SystemWayland::getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const +{ + getMainDisplayDimensions(width, height); +} + +GHOST_IContext *GHOST_SystemWayland::createOffscreenContext() +{ + /* Create new off-screen window. */ + wl_surface *os_surface = wl_compositor_create_surface(compositor()); + wl_egl_window *os_egl_window = wl_egl_window_create(os_surface, int(1), int(1)); + + d->os_surfaces.push_back(os_surface); + d->os_egl_windows.push_back(os_egl_window); + + GHOST_Context *context = new GHOST_ContextEGL(false, + EGLNativeWindowType(os_egl_window), + EGLNativeDisplayType(d->display), + EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + 3, + 3, + GHOST_OPENGL_EGL_CONTEXT_FLAGS, + GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY, + EGL_OPENGL_API); + + if (context->initializeDrawingContext()) { + return context; + } + else { + delete context; + } + + GHOST_PRINT("Cannot create off-screen EGL context" << std::endl); + + return nullptr; +} + +GHOST_TSuccess GHOST_SystemWayland::disposeContext(GHOST_IContext *context) +{ + delete context; + return GHOST_kSuccess; +} + +GHOST_IWindow *GHOST_SystemWayland::createWindow(const char *title, + GHOST_TInt32 left, + GHOST_TInt32 top, + GHOST_TUns32 width, + GHOST_TUns32 height, + GHOST_TWindowState state, + GHOST_TDrawingContextType type, + GHOST_GLSettings glSettings, + const bool exclusive, + const bool is_dialog, + const GHOST_IWindow *parentWindow) +{ + GHOST_WindowWayland *window = new GHOST_WindowWayland( + this, + title, + left, + top, + width, + height, + state, + parentWindow, + type, + is_dialog, + ((glSettings.flags & GHOST_glStereoVisual) != 0), + exclusive); + + if (window) { + if (window->getValid()) { + m_windowManager->addWindow(window); + m_windowManager->setActiveWindow(window); + pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window)); + } + else { + delete window; + window = nullptr; + } + } + + return window; +} + +wl_display *GHOST_SystemWayland::display() +{ + return d->display; +} + +wl_compositor *GHOST_SystemWayland::compositor() +{ + return d->compositor; +} + +xdg_wm_base *GHOST_SystemWayland::shell() +{ + return d->xdg_shell; +} + +void GHOST_SystemWayland::setSelection(const std::string &selection) +{ + this->selection = selection; +} + +static void set_cursor_buffer(input_t *input, wl_buffer *buffer) +{ + input->cursor.visible = (buffer != nullptr); + + wl_surface_attach(input->cursor.surface, buffer, 0, 0); + wl_surface_commit(input->cursor.surface); + + if (input->cursor.visible) { + wl_surface_damage(input->cursor.surface, + 0, + 0, + int32_t(input->cursor.image.width), + int32_t(input->cursor.image.height)); + wl_pointer_set_cursor(input->pointer, + input->pointer_serial, + input->cursor.surface, + int32_t(input->cursor.image.hotspot_x), + int32_t(input->cursor.image.hotspot_y)); + } +} + +GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape) +{ + if (d->inputs.empty()) { + return GHOST_kFailure; + } + const std::string cursor_name = cursors.count(shape) ? cursors.at(shape) : + cursors.at(GHOST_kStandardCursorDefault); + + wl_cursor *cursor = wl_cursor_theme_get_cursor(d->cursor_theme, cursor_name.c_str()); + + if (!cursor) { + GHOST_PRINT("cursor '" << cursor_name << "' does not exist" << std::endl); + return GHOST_kFailure; + } + + struct wl_cursor_image *image = cursor->images[0]; + struct wl_buffer *buffer = wl_cursor_image_get_buffer(image); + if (!buffer) { + return GHOST_kFailure; + } + cursor_t *c = &d->inputs[0]->cursor; + c->buffer = buffer; + c->image = *image; + + set_cursor_buffer(d->inputs[0], buffer); + + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_SystemWayland::hasCursorShape(GHOST_TStandardCursor cursorShape) +{ + return GHOST_TSuccess(cursors.count(cursorShape) && !cursors.at(cursorShape).empty()); +} + +GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(GHOST_TUns8 *bitmap, + GHOST_TUns8 *mask, + int sizex, + int sizey, + int hotX, + int hotY, + bool /*canInvertColor*/) +{ + if (d->inputs.empty()) { + return GHOST_kFailure; + } + + cursor_t *cursor = &d->inputs[0]->cursor; + + static const int32_t stride = sizex * 4; /* ARGB */ + cursor->file_buffer->size = size_t(stride * sizey); + + const int fd = memfd_create("blender-cursor-custom", MFD_CLOEXEC | MFD_ALLOW_SEALING); + fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK); + posix_fallocate(fd, 0, int32_t(cursor->file_buffer->size)); + + cursor->file_buffer->data = mmap( + nullptr, cursor->file_buffer->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + + struct wl_shm_pool *pool = wl_shm_create_pool(d->shm, fd, int32_t(cursor->file_buffer->size)); + + wl_buffer *buffer = wl_shm_pool_create_buffer( + pool, 0, sizex, sizey, stride, WL_SHM_FORMAT_ARGB8888); + + wl_shm_pool_destroy(pool); + close(fd); + + wl_buffer_add_listener(buffer, &cursor_buffer_listener, cursor); + + static constexpr uint32_t black = 0xFF000000; + static constexpr uint32_t white = 0xFFFFFFFF; + static constexpr uint32_t transparent = 0x00000000; + + uint8_t datab = 0, maskb = 0; + uint32_t *pixel; + + for (int y = 0; y < sizey; ++y) { + pixel = &static_cast<uint32_t *>(cursor->file_buffer->data)[y * sizex]; + for (int x = 0; x < sizex; ++x) { + if ((x % 8) == 0) { + datab = *bitmap++; + maskb = *mask++; + + /* Reverse bit order. */ + datab = uint8_t((datab * 0x0202020202ULL & 0x010884422010ULL) % 1023); + maskb = uint8_t((maskb * 0x0202020202ULL & 0x010884422010ULL) % 1023); + } + + if (maskb & 0x80) { + *pixel++ = (datab & 0x80) ? white : black; + } + else { + *pixel++ = (datab & 0x80) ? white : transparent; + } + datab <<= 1; + maskb <<= 1; + } + } + + cursor->buffer = buffer; + cursor->image.width = uint32_t(sizex); + cursor->image.height = uint32_t(sizey); + cursor->image.hotspot_x = uint32_t(hotX); + cursor->image.hotspot_y = uint32_t(hotY); + + set_cursor_buffer(d->inputs[0], buffer); + + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_SystemWayland::setCursorVisibility(bool visible) +{ + if (d->inputs.empty()) { + return GHOST_kFailure; + } + + input_t *input = d->inputs[0]; + + cursor_t *cursor = &input->cursor; + if (visible) { + if (!cursor->visible) { + set_cursor_buffer(input, cursor->buffer); + } + } + else { + if (cursor->visible) { + set_cursor_buffer(input, nullptr); + } + } + + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mode, + wl_surface *surface) +{ + if (d->inputs.empty()) { + return GHOST_kFailure; + } + + input_t *input = d->inputs[0]; + + switch (mode) { + case GHOST_kGrabDisable: + if (input->relative_pointer) { + zwp_relative_pointer_v1_destroy(input->relative_pointer); + input->relative_pointer = nullptr; + } + if (input->locked_pointer) { + zwp_locked_pointer_v1_destroy(input->locked_pointer); + input->locked_pointer = nullptr; + } + break; + + case GHOST_kGrabNormal: + case GHOST_kGrabWrap: + input->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer( + d->relative_pointer_manager, input->pointer); + zwp_relative_pointer_v1_add_listener( + input->relative_pointer, &relative_pointer_listener, input); + input->locked_pointer = zwp_pointer_constraints_v1_lock_pointer( + d->pointer_constraints, + surface, + input->pointer, + nullptr, + ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); + break; + + case GHOST_kGrabHide: + setCursorVisibility(false); + break; + } + + return GHOST_kSuccess; +} + +/** \} */ diff --git a/intern/ghost/intern/GHOST_SystemWayland.h b/intern/ghost/intern/GHOST_SystemWayland.h new file mode 100644 index 00000000000..89cd3406b69 --- /dev/null +++ b/intern/ghost/intern/GHOST_SystemWayland.h @@ -0,0 +1,111 @@ +/* + * 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. + */ + +/** \file + * \ingroup GHOST + * Declaration of GHOST_SystemWayland class. + */ + +#ifndef __GHOST_SYSTEMWAYLAND_H__ +#define __GHOST_SYSTEMWAYLAND_H__ + +#include "../GHOST_Types.h" +#include "GHOST_System.h" +#include "GHOST_WindowWayland.h" + +#include <wayland-client.h> +#include <xdg-shell-client-protocol.h> + +#include <string> + +class GHOST_WindowWayland; + +struct display_t; + +class GHOST_SystemWayland : public GHOST_System { + public: + GHOST_SystemWayland(); + + ~GHOST_SystemWayland() override; + + bool processEvents(bool waitForEvent) override; + + int toggleConsole(int action) override; + + GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const override; + + GHOST_TSuccess getButtons(GHOST_Buttons &buttons) const override; + + GHOST_TUns8 *getClipboard(bool selection) const override; + + void putClipboard(GHOST_TInt8 *buffer, bool selection) const override; + + GHOST_TUns8 getNumDisplays() const override; + + GHOST_TSuccess getCursorPosition(GHOST_TInt32 &x, GHOST_TInt32 &y) const override; + + GHOST_TSuccess setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y) override; + + void getMainDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const override; + + void getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const override; + + GHOST_IContext *createOffscreenContext() override; + + GHOST_TSuccess disposeContext(GHOST_IContext *context) override; + + GHOST_IWindow *createWindow(const char *title, + GHOST_TInt32 left, + GHOST_TInt32 top, + GHOST_TUns32 width, + GHOST_TUns32 height, + GHOST_TWindowState state, + GHOST_TDrawingContextType type, + GHOST_GLSettings glSettings, + const bool exclusive, + const bool is_dialog, + const GHOST_IWindow *parentWindow) override; + + wl_display *display(); + + wl_compositor *compositor(); + + xdg_wm_base *shell(); + + void setSelection(const std::string &selection); + + GHOST_TSuccess setCursorShape(GHOST_TStandardCursor shape); + + GHOST_TSuccess hasCursorShape(GHOST_TStandardCursor cursorShape); + + GHOST_TSuccess setCustomCursorShape(GHOST_TUns8 *bitmap, + GHOST_TUns8 *mask, + int sizex, + int sizey, + int hotX, + int hotY, + bool canInvertColor); + + GHOST_TSuccess setCursorVisibility(bool visible); + + GHOST_TSuccess setCursorGrab(const GHOST_TGrabCursorMode mode, wl_surface *surface); + + private: + struct display_t *d; + std::string selection; +}; + +#endif /* __GHOST_SYSTEMWAYLAND_H__ */ diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index e31186bd6a5..849aa5a96f5 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -115,12 +115,22 @@ # define WM_DPICHANGED 0x02E0 #endif // WM_DPICHANGED +// WM_POINTER API messages minimum Windows 7 +#ifndef WM_POINTERENTER +# define WM_POINTERENTER 0x0249 +#endif // WM_POINTERENTER +#ifndef WM_POINTERDOWN +# define WM_POINTERDOWN 0x0246 +#endif // WM_POINTERDOWN #ifndef WM_POINTERUPDATE # define WM_POINTERUPDATE 0x0245 #endif // WM_POINTERUPDATE - -#define WM_POINTERDOWN 0x0246 -#define WM_POINTERUP 0x0247 +#ifndef WM_POINTERUP +# define WM_POINTERUP 0x0247 +#endif // WM_POINTERUP +#ifndef WM_POINTERLEAVE +# define WM_POINTERLEAVE 0x024A +#endif // WM_POINTERLEAVE /* Workaround for some laptop touchpads, some of which seems to * have driver issues which makes it so window function receives @@ -184,7 +194,8 @@ typedef enum MONITOR_DPI_TYPE { typedef HRESULT(API *GHOST_WIN32_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); typedef BOOL(API *GHOST_WIN32_EnableNonClientDpiScaling)(HWND); -GHOST_SystemWin32::GHOST_SystemWin32() : m_hasPerformanceCounter(false), m_freq(0), m_start(0) +GHOST_SystemWin32::GHOST_SystemWin32() + : m_hasPerformanceCounter(false), m_freq(0), m_start(0), m_lfstart(0) { m_displayManager = new GHOST_DisplayManagerWin32(); GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::GHOST_SystemWin32(): m_displayManager==0\n"); @@ -223,22 +234,32 @@ GHOST_SystemWin32::~GHOST_SystemWin32() toggleConsole(1); } +GHOST_TUns64 GHOST_SystemWin32::performanceCounterToMillis(__int64 perf_ticks) const +{ + // Calculate the time passed since system initialization. + __int64 delta = (perf_ticks - m_start) * 1000; + + GHOST_TUns64 t = (GHOST_TUns64)(delta / m_freq); + return t; +} + +GHOST_TUns64 GHOST_SystemWin32::tickCountToMillis(__int64 ticks) const +{ + return ticks - m_lfstart; +} + GHOST_TUns64 GHOST_SystemWin32::getMilliSeconds() const { // Hardware does not support high resolution timers. We will use GetTickCount instead then. if (!m_hasPerformanceCounter) { - return ::GetTickCount(); + return tickCountToMillis(::GetTickCount()); } // Retrieve current count __int64 count = 0; ::QueryPerformanceCounter((LARGE_INTEGER *)&count); - // Calculate the time passed since system initialization. - __int64 delta = 1000 * (count - m_start); - - GHOST_TUns64 t = (GHOST_TUns64)(delta / m_freq); - return t; + return performanceCounterToMillis(count); } GHOST_TUns8 GHOST_SystemWin32::getNumDisplays() const @@ -261,7 +282,7 @@ void GHOST_SystemWin32::getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns3 height = ::GetSystemMetrics(SM_CYVIRTUALSCREEN); } -GHOST_IWindow *GHOST_SystemWin32::createWindow(const STR_String &title, +GHOST_IWindow *GHOST_SystemWin32::createWindow(const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -411,9 +432,9 @@ GHOST_TSuccess GHOST_SystemWin32::disposeContext(GHOST_IContext *context) * Never explicitly delete the window, use #disposeContext() instead. * \return The new context (or 0 if creation failed). */ -GHOST_IContext *GHOST_SystemWin32::createOffscreenContextD3D() +GHOST_ContextD3D *GHOST_SystemWin32::createOffscreenContextD3D() { - GHOST_Context *context; + GHOST_ContextD3D *context; HWND wnd = CreateWindowA("STATIC", "Blender XR", @@ -435,16 +456,11 @@ GHOST_IContext *GHOST_SystemWin32::createOffscreenContextD3D() return context; } -GHOST_IContext *GHOST_SystemWin32::createOffscreenContext(GHOST_TDrawingContextType type) +GHOST_TSuccess GHOST_SystemWin32::disposeContextD3D(GHOST_ContextD3D *context) { - switch (type) { - case GHOST_kDrawingContextTypeOpenGL: - return createOffscreenContext(); - case GHOST_kDrawingContextTypeD3D: - return createOffscreenContextD3D(); - default: - return NULL; - } + delete context; + + return GHOST_kSuccess; } bool GHOST_SystemWin32::processEvents(bool waitForEvent) @@ -575,6 +591,7 @@ GHOST_TSuccess GHOST_SystemWin32::init() FreeLibrary(user32); initRawInput(); + m_lfstart = ::GetTickCount(); // Determine whether this system has a high frequency performance counter. */ m_hasPerformanceCounter = ::QueryPerformanceFrequency((LARGE_INTEGER *)&m_freq) == TRUE; if (m_hasPerformanceCounter) { @@ -620,8 +637,12 @@ GHOST_TSuccess GHOST_SystemWin32::exit() return GHOST_System::exit(); } -GHOST_TKey GHOST_SystemWin32::hardKey(RAWINPUT const &raw, int *keyDown, char *vk) +GHOST_TKey GHOST_SystemWin32::hardKey(RAWINPUT const &raw, + bool *r_keyDown, + bool *r_is_repeated_modifier) { + bool is_repeated_modifier = false; + GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); GHOST_TKey key = GHOST_kKeyUnknown; GHOST_ModifierKeys modifiers; @@ -630,7 +651,7 @@ GHOST_TKey GHOST_SystemWin32::hardKey(RAWINPUT const &raw, int *keyDown, char *v // RI_KEY_BREAK doesn't work for sticky keys release, so we also // check for the up message unsigned int msg = raw.data.keyboard.Message; - *keyDown = !(raw.data.keyboard.Flags & RI_KEY_BREAK) && msg != WM_KEYUP && msg != WM_SYSKEYUP; + *r_keyDown = !(raw.data.keyboard.Flags & RI_KEY_BREAK) && msg != WM_KEYUP && msg != WM_SYSKEYUP; key = this->convertKey(raw.data.keyboard.VKey, raw.data.keyboard.MakeCode, @@ -642,32 +663,32 @@ GHOST_TKey GHOST_SystemWin32::hardKey(RAWINPUT const &raw, int *keyDown, char *v GHOST_TModifierKeyMask modifier; switch (key) { case GHOST_kKeyLeftShift: { - changed = (modifiers.get(GHOST_kModifierKeyLeftShift) != (bool)*keyDown); + changed = (modifiers.get(GHOST_kModifierKeyLeftShift) != *r_keyDown); modifier = GHOST_kModifierKeyLeftShift; break; } case GHOST_kKeyRightShift: { - changed = (modifiers.get(GHOST_kModifierKeyRightShift) != (bool)*keyDown); + changed = (modifiers.get(GHOST_kModifierKeyRightShift) != *r_keyDown); modifier = GHOST_kModifierKeyRightShift; break; } case GHOST_kKeyLeftControl: { - changed = (modifiers.get(GHOST_kModifierKeyLeftControl) != (bool)*keyDown); + changed = (modifiers.get(GHOST_kModifierKeyLeftControl) != *r_keyDown); modifier = GHOST_kModifierKeyLeftControl; break; } case GHOST_kKeyRightControl: { - changed = (modifiers.get(GHOST_kModifierKeyRightControl) != (bool)*keyDown); + changed = (modifiers.get(GHOST_kModifierKeyRightControl) != *r_keyDown); modifier = GHOST_kModifierKeyRightControl; break; } case GHOST_kKeyLeftAlt: { - changed = (modifiers.get(GHOST_kModifierKeyLeftAlt) != (bool)*keyDown); + changed = (modifiers.get(GHOST_kModifierKeyLeftAlt) != *r_keyDown); modifier = GHOST_kModifierKeyLeftAlt; break; } case GHOST_kKeyRightAlt: { - changed = (modifiers.get(GHOST_kModifierKeyRightAlt) != (bool)*keyDown); + changed = (modifiers.get(GHOST_kModifierKeyRightAlt) != *r_keyDown); modifier = GHOST_kModifierKeyRightAlt; break; } @@ -676,17 +697,15 @@ GHOST_TKey GHOST_SystemWin32::hardKey(RAWINPUT const &raw, int *keyDown, char *v } if (changed) { - modifiers.set(modifier, (bool)*keyDown); + modifiers.set(modifier, *r_keyDown); system->storeModifierKeys(modifiers); } else { - key = GHOST_kKeyUnknown; + is_repeated_modifier = true; } } - if (vk) - *vk = raw.data.keyboard.VKey; - + *r_is_repeated_modifier = is_repeated_modifier; return key; } @@ -911,73 +930,122 @@ GHOST_EventButton *GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type, GHOST_TButtonMask mask) { GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); - if (window->useTabletAPI(GHOST_kTabletNative)) { - window->setTabletData(NULL); + + if (type == GHOST_kEventButtonDown) { + window->updateMouseCapture(MousePressed); + } + else if (type == GHOST_kEventButtonUp) { + window->updateMouseCapture(MouseReleased); + } + + if (window->m_tabletInRange) { + if (window->useTabletAPI(GHOST_kTabletNative)) { + // Win32 Pointer processing handles input while in-range and in-contact events. + return NULL; + } } - return new GHOST_EventButton(system->getMilliSeconds(), type, window, mask); + + return new GHOST_EventButton( + system->getMilliSeconds(), type, window, mask, window->getTabletData()); } -GHOST_Event *GHOST_SystemWin32::processPointerEvent(GHOST_TEventType type, - GHOST_WindowWin32 *window, - WPARAM wParam, - LPARAM lParam, - bool &eventHandled) +void GHOST_SystemWin32::processPointerEvents( + UINT type, GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam, bool &eventHandled) { - GHOST_PointerInfoWin32 pointerInfo; + std::vector<GHOST_PointerInfoWin32> pointerInfo; GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); if (!window->useTabletAPI(GHOST_kTabletNative)) { - return NULL; + return; } - if (window->getPointerInfo(&pointerInfo, wParam, lParam) != GHOST_kSuccess) { - return NULL; + if (window->getPointerInfo(pointerInfo, wParam, lParam) != GHOST_kSuccess) { + return; } - if (!pointerInfo.isPrimary) { + if (!pointerInfo[0].isPrimary) { eventHandled = true; - return NULL; // For multi-touch displays we ignore these events + return; // For multi-touch displays we ignore these events } - system->setCursorPosition(pointerInfo.pixelLocation.x, pointerInfo.pixelLocation.y); - switch (type) { - case GHOST_kEventButtonDown: - /* Update window tablet data to be included in event. */ - window->setTabletData(&pointerInfo.tabletData); - eventHandled = true; - return new GHOST_EventButton( - system->getMilliSeconds(), GHOST_kEventButtonDown, window, pointerInfo.buttonMask); - case GHOST_kEventButtonUp: - eventHandled = true; - return new GHOST_EventButton( - system->getMilliSeconds(), GHOST_kEventButtonUp, window, pointerInfo.buttonMask); - case GHOST_kEventCursorMove: - /* Update window tablet data to be included in event. */ - window->setTabletData(&pointerInfo.tabletData); - eventHandled = true; - return new GHOST_EventCursor(system->getMilliSeconds(), - GHOST_kEventCursorMove, - window, - pointerInfo.pixelLocation.x, - pointerInfo.pixelLocation.y); + case WM_POINTERENTER: + window->m_tabletInRange = true; + system->pushEvent(new GHOST_EventCursor(pointerInfo[0].time, + GHOST_kEventCursorMove, + window, + pointerInfo[0].pixelLocation.x, + pointerInfo[0].pixelLocation.y, + pointerInfo[0].tabletData)); + break; + case WM_POINTERDOWN: + // Move cursor to point of contact because GHOST_EventButton does not include position. + system->pushEvent(new GHOST_EventCursor(pointerInfo[0].time, + GHOST_kEventCursorMove, + window, + pointerInfo[0].pixelLocation.x, + pointerInfo[0].pixelLocation.y, + pointerInfo[0].tabletData)); + system->pushEvent(new GHOST_EventButton(pointerInfo[0].time, + GHOST_kEventButtonDown, + window, + pointerInfo[0].buttonMask, + pointerInfo[0].tabletData)); + window->updateMouseCapture(MousePressed); + break; + case WM_POINTERUPDATE: + // Coalesced pointer events are reverse chronological order, reorder chronologically. + // Only contiguous move events are coalesced. + for (GHOST_TUns32 i = pointerInfo.size(); i-- > 0;) { + system->pushEvent(new GHOST_EventCursor(pointerInfo[i].time, + GHOST_kEventCursorMove, + window, + pointerInfo[i].pixelLocation.x, + pointerInfo[i].pixelLocation.y, + pointerInfo[i].tabletData)); + } + break; + case WM_POINTERUP: + system->pushEvent(new GHOST_EventButton(pointerInfo[0].time, + GHOST_kEventButtonUp, + window, + pointerInfo[0].buttonMask, + pointerInfo[0].tabletData)); + window->updateMouseCapture(MouseReleased); + break; + case WM_POINTERLEAVE: + window->m_tabletInRange = false; + system->pushEvent(new GHOST_EventButton(pointerInfo[0].time, + GHOST_kEventCursorMove, + window, + pointerInfo[0].buttonMask, + pointerInfo[0].tabletData)); + break; default: - return NULL; + break; } + + eventHandled = true; + system->setCursorPosition(pointerInfo[0].pixelLocation.x, pointerInfo[0].pixelLocation.y); } -GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_TEventType type, - GHOST_WindowWin32 *window) +GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *window) { GHOST_TInt32 x_screen, y_screen; GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); - system->getCursorPosition(x_screen, y_screen); + if (window->m_tabletInRange) { + if (window->useTabletAPI(GHOST_kTabletNative)) { + // Tablet input handled in WM_POINTER* events. WM_MOUSEMOVE events in response to tablet + // input aren't normally generated when using WM_POINTER events, but manually moving the + // system cursor as we do in WM_POINTER handling does. + return NULL; + } + } - /* TODO: CHECK IF THIS IS A TABLET EVENT */ - bool is_tablet = false; + system->getCursorPosition(x_screen, y_screen); - if (is_tablet == false && window->getCursorGrabModeIsWarp()) { + if (window->getCursorGrabModeIsWarp() && !window->m_tabletInRange) { GHOST_TInt32 x_new = x_screen; GHOST_TInt32 y_new = y_screen; GHOST_TInt32 x_accum, y_accum; @@ -1004,12 +1072,17 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_TEventType type, GHOST_kEventCursorMove, window, x_screen + x_accum, - y_screen + y_accum); + y_screen + y_accum, + window->getTabletData()); } } else { - return new GHOST_EventCursor( - system->getMilliSeconds(), GHOST_kEventCursorMove, window, x_screen, y_screen); + return new GHOST_EventCursor(system->getMilliSeconds(), + GHOST_kEventCursorMove, + window, + x_screen, + y_screen, + window->getTabletData()); } return NULL; } @@ -1038,16 +1111,17 @@ void GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window, WPARAM wPar GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RAWINPUT const &raw) { - int keyDown = 0; - char vk; + bool keyDown = false; + bool is_repeated_modifier = false; GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); - GHOST_TKey key = system->hardKey(raw, &keyDown, &vk); + GHOST_TKey key = system->hardKey(raw, &keyDown, &is_repeated_modifier); GHOST_EventKey *event; /* We used to check `if (key != GHOST_kKeyUnknown)`, but since the message * values `WM_SYSKEYUP`, `WM_KEYUP` and `WM_CHAR` are ignored, we capture * those events here as well. */ - { + if (!is_repeated_modifier) { + char vk = raw.data.keyboard.VKey; char utf8_char[6] = {0}; char ascii = 0; bool is_repeat = false; @@ -1105,6 +1179,10 @@ GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RA // GHOST_PRINTF("%c\n", ascii); // we already get this info via EventPrinter } + else { + event = NULL; + } + return event; } @@ -1390,40 +1468,26 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, //////////////////////////////////////////////////////////////////////// // Pointer events, processed //////////////////////////////////////////////////////////////////////// + case WM_POINTERENTER: case WM_POINTERDOWN: - event = processPointerEvent( - GHOST_kEventButtonDown, window, wParam, lParam, eventHandled); - if (event && eventHandled) { - window->registerMouseClickEvent(0); - } - break; - case WM_POINTERUP: - event = processPointerEvent(GHOST_kEventButtonUp, window, wParam, lParam, eventHandled); - if (event && eventHandled) { - window->registerMouseClickEvent(1); - } - break; case WM_POINTERUPDATE: - event = processPointerEvent( - GHOST_kEventCursorMove, window, wParam, lParam, eventHandled); + case WM_POINTERUP: + case WM_POINTERLEAVE: + processPointerEvents(msg, window, wParam, lParam, eventHandled); break; //////////////////////////////////////////////////////////////////////// // Mouse events, processed //////////////////////////////////////////////////////////////////////// case WM_LBUTTONDOWN: - window->registerMouseClickEvent(0); event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft); break; case WM_MBUTTONDOWN: - window->registerMouseClickEvent(0); event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskMiddle); break; case WM_RBUTTONDOWN: - window->registerMouseClickEvent(0); event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight); break; case WM_XBUTTONDOWN: - window->registerMouseClickEvent(0); if ((short)HIWORD(wParam) == XBUTTON1) { event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton4); } @@ -1432,19 +1496,15 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, } break; case WM_LBUTTONUP: - window->registerMouseClickEvent(1); event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft); break; case WM_MBUTTONUP: - window->registerMouseClickEvent(1); event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle); break; case WM_RBUTTONUP: - window->registerMouseClickEvent(1); event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight); break; case WM_XBUTTONUP: - window->registerMouseClickEvent(1); if ((short)HIWORD(wParam) == XBUTTON1) { event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton4); } @@ -1453,7 +1513,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, } break; case WM_MOUSEMOVE: - event = processCursorEvent(GHOST_kEventCursorMove, window); + event = processCursorEvent(window); break; case WM_MOUSEWHEEL: { /* The WM_MOUSEWHEEL message is sent to the focus window diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h index e624cc83427..6b7901c2ade 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.h +++ b/intern/ghost/intern/GHOST_SystemWin32.h @@ -42,6 +42,7 @@ class GHOST_EventWheel; class GHOST_EventWindow; class GHOST_EventDragnDrop; +class GHOST_ContextD3D; class GHOST_WindowWin32; /** @@ -65,6 +66,20 @@ class GHOST_SystemWin32 : public GHOST_System { ***************************************************************************************/ /** + * This method converts performance counter measurements into milliseconds since the start of the + * system process. + * \return The number of milliseconds since the start of the system process. + */ + GHOST_TUns64 performanceCounterToMillis(__int64 perf_ticks) const; + + /** + * This method converts system ticks into milliseconds since the start of the + * system process. + * \return The number of milliseconds since the start of the system process. + */ + GHOST_TUns64 tickCountToMillis(__int64 ticks) const; + + /** * Returns the system time. * Returns the number of milliseconds since the start of the system process. * This overloaded method uses the high frequency timer if available. @@ -111,7 +126,7 @@ class GHOST_SystemWin32 : public GHOST_System { * \param parentWindow Parent window * \return The new window (or 0 if creation failed). */ - GHOST_IWindow *createWindow(const STR_String &title, + GHOST_IWindow *createWindow(const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -131,18 +146,28 @@ class GHOST_SystemWin32 : public GHOST_System { GHOST_IContext *createOffscreenContext(); /** - * Create a new offscreen context. - * Never explicitly delete the window, use disposeContext() instead. + * Dispose of a context. + * \param context Pointer to the context to be disposed. + * \return Indication of success. + */ + GHOST_TSuccess disposeContext(GHOST_IContext *context); + + /** + * Create a new offscreen DirectX context. + * Never explicitly delete the context, use disposeContext() instead. + * This is for GHOST internal, Win32 specific use, so it can be called statically. + * * \return The new context (or 0 if creation failed). */ - GHOST_IContext *createOffscreenContext(GHOST_TDrawingContextType type); + static GHOST_ContextD3D *createOffscreenContextD3D(); /** - * Dispose of a context. + * Dispose of a DirectX context. + * This is for GHOST internal, Win32 specific use, so it can be called statically. * \param context Pointer to the context to be disposed. * \return Indication of success. */ - GHOST_TSuccess disposeContext(GHOST_IContext *context); + static GHOST_TSuccess disposeContextD3D(GHOST_ContextD3D *context); /*************************************************************************************** ** Event management functionality @@ -187,7 +212,7 @@ class GHOST_SystemWin32 : public GHOST_System { GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const; /** - * Returns the state of the mouse buttons (ouside the message queue). + * Returns the state of the mouse buttons (outside the message queue). * \param buttons The state of the buttons. * \return Indication of success. */ @@ -256,13 +281,6 @@ class GHOST_SystemWin32 : public GHOST_System { GHOST_TSuccess exit(); /** - * Create a new offscreen DirectX context. - * Never explicitly delete the window, use disposeContext() instead. - * \return The new context (or 0 if creation failed). - */ - GHOST_IContext *createOffscreenContextD3D(); - - /** * Converts raw WIN32 key codes from the wndproc to GHOST keys. * \param vKey The virtual key from hardKey * \param ScanCode The ScanCode of pressed key (similar to PS/2 Set 1) @@ -278,7 +296,7 @@ class GHOST_SystemWin32 : public GHOST_System { * \param vk Pointer to virtual key * \return The GHOST key (GHOST_kKeyUnknown if no match). */ - GHOST_TKey hardKey(RAWINPUT const &raw, int *keyDown, char *vk); + GHOST_TKey hardKey(RAWINPUT const &raw, bool *r_keyDown, bool *r_is_repeated_modifier); /** * Creates mouse button event. @@ -292,27 +310,29 @@ class GHOST_SystemWin32 : public GHOST_System { GHOST_TButtonMask mask); /** - * Creates pointer event. - * \param type The type of event to create. + * Creates tablet events from Wintab events. + * \param type The type of pointer event + * \param window The window receiving the event (the active window). + */ + static GHOST_TSuccess processWintabEvents(GHOST_TEventType type, GHOST_WindowWin32 *window); + + /** + * Creates tablet events from pointer events. + * \param type The type of pointer event * \param window The window receiving the event (the active window). * \param wParam The wParam from the wndproc * \param lParam The lParam from the wndproc * \param eventhandled true if the method handled the event - * \return The event created. */ - static GHOST_Event *processPointerEvent(GHOST_TEventType type, - GHOST_WindowWin32 *window, - WPARAM wParam, - LPARAM lParam, - bool &eventhandled); + static void processPointerEvents( + UINT type, GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam, bool &eventhandled); /** * Creates cursor event. - * \param type The type of event to create. * \param window The window receiving the event (the active window). * \return The event created. */ - static GHOST_EventCursor *processCursorEvent(GHOST_TEventType type, GHOST_WindowWin32 *window); + static GHOST_EventCursor *processCursorEvent(GHOST_WindowWin32 *window); /** * Handles a mouse wheel event. @@ -422,6 +442,8 @@ class GHOST_SystemWin32 : public GHOST_System { __int64 m_freq; /** High frequency timer variable. */ __int64 m_start; + /** Low frequency timer variable. */ + __int64 m_lfstart; /** AltGr on current keyboard layout. */ bool m_hasAltGr; /** language identifier. */ diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index a9d656a1c36..5c1f34e3a63 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -338,7 +338,7 @@ void GHOST_SystemX11::getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 * \param parentWindow Parent window * \return The new window (or 0 if creation failed). */ -GHOST_IWindow *GHOST_SystemX11::createWindow(const STR_String &title, +GHOST_IWindow *GHOST_SystemX11::createWindow(const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -910,8 +910,8 @@ void GHOST_SystemX11::processEvent(XEvent *xe) vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end(); for (; win_it != win_end; ++win_it) { - GHOST_WindowX11 *window = static_cast<GHOST_WindowX11 *>(*win_it); - window->refreshXInputDevices(); + GHOST_WindowX11 *window_xinput = static_cast<GHOST_WindowX11 *>(*win_it); + window_xinput->refreshXInputDevices(); } } } @@ -960,11 +960,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) case MotionNotify: { XMotionEvent &xme = xe->xmotion; -#ifdef WITH_X11_XINPUT bool is_tablet = window->GetTabletData().Active != GHOST_kTabletModeNone; -#else - bool is_tablet = false; -#endif if (is_tablet == false && window->getCursorGrabModeIsWarp()) { GHOST_TInt32 x_new = xme.x_root; @@ -1000,12 +996,17 @@ void GHOST_SystemX11::processEvent(XEvent *xe) GHOST_kEventCursorMove, window, xme.x_root + x_accum, - xme.y_root + y_accum); + xme.y_root + y_accum, + window->GetTabletData()); } } else { - g_event = new GHOST_EventCursor( - getMilliSeconds(), GHOST_kEventCursorMove, window, xme.x_root, xme.y_root); + g_event = new GHOST_EventCursor(getMilliSeconds(), + GHOST_kEventCursorMove, + window, + xme.x_root, + xme.y_root, + window->GetTabletData()); } break; } @@ -1272,7 +1273,8 @@ void GHOST_SystemX11::processEvent(XEvent *xe) else break; - g_event = new GHOST_EventButton(getMilliSeconds(), type, window, gbmask); + g_event = new GHOST_EventButton( + getMilliSeconds(), type, window, gbmask, window->GetTabletData()); break; } @@ -1373,8 +1375,12 @@ void GHOST_SystemX11::processEvent(XEvent *xe) */ XCrossingEvent &xce = xe->xcrossing; if (xce.mode == NotifyNormal) { - g_event = new GHOST_EventCursor( - getMilliSeconds(), GHOST_kEventCursorMove, window, xce.x_root, xce.y_root); + g_event = new GHOST_EventCursor(getMilliSeconds(), + GHOST_kEventCursorMove, + window, + xce.x_root, + xce.y_root, + window->GetTabletData()); } // printf("X: %s window %d\n", @@ -1563,7 +1569,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) GHOST_TSuccess GHOST_SystemX11::getModifierKeys(GHOST_ModifierKeys &keys) const { - /* Analyze the masks retuned from XQueryPointer. */ + /* Analyze the masks returned from XQueryPointer. */ memset((void *)m_keyboard_vector, 0, sizeof(m_keyboard_vector)); @@ -1875,7 +1881,7 @@ static GHOST_TKey ghost_key_from_keysym(const KeySym key) # endif #endif default: -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG printf("%s: unknown key: %lu / 0x%lx\n", __func__, key, key); #endif type = GHOST_kKeyUnknown; @@ -1899,7 +1905,7 @@ static GHOST_TKey ghost_key_from_keycode(const XkbDescPtr xkb_descr, const KeyCo switch (id) { case MAKE_ID('T', 'L', 'D', 'E'): return GHOST_kKeyAccentGrave; -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG default: printf("%s unhandled keycode: %.*s\n", __func__, XkbKeyNameLength, id_str); break; @@ -2166,14 +2172,24 @@ GHOST_TUns8 *GHOST_SystemX11::getClipboard(bool selection) const else if (owner == None) return (NULL); + /* Restore events so copy doesn't swallow other event types (keyboard/mouse). */ + vector<XEvent> restore_events; + while (1) { /* only get an event if xcout() is doing something */ - if (context != XCLIB_XCOUT_NONE) + bool restore_this_event = false; + if (context != XCLIB_XCOUT_NONE) { XNextEvent(m_display, &evt); + restore_this_event = (evt.type != SelectionNotify); + } /* fetch the selection, or part of it */ getClipboard_xcout(&evt, sseln, target, &sel_buf, &sel_len, &context); + if (restore_this_event) { + restore_events.push_back(evt); + } + /* fallback is needed. set XA_STRING to target and restart the loop. */ if (context == XCLIB_XCOUT_FALLBACK) { context = XCLIB_XCOUT_NONE; @@ -2202,6 +2218,11 @@ GHOST_TUns8 *GHOST_SystemX11::getClipboard(bool selection) const break; } + while (!restore_events.empty()) { + XPutBackEvent(m_display, &restore_events.back()); + restore_events.pop_back(); + } + if (sel_len) { /* only print the buffer out, and free it, if it's not * empty @@ -2450,7 +2471,7 @@ GHOST_TSuccess GHOST_SystemX11::showMessageBox(const char *title, string cmd = "xdg-open \"" + string(link) + "\""; if (system(cmd.c_str()) != 0) { GHOST_PRINTF("GHOST_SystemX11::showMessageBox: Unable to run system command [%s]", - cmd); + cmd.c_str()); } } break; diff --git a/intern/ghost/intern/GHOST_SystemX11.h b/intern/ghost/intern/GHOST_SystemX11.h index d0e0506e77b..5888605ec95 100644 --- a/intern/ghost/intern/GHOST_SystemX11.h +++ b/intern/ghost/intern/GHOST_SystemX11.h @@ -137,7 +137,7 @@ class GHOST_SystemX11 : public GHOST_System { * \param parentWindow Parent (embedder) window * \return The new window (or 0 if creation failed). */ - GHOST_IWindow *createWindow(const STR_String &title, + GHOST_IWindow *createWindow(const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -182,7 +182,7 @@ class GHOST_SystemX11 : public GHOST_System { GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const; /** - * Returns the state of the mouse buttons (ouside the message queue). + * Returns the state of the mouse buttons (outside the message queue). * \param buttons The state of the buttons. * \return Indication of success. */ @@ -211,7 +211,7 @@ class GHOST_SystemX11 : public GHOST_System { } #endif - /* Helped function for get data from the clipboard. */ + /** Helped function for get data from the clipboard. */ void getClipboard_xcout(const XEvent *evt, Atom sel, Atom target, @@ -337,7 +337,7 @@ class GHOST_SystemX11 : public GHOST_System { private: Display *m_display; - /* Use for scancode lookups. */ + /** Use for scan-code look-ups. */ XkbDescRec *m_xkb_descr; #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) @@ -349,20 +349,22 @@ class GHOST_SystemX11 : public GHOST_System { std::vector<GHOST_TabletX11> m_xtablets; #endif - /// The vector of windows that need to be updated. + /** The vector of windows that need to be updated. */ std::vector<GHOST_WindowX11 *> m_dirty_windows; - /// Start time at initialization. + /** Start time at initialization. */ GHOST_TUns64 m_start_time; - /// A vector of keyboard key masks + /** A vector of keyboard key masks. */ char m_keyboard_vector[32]; - /* to prevent multiple warp, we store the time of the last warp event - * and stop accumulating all events generated before that */ + /** + * To prevent multiple warp, we store the time of the last warp event + * and stop accumulating all events generated before that. + */ Time m_last_warp; - /* detect autorepeat glitch */ + /* Detect auto-repeat glitch. */ unsigned int m_last_release_keycode; Time m_last_release_time; diff --git a/intern/ghost/intern/GHOST_Window.h b/intern/ghost/intern/GHOST_Window.h index 553a7d89df4..472149148e6 100644 --- a/intern/ghost/intern/GHOST_Window.h +++ b/intern/ghost/intern/GHOST_Window.h @@ -27,7 +27,6 @@ #include "GHOST_IWindow.h" -class STR_String; class GHOST_Context; /** @@ -61,8 +60,8 @@ class GHOST_Window : public GHOST_IWindow { * \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 setTitle(const char * title) = 0; + * virtual std::string getTitle() 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; diff --git a/intern/ghost/intern/GHOST_WindowCocoa.h b/intern/ghost/intern/GHOST_WindowCocoa.h index a49949c2c8d..15429eab5db 100644 --- a/intern/ghost/intern/GHOST_WindowCocoa.h +++ b/intern/ghost/intern/GHOST_WindowCocoa.h @@ -30,7 +30,6 @@ #endif // __APPLE__ #include "GHOST_Window.h" -#include "STR_String.h" @class CAMetalLayer; @class CocoaMetalView; @@ -58,7 +57,7 @@ class GHOST_WindowCocoa : public GHOST_Window { * \param stereoVisual Stereo visual for quad buffered stereo. */ GHOST_WindowCocoa(GHOST_SystemCocoa *systemCocoa, - const STR_String &title, + const char *title, GHOST_TInt32 left, GHOST_TInt32 bottom, GHOST_TUns32 width, @@ -92,13 +91,12 @@ class GHOST_WindowCocoa : public GHOST_Window { * Sets the title displayed in the title bar. * \param title The title to display in the title bar. */ - void setTitle(const STR_String &title); - + void setTitle(const char *title); /** * Returns the title displayed in the title bar. * \param title The title displayed in the title bar. */ - void getTitle(STR_String &title) const; + std::string getTitle() const; /** * Returns the window rectangle dimensions. @@ -222,11 +220,6 @@ class GHOST_WindowCocoa : public GHOST_Window { bool isDialog() const; - const GHOST_TabletData &GetTabletData() - { - return m_tablet; - } - GHOST_TabletData &GetCocoaTabletData() { return m_tablet; diff --git a/intern/ghost/intern/GHOST_WindowCocoa.mm b/intern/ghost/intern/GHOST_WindowCocoa.mm index c90b49c27a5..05adc41cb8e 100644 --- a/intern/ghost/intern/GHOST_WindowCocoa.mm +++ b/intern/ghost/intern/GHOST_WindowCocoa.mm @@ -291,7 +291,7 @@ /* clang-format on */ GHOST_WindowCocoa::GHOST_WindowCocoa(GHOST_SystemCocoa *systemCocoa, - const STR_String &title, + const char *title, GHOST_TInt32 left, GHOST_TInt32 bottom, GHOST_TUns32 width, @@ -395,7 +395,7 @@ GHOST_WindowCocoa::GHOST_WindowCocoa(GHOST_SystemCocoa *systemCocoa, setTitle(title); - m_tablet = GHOST_TABLET_DATA_DEFAULT; + m_tablet = GHOST_TABLET_DATA_NONE; CocoaWindowDelegate *windowDelegate = [[CocoaWindowDelegate alloc] init]; [windowDelegate setSystemAndWindowCocoa:systemCocoa windowCocoa:this]; @@ -482,7 +482,7 @@ void *GHOST_WindowCocoa::getOSWindow() const return (void *)m_window; } -void GHOST_WindowCocoa::setTitle(const STR_String &title) +void GHOST_WindowCocoa::setTitle(const char *title) { GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setTitle(): window invalid"); NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; @@ -524,7 +524,7 @@ void GHOST_WindowCocoa::setTitle(const STR_String &title) [pool drain]; } -void GHOST_WindowCocoa::getTitle(STR_String &title) const +std::string GHOST_WindowCocoa::getTitle() const { GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getTitle(): window invalid"); @@ -532,11 +532,14 @@ void GHOST_WindowCocoa::getTitle(STR_String &title) const NSString *windowTitle = [m_window title]; + std::string title; if (windowTitle != nil) { title = [windowTitle UTF8String]; } [pool drain]; + + return title; } void GHOST_WindowCocoa::getWindowBounds(GHOST_Rect &bounds) const diff --git a/intern/ghost/intern/GHOST_WindowNULL.h b/intern/ghost/intern/GHOST_WindowNULL.h index db40075e6ca..e1aa0cb7f13 100644 --- a/intern/ghost/intern/GHOST_WindowNULL.h +++ b/intern/ghost/intern/GHOST_WindowNULL.h @@ -26,7 +26,6 @@ #include <map> -class STR_String; class GHOST_SystemNULL; class GHOST_WindowNULL : public GHOST_Window { @@ -37,7 +36,7 @@ class GHOST_WindowNULL : public GHOST_Window { } GHOST_WindowNULL(GHOST_SystemNULL *system, - const STR_String &title, + const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -83,12 +82,12 @@ class GHOST_WindowNULL : public GHOST_Window { { return true; } - void setTitle(const STR_String &title) + void setTitle(const char *title) { /* nothing */ } - void getTitle(STR_String &title) const + std::string getTitle() const { - title = "untitled"; + return "untitled"; } void getWindowBounds(GHOST_Rect &bounds) const { diff --git a/intern/ghost/intern/GHOST_WindowSDL.cpp b/intern/ghost/intern/GHOST_WindowSDL.cpp index e8d129f45fe..dcb1ab8c78c 100644 --- a/intern/ghost/intern/GHOST_WindowSDL.cpp +++ b/intern/ghost/intern/GHOST_WindowSDL.cpp @@ -27,7 +27,7 @@ #include <assert.h> GHOST_WindowSDL::GHOST_WindowSDL(GHOST_SystemSDL *system, - const STR_String &title, + const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -148,14 +148,14 @@ bool GHOST_WindowSDL::getValid() const return GHOST_Window::getValid() && m_valid_setup; } -void GHOST_WindowSDL::setTitle(const STR_String &title) +void GHOST_WindowSDL::setTitle(const char *title) { - SDL_SetWindowTitle(m_sdl_win, title.ReadPtr()); + SDL_SetWindowTitle(m_sdl_win, title); } -void GHOST_WindowSDL::getTitle(STR_String &title) const +std::string GHOST_WindowSDL::getTitle() const { - title = SDL_GetWindowTitle(m_sdl_win); + return SDL_GetWindowTitle(m_sdl_win); } void GHOST_WindowSDL::getWindowBounds(GHOST_Rect &bounds) const diff --git a/intern/ghost/intern/GHOST_WindowSDL.h b/intern/ghost/intern/GHOST_WindowSDL.h index eadd1b7df9d..5039c742c9d 100644 --- a/intern/ghost/intern/GHOST_WindowSDL.h +++ b/intern/ghost/intern/GHOST_WindowSDL.h @@ -35,7 +35,6 @@ extern "C" { # error "SDL 2.0 or newer is needed to build with Ghost" #endif -class STR_String; class GHOST_SystemSDL; class GHOST_WindowSDL : public GHOST_Window { @@ -49,7 +48,7 @@ class GHOST_WindowSDL : public GHOST_Window { public: GHOST_WindowSDL(GHOST_SystemSDL *system, - const STR_String &title, + const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -107,9 +106,9 @@ class GHOST_WindowSDL : public GHOST_Window { GHOST_TSuccess setWindowCursorVisibility(bool visible); - void setTitle(const STR_String &title); + void setTitle(const char *title); - void getTitle(STR_String &title) const; + std::string getTitle() const; GHOST_TSuccess setClientWidth(GHOST_TUns32 width); diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp new file mode 100644 index 00000000000..ef02db7abc3 --- /dev/null +++ b/intern/ghost/intern/GHOST_WindowWayland.cpp @@ -0,0 +1,415 @@ +/* + * 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. + */ + +/** \file + * \ingroup GHOST + */ + +#include "GHOST_WindowWayland.h" +#include "GHOST_SystemWayland.h" +#include "GHOST_WindowManager.h" + +#include "GHOST_Event.h" + +#include "GHOST_ContextEGL.h" +#include "GHOST_ContextNone.h" + +#include <wayland-egl.h> + +struct window_t { + GHOST_WindowWayland *w; + wl_surface *surface; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; + wl_egl_window *egl_window; + int32_t pending_width, pending_height; + bool is_maximised; + bool is_fullscreen; + bool is_active; + bool is_dialog; + int32_t width, height; +}; + +/* -------------------------------------------------------------------- */ +/** \name Wayland Interface Callbacks + * + * These callbacks are registered for Wayland interfaces and called when + * an event is received from the compositor. + * \{ */ + +static void toplevel_configure( + void *data, xdg_toplevel * /*xdg_toplevel*/, int32_t width, int32_t height, wl_array *states) +{ + window_t *win = static_cast<window_t *>(data); + win->pending_width = width; + win->pending_height = height; + + win->is_maximised = false; + win->is_fullscreen = false; + win->is_active = false; + + /* Note that the macro 'wl_array_for_each' would typically be used to simplify this logic, + * however it's not compatible with C++, so perform casts instead. + * If this needs to be done more often we could define our own C++ compatible macro. */ + for (enum xdg_toplevel_state *state = static_cast<xdg_toplevel_state *>(states->data); + reinterpret_cast<uint8_t *>(state) < (static_cast<uint8_t *>(states->data) + states->size); + state++) { + switch (*state) { + case XDG_TOPLEVEL_STATE_MAXIMIZED: + win->is_maximised = true; + break; + case XDG_TOPLEVEL_STATE_FULLSCREEN: + win->is_fullscreen = true; + break; + case XDG_TOPLEVEL_STATE_ACTIVATED: + win->is_active = true; + break; + default: + break; + } + } +} + +static void toplevel_close(void *data, xdg_toplevel * /*xdg_toplevel*/) +{ + static_cast<window_t *>(data)->w->close(); +} + +static const xdg_toplevel_listener toplevel_listener = { + toplevel_configure, + toplevel_close, +}; + +static void surface_configure(void *data, xdg_surface *xdg_surface, uint32_t serial) +{ + window_t *win = static_cast<window_t *>(data); + + int w, h; + wl_egl_window_get_attached_size(win->egl_window, &w, &h); + if (win->pending_width != 0 && win->pending_height != 0 && win->pending_width != w && + win->pending_height != h) { + win->width = win->pending_width; + win->height = win->pending_height; + wl_egl_window_resize(win->egl_window, win->pending_width, win->pending_height, 0, 0); + win->pending_width = 0; + win->pending_height = 0; + win->w->notify_size(); + } + + if (win->is_active) { + win->w->activate(); + } + else { + win->w->deactivate(); + } + + xdg_surface_ack_configure(xdg_surface, serial); +} + +static const xdg_surface_listener surface_listener = { + surface_configure, +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Ghost Implementation + * + * Wayland specific implementation of the GHOST_Window interface. + * \{ */ + +GHOST_TSuccess GHOST_WindowWayland::hasCursorShape(GHOST_TStandardCursor cursorShape) +{ + return m_system->hasCursorShape(cursorShape); +} + +GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, + const char *title, + GHOST_TInt32 /*left*/, + GHOST_TInt32 /*top*/, + GHOST_TUns32 width, + GHOST_TUns32 height, + GHOST_TWindowState state, + const GHOST_IWindow *parentWindow, + GHOST_TDrawingContextType type, + const bool is_dialog, + const bool stereoVisual, + const bool exclusive) + : GHOST_Window(width, height, state, stereoVisual, exclusive), + m_system(system), + w(new window_t) +{ + w->w = this; + + w->width = int32_t(width); + w->height = int32_t(height); + + w->is_dialog = is_dialog; + + /* Window surfaces. */ + w->surface = wl_compositor_create_surface(m_system->compositor()); + w->egl_window = wl_egl_window_create(w->surface, int(width), int(height)); + + w->xdg_surface = xdg_wm_base_get_xdg_surface(m_system->shell(), w->surface); + w->xdg_toplevel = xdg_surface_get_toplevel(w->xdg_surface); + + wl_surface_set_user_data(w->surface, this); + + xdg_surface_add_listener(w->xdg_surface, &surface_listener, w); + xdg_toplevel_add_listener(w->xdg_toplevel, &toplevel_listener, w); + + if (parentWindow) { + xdg_toplevel_set_parent( + w->xdg_toplevel, dynamic_cast<const GHOST_WindowWayland *>(parentWindow)->w->xdg_toplevel); + } + + /* Call top-level callbacks. */ + wl_surface_commit(w->surface); + wl_display_roundtrip(m_system->display()); + + setState(state); + + setTitle(title); + + /* EGL context. */ + if (setDrawingContextType(type) == GHOST_kFailure) { + GHOST_PRINT("Failed to create EGL context" << std::endl); + } +} + +GHOST_TSuccess GHOST_WindowWayland::close() +{ + return m_system->pushEvent( + new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowClose, this)); +} + +GHOST_TSuccess GHOST_WindowWayland::activate() +{ + if (m_system->getWindowManager()->setActiveWindow(this) == GHOST_kFailure) { + return GHOST_kFailure; + } + return m_system->pushEvent( + new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowActivate, this)); +} + +GHOST_TSuccess GHOST_WindowWayland::deactivate() +{ + m_system->getWindowManager()->setWindowInactive(this); + return m_system->pushEvent( + new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowDeactivate, this)); +} + +GHOST_TSuccess GHOST_WindowWayland::notify_size() +{ + return m_system->pushEvent( + new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowSize, this)); +} + +GHOST_TSuccess GHOST_WindowWayland::setWindowCursorGrab(GHOST_TGrabCursorMode mode) +{ + return m_system->setCursorGrab(mode, w->surface); +} + +GHOST_TSuccess GHOST_WindowWayland::setWindowCursorShape(GHOST_TStandardCursor shape) +{ + const GHOST_TSuccess ok = m_system->setCursorShape(shape); + m_cursorShape = (ok == GHOST_kSuccess) ? shape : GHOST_kStandardCursorDefault; + return ok; +} + +GHOST_TSuccess GHOST_WindowWayland::setWindowCustomCursorShape(GHOST_TUns8 *bitmap, + GHOST_TUns8 *mask, + int sizex, + int sizey, + int hotX, + int hotY, + bool canInvertColor) +{ + return m_system->setCustomCursorShape(bitmap, mask, sizex, sizey, hotX, hotY, canInvertColor); +} + +void GHOST_WindowWayland::setTitle(const char *title) +{ + xdg_toplevel_set_title(w->xdg_toplevel, title); + xdg_toplevel_set_app_id(w->xdg_toplevel, title); + this->title = title; +} + +std::string GHOST_WindowWayland::getTitle() const +{ + return this->title.empty() ? "untitled" : this->title; +} + +void GHOST_WindowWayland::getWindowBounds(GHOST_Rect &bounds) const +{ + getClientBounds(bounds); +} + +void GHOST_WindowWayland::getClientBounds(GHOST_Rect &bounds) const +{ + bounds.set(0, 0, w->width, w->height); +} + +GHOST_TSuccess GHOST_WindowWayland::setClientWidth(GHOST_TUns32 width) +{ + return setClientSize(width, GHOST_TUns32(w->height)); +} + +GHOST_TSuccess GHOST_WindowWayland::setClientHeight(GHOST_TUns32 height) +{ + return setClientSize(GHOST_TUns32(w->width), height); +} + +GHOST_TSuccess GHOST_WindowWayland::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height) +{ + wl_egl_window_resize(w->egl_window, int(width), int(height), 0, 0); + return GHOST_kSuccess; +} + +void GHOST_WindowWayland::screenToClient(GHOST_TInt32 inX, + GHOST_TInt32 inY, + GHOST_TInt32 &outX, + GHOST_TInt32 &outY) const +{ + outX = inX; + outY = inY; +} + +void GHOST_WindowWayland::clientToScreen(GHOST_TInt32 inX, + GHOST_TInt32 inY, + GHOST_TInt32 &outX, + GHOST_TInt32 &outY) const +{ + outX = inX; + outY = inY; +} + +GHOST_WindowWayland::~GHOST_WindowWayland() +{ + releaseNativeHandles(); + + wl_egl_window_destroy(w->egl_window); + xdg_toplevel_destroy(w->xdg_toplevel); + xdg_surface_destroy(w->xdg_surface); + wl_surface_destroy(w->surface); + + delete w; +} + +GHOST_TSuccess GHOST_WindowWayland::setWindowCursorVisibility(bool visible) +{ + return m_system->setCursorVisibility(visible); +} + +GHOST_TSuccess GHOST_WindowWayland::setState(GHOST_TWindowState state) +{ + switch (state) { + case GHOST_kWindowStateNormal: + /* Unset states. */ + switch (getState()) { + case GHOST_kWindowStateMaximized: + xdg_toplevel_unset_maximized(w->xdg_toplevel); + break; + case GHOST_kWindowStateFullScreen: + xdg_toplevel_unset_fullscreen(w->xdg_toplevel); + break; + default: + break; + } + break; + case GHOST_kWindowStateMaximized: + xdg_toplevel_set_maximized(w->xdg_toplevel); + break; + case GHOST_kWindowStateMinimized: + xdg_toplevel_set_minimized(w->xdg_toplevel); + break; + case GHOST_kWindowStateFullScreen: + xdg_toplevel_set_fullscreen(w->xdg_toplevel, nullptr); + break; + case GHOST_kWindowStateEmbedded: + return GHOST_kFailure; + } + return GHOST_kSuccess; +} + +GHOST_TWindowState GHOST_WindowWayland::getState() const +{ + if (w->is_fullscreen) { + return GHOST_kWindowStateFullScreen; + } + else if (w->is_maximised) { + return GHOST_kWindowStateMaximized; + } + else { + return GHOST_kWindowStateNormal; + } +} + +GHOST_TSuccess GHOST_WindowWayland::invalidate() +{ + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_WindowWayland::setOrder(GHOST_TWindowOrder /*order*/) +{ + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_WindowWayland::beginFullScreen() const +{ + xdg_toplevel_set_fullscreen(w->xdg_toplevel, nullptr); + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_WindowWayland::endFullScreen() const +{ + xdg_toplevel_unset_fullscreen(w->xdg_toplevel); + return GHOST_kSuccess; +} + +bool GHOST_WindowWayland::isDialog() const +{ + return w->is_dialog; +} + +/** + * \param type The type of rendering context create. + * \return Indication of success. + */ +GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType type) +{ + GHOST_Context *context; + switch (type) { + case GHOST_kDrawingContextTypeNone: + context = new GHOST_ContextNone(m_wantStereoVisual); + break; + case GHOST_kDrawingContextTypeOpenGL: + context = new GHOST_ContextEGL(m_wantStereoVisual, + EGLNativeWindowType(w->egl_window), + EGLNativeDisplayType(m_system->display()), + EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + 3, + 3, + GHOST_OPENGL_EGL_CONTEXT_FLAGS, + GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY, + EGL_OPENGL_API); + break; + } + + return (context->initializeDrawingContext() == GHOST_kSuccess) ? context : nullptr; +} + +/** \} */ diff --git a/intern/ghost/intern/GHOST_WindowWayland.h b/intern/ghost/intern/GHOST_WindowWayland.h new file mode 100644 index 00000000000..23e55fcd6e4 --- /dev/null +++ b/intern/ghost/intern/GHOST_WindowWayland.h @@ -0,0 +1,124 @@ +/* + * 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. + */ + +/** \file + * \ingroup GHOST + * + * Declaration of GHOST_WindowWayland class. + */ + +#ifndef __GHOST_WINDOWWAYLAND_H__ +#define __GHOST_WINDOWWAYLAND_H__ + +#include "GHOST_Window.h" + +class GHOST_SystemWayland; + +struct window_t; + +class GHOST_WindowWayland : public GHOST_Window { + public: + GHOST_TSuccess hasCursorShape(GHOST_TStandardCursor cursorShape) override; + + GHOST_WindowWayland(GHOST_SystemWayland *system, + const char *title, + GHOST_TInt32 left, + GHOST_TInt32 top, + GHOST_TUns32 width, + GHOST_TUns32 height, + GHOST_TWindowState state, + const GHOST_IWindow *parentWindow, + GHOST_TDrawingContextType type, + const bool is_dialog, + const bool stereoVisual, + const bool exclusive); + + ~GHOST_WindowWayland() override; + + GHOST_TSuccess close(); + + GHOST_TSuccess activate(); + + GHOST_TSuccess deactivate(); + + GHOST_TSuccess notify_size(); + + protected: + GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode) override; + + GHOST_TSuccess setWindowCursorShape(GHOST_TStandardCursor shape) override; + + GHOST_TSuccess setWindowCustomCursorShape(GHOST_TUns8 *bitmap, + GHOST_TUns8 *mask, + int sizex, + int sizey, + int hotX, + int hotY, + bool canInvertColor) override; + + void setTitle(const char *title) override; + + std::string getTitle() const override; + + void getWindowBounds(GHOST_Rect &bounds) const override; + + void getClientBounds(GHOST_Rect &bounds) const override; + + GHOST_TSuccess setClientWidth(GHOST_TUns32 width) override; + + GHOST_TSuccess setClientHeight(GHOST_TUns32 height) override; + + GHOST_TSuccess setClientSize(GHOST_TUns32 width, GHOST_TUns32 height) override; + + void screenToClient(GHOST_TInt32 inX, + GHOST_TInt32 inY, + GHOST_TInt32 &outX, + GHOST_TInt32 &outY) const override; + + void clientToScreen(GHOST_TInt32 inX, + GHOST_TInt32 inY, + GHOST_TInt32 &outX, + GHOST_TInt32 &outY) const override; + + GHOST_TSuccess setWindowCursorVisibility(bool visible) override; + + GHOST_TSuccess setState(GHOST_TWindowState state) override; + + GHOST_TWindowState getState() const override; + + GHOST_TSuccess invalidate() override; + + GHOST_TSuccess setOrder(GHOST_TWindowOrder order) override; + + GHOST_TSuccess beginFullScreen() const override; + + GHOST_TSuccess endFullScreen() const override; + + bool isDialog() const override; + + private: + GHOST_SystemWayland *m_system; + struct window_t *w; + std::string title; + + /** + * \param type The type of rendering context create. + * \return Indication of success. + */ + GHOST_Context *newDrawingContext(GHOST_TDrawingContextType type) override; +}; + +#endif // __GHOST_WINDOWWAYLAND_H__ diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index 6570f27ac5a..55525157753 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -59,7 +59,7 @@ __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; } GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, - const STR_String &title, + const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -72,6 +72,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, bool is_debug, bool dialog) : GHOST_Window(width, height, state, wantStereoVisual, false), + m_tabletInRange(false), m_inLiveResize(false), m_system(system), m_hDC(0), @@ -82,15 +83,15 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, m_wantAlphaBackground(alphaBackground), m_normal_state(GHOST_kWindowStateNormal), m_user32(NULL), - m_fpGetPointerInfo(NULL), - m_fpGetPointerPenInfo(NULL), - m_fpGetPointerTouchInfo(NULL), + m_fpGetPointerInfoHistory(NULL), + m_fpGetPointerPenInfoHistory(NULL), + m_fpGetPointerTouchInfoHistory(NULL), m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : NULL), m_debug_context(is_debug) { // Initialize tablet variables memset(&m_wintab, 0, sizeof(m_wintab)); - m_tabletData = GHOST_TABLET_DATA_DEFAULT; + m_tabletData = GHOST_TABLET_DATA_NONE; // Create window if (state != GHOST_kWindowStateFullScreen) { @@ -157,7 +158,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, height = rect.bottom - rect.top; } - wchar_t *title_16 = alloc_utf16_from_8((char *)(const char *)title, 0); + wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0); m_hWnd = ::CreateWindowW(s_windowClassName, // pointer to registered class name title_16, // pointer to window name wintype, // window style @@ -172,7 +173,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, free(title_16); } else { - wchar_t *title_16 = alloc_utf16_from_8((char *)(const char *)title, 0); + wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0); m_hWnd = ::CreateWindowW(s_windowClassName, // pointer to registered class name title_16, // pointer to window name WS_MAXIMIZE, // window style @@ -288,11 +289,12 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, // Initialize Windows Ink if (m_user32) { - m_fpGetPointerInfo = (GHOST_WIN32_GetPointerInfo)::GetProcAddress(m_user32, "GetPointerInfo"); - m_fpGetPointerPenInfo = (GHOST_WIN32_GetPointerPenInfo)::GetProcAddress(m_user32, - "GetPointerPenInfo"); - m_fpGetPointerTouchInfo = (GHOST_WIN32_GetPointerTouchInfo)::GetProcAddress( - m_user32, "GetPointerTouchInfo"); + m_fpGetPointerInfoHistory = (GHOST_WIN32_GetPointerInfoHistory)::GetProcAddress( + m_user32, "GetPointerInfoHistory"); + m_fpGetPointerPenInfoHistory = (GHOST_WIN32_GetPointerPenInfoHistory)::GetProcAddress( + m_user32, "GetPointerPenInfoHistory"); + m_fpGetPointerTouchInfoHistory = (GHOST_WIN32_GetPointerTouchInfoHistory)::GetProcAddress( + m_user32, "GetPointerTouchInfoHistory"); } // Initialize Wintab @@ -379,9 +381,9 @@ GHOST_WindowWin32::~GHOST_WindowWin32() if (m_user32) { FreeLibrary(m_user32); m_user32 = NULL; - m_fpGetPointerInfo = NULL; - m_fpGetPointerPenInfo = NULL; - m_fpGetPointerTouchInfo = NULL; + m_fpGetPointerInfoHistory = NULL; + m_fpGetPointerPenInfoHistory = NULL; + m_fpGetPointerTouchInfoHistory = NULL; } if (m_customCursor) { @@ -428,19 +430,18 @@ HWND GHOST_WindowWin32::getHWND() const return m_hWnd; } -void GHOST_WindowWin32::setTitle(const STR_String &title) +void GHOST_WindowWin32::setTitle(const char *title) { - wchar_t *title_16 = alloc_utf16_from_8((char *)(const char *)title, 0); + wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0); ::SetWindowTextW(m_hWnd, (wchar_t *)title_16); free(title_16); } -void GHOST_WindowWin32::getTitle(STR_String &title) const +std::string GHOST_WindowWin32::getTitle() const { char buf[s_maxTitleLength]; /*CHANGE + never used yet*/ ::GetWindowText(m_hWnd, buf, s_maxTitleLength); - STR_String temp(buf); - title = buf; + return std::string(buf); } void GHOST_WindowWin32::getWindowBounds(GHOST_Rect &bounds) const @@ -785,21 +786,20 @@ bool GHOST_WindowWin32::isDialog() const return (parent != 0) && (style & WS_POPUPWINDOW); } -void GHOST_WindowWin32::registerMouseClickEvent(int press) +void GHOST_WindowWin32::updateMouseCapture(GHOST_MouseCaptureEventWin32 event) { - - switch (press) { - case 0: + switch (event) { + case MousePressed: m_nPressedButtons++; break; - case 1: + case MouseReleased: if (m_nPressedButtons) m_nPressedButtons--; break; - case 2: + case OperatorGrab: m_hasGrabMouse = true; break; - case 3: + case OperatorUngrab: m_hasGrabMouse = false; break; } @@ -814,6 +814,11 @@ void GHOST_WindowWin32::registerMouseClickEvent(int press) } } +bool GHOST_WindowWin32::getMousePressed() const +{ + return m_nPressedButtons; +} + HCURSOR GHOST_WindowWin32::getStandardCursor(GHOST_TStandardCursor shape) const { // Convert GHOST cursor to Windows OEM cursor @@ -977,7 +982,7 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCursorGrab(GHOST_TGrabCursorMode mode if (mode == GHOST_kGrabHide) setWindowCursorVisibility(false); } - registerMouseClickEvent(2); + updateMouseCapture(OperatorGrab); } else { if (m_cursorGrab == GHOST_kGrabHide) { @@ -997,7 +1002,7 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCursorGrab(GHOST_TGrabCursorMode mode * can be incorrect on exit. */ setCursorGrabAccum(0, 0); m_cursorGrabBounds.m_l = m_cursorGrabBounds.m_r = -1; /* disable */ - registerMouseClickEvent(3); + updateMouseCapture(OperatorUngrab); } return GHOST_kSuccess; @@ -1017,78 +1022,82 @@ GHOST_TSuccess GHOST_WindowWin32::hasCursorShape(GHOST_TStandardCursor cursorSha return (getStandardCursor(cursorShape)) ? GHOST_kSuccess : GHOST_kFailure; } -GHOST_TSuccess GHOST_WindowWin32::getPointerInfo(GHOST_PointerInfoWin32 *pointerInfo, - WPARAM wParam, - LPARAM lParam) +GHOST_TSuccess GHOST_WindowWin32::getPointerInfo( + std::vector<GHOST_PointerInfoWin32> &outPointerInfo, WPARAM wParam, LPARAM lParam) { - ZeroMemory(pointerInfo, sizeof(GHOST_PointerInfoWin32)); - - // Obtain the basic information from the event - pointerInfo->pointerId = GET_POINTERID_WPARAM(wParam); - pointerInfo->isInContact = IS_POINTER_INCONTACT_WPARAM(wParam); - pointerInfo->isPrimary = IS_POINTER_PRIMARY_WPARAM(wParam); - - // Obtain more accurate and predicted information from the Pointer API - POINTER_INFO pointerApiInfo; - if (!(m_fpGetPointerInfo && m_fpGetPointerInfo(pointerInfo->pointerId, &pointerApiInfo))) { + if (!useTabletAPI(GHOST_kTabletNative)) { return GHOST_kFailure; } - pointerInfo->hasButtonMask = GHOST_kSuccess; - switch (pointerApiInfo.ButtonChangeType) { - case POINTER_CHANGE_FIRSTBUTTON_DOWN: - case POINTER_CHANGE_FIRSTBUTTON_UP: - pointerInfo->buttonMask = GHOST_kButtonMaskLeft; - break; - case POINTER_CHANGE_SECONDBUTTON_DOWN: - case POINTER_CHANGE_SECONDBUTTON_UP: - pointerInfo->buttonMask = GHOST_kButtonMaskRight; - break; - case POINTER_CHANGE_THIRDBUTTON_DOWN: - case POINTER_CHANGE_THIRDBUTTON_UP: - pointerInfo->buttonMask = GHOST_kButtonMaskMiddle; - break; - case POINTER_CHANGE_FOURTHBUTTON_DOWN: - case POINTER_CHANGE_FOURTHBUTTON_UP: - pointerInfo->buttonMask = GHOST_kButtonMaskButton4; - break; - case POINTER_CHANGE_FIFTHBUTTON_DOWN: - case POINTER_CHANGE_FIFTHBUTTON_UP: - pointerInfo->buttonMask = GHOST_kButtonMaskButton5; - break; - default: - pointerInfo->hasButtonMask = GHOST_kFailure; - break; + GHOST_TInt32 pointerId = GET_POINTERID_WPARAM(wParam); + GHOST_TInt32 isPrimary = IS_POINTER_PRIMARY_WPARAM(wParam); + GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem(); + GHOST_TUns32 outCount; + + if (!(m_fpGetPointerInfoHistory && m_fpGetPointerInfoHistory(pointerId, &outCount, NULL))) { + return GHOST_kFailure; } - pointerInfo->pixelLocation = pointerApiInfo.ptPixelLocation; - pointerInfo->tabletData.Active = GHOST_kTabletModeNone; - pointerInfo->tabletData.Pressure = 1.0f; - pointerInfo->tabletData.Xtilt = 0.0f; - pointerInfo->tabletData.Ytilt = 0.0f; + auto pointerPenInfo = std::vector<POINTER_PEN_INFO>(outCount); + outPointerInfo.resize(outCount); - if (pointerApiInfo.pointerType != PT_PEN) { + if (!(m_fpGetPointerPenInfoHistory && + m_fpGetPointerPenInfoHistory(pointerId, &outCount, pointerPenInfo.data()))) { return GHOST_kFailure; } - POINTER_PEN_INFO pointerPenInfo; - if (m_fpGetPointerPenInfo && m_fpGetPointerPenInfo(pointerInfo->pointerId, &pointerPenInfo)) { - pointerInfo->tabletData.Active = GHOST_kTabletModeStylus; + for (GHOST_TUns32 i = 0; i < outCount; i++) { + POINTER_INFO pointerApiInfo = pointerPenInfo[i].pointerInfo; + // Obtain the basic information from the event + outPointerInfo[i].pointerId = pointerId; + outPointerInfo[i].isPrimary = isPrimary; + + switch (pointerApiInfo.ButtonChangeType) { + case POINTER_CHANGE_FIRSTBUTTON_DOWN: + case POINTER_CHANGE_FIRSTBUTTON_UP: + outPointerInfo[i].buttonMask = GHOST_kButtonMaskLeft; + break; + case POINTER_CHANGE_SECONDBUTTON_DOWN: + case POINTER_CHANGE_SECONDBUTTON_UP: + outPointerInfo[i].buttonMask = GHOST_kButtonMaskRight; + break; + case POINTER_CHANGE_THIRDBUTTON_DOWN: + case POINTER_CHANGE_THIRDBUTTON_UP: + outPointerInfo[i].buttonMask = GHOST_kButtonMaskMiddle; + break; + case POINTER_CHANGE_FOURTHBUTTON_DOWN: + case POINTER_CHANGE_FOURTHBUTTON_UP: + outPointerInfo[i].buttonMask = GHOST_kButtonMaskButton4; + break; + case POINTER_CHANGE_FIFTHBUTTON_DOWN: + case POINTER_CHANGE_FIFTHBUTTON_UP: + outPointerInfo[i].buttonMask = GHOST_kButtonMaskButton5; + break; + default: + break; + } + + outPointerInfo[i].pixelLocation = pointerApiInfo.ptPixelLocation; + outPointerInfo[i].tabletData.Active = GHOST_kTabletModeStylus; + outPointerInfo[i].tabletData.Pressure = 1.0f; + outPointerInfo[i].tabletData.Xtilt = 0.0f; + outPointerInfo[i].tabletData.Ytilt = 0.0f; + outPointerInfo[i].time = system->performanceCounterToMillis(pointerApiInfo.PerformanceCount); - if (pointerPenInfo.penMask & PEN_MASK_PRESSURE) { - pointerInfo->tabletData.Pressure = pointerPenInfo.pressure / 1024.0f; + if (pointerPenInfo[i].penMask & PEN_MASK_PRESSURE) { + outPointerInfo[i].tabletData.Pressure = pointerPenInfo[i].pressure / 1024.0f; } - if (pointerPenInfo.penFlags & PEN_FLAG_ERASER) { - pointerInfo->tabletData.Active = GHOST_kTabletModeEraser; + if (pointerPenInfo[i].penFlags & PEN_FLAG_ERASER) { + outPointerInfo[i].tabletData.Active = GHOST_kTabletModeEraser; } - if (pointerPenInfo.penFlags & PEN_MASK_TILT_X) { - pointerInfo->tabletData.Xtilt = fmin(fabs(pointerPenInfo.tiltX / 90), 1.0f); + if (pointerPenInfo[i].penMask & PEN_MASK_TILT_X) { + outPointerInfo[i].tabletData.Xtilt = fmin(fabs(pointerPenInfo[i].tiltX / 90.0f), 1.0f); } - if (pointerPenInfo.penFlags & PEN_MASK_TILT_Y) { - pointerInfo->tabletData.Ytilt = fmin(fabs(pointerPenInfo.tiltY / 90), 1.0f); + if (pointerPenInfo[i].penMask & PEN_MASK_TILT_Y) { + outPointerInfo[i].tabletData.Ytilt = fmin(fabs(pointerPenInfo[i].tiltY / 90.0f), 1.0f); } } @@ -1101,7 +1110,7 @@ void GHOST_WindowWin32::setTabletData(GHOST_TabletData *pTabletData) m_tabletData = *pTabletData; } else { - m_tabletData = GHOST_TABLET_DATA_DEFAULT; + m_tabletData = GHOST_TABLET_DATA_NONE; } } diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h index ac1ec0ee852..dbed7c5ee5f 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.h +++ b/intern/ghost/intern/GHOST_WindowWin32.h @@ -35,6 +35,8 @@ # include "GHOST_ImeWin32.h" #endif +#include <vector> + #include <wintab.h> #define PACKETDATA (PK_BUTTONS | PK_NORMAL_PRESSURE | PK_ORIENTATION | PK_CURSOR) #define PACKETMODE PK_BUTTONS @@ -88,6 +90,26 @@ typedef enum tagPOINTER_BUTTON_CHANGE_TYPE { typedef DWORD POINTER_INPUT_TYPE; typedef UINT32 POINTER_FLAGS; +#define POINTER_FLAG_NONE 0x00000000 +#define POINTER_FLAG_NEW 0x00000001 +#define POINTER_FLAG_INRANGE 0x00000002 +#define POINTER_FLAG_INCONTACT 0x00000004 +#define POINTER_FLAG_FIRSTBUTTON 0x00000010 +#define POINTER_FLAG_SECONDBUTTON 0x00000020 +#define POINTER_FLAG_THIRDBUTTON 0x00000040 +#define POINTER_FLAG_FOURTHBUTTON 0x00000080 +#define POINTER_FLAG_FIFTHBUTTON 0x00000100 +#define POINTER_FLAG_PRIMARY 0x00002000 +#define POINTER_FLAG_CONFIDENCE 0x000004000 +#define POINTER_FLAG_CANCELED 0x000008000 +#define POINTER_FLAG_DOWN 0x00010000 +#define POINTER_FLAG_UPDATE 0x00020000 +#define POINTER_FLAG_UP 0x00040000 +#define POINTER_FLAG_WHEEL 0x00080000 +#define POINTER_FLAG_HWHEEL 0x00100000 +#define POINTER_FLAG_CAPTURECHANGED 0x00200000 +#define POINTER_FLAG_HASTRANSFORM 0x00400000 + typedef struct tagPOINTER_INFO { POINTER_INPUT_TYPE pointerType; UINT32 pointerId; @@ -192,20 +214,33 @@ typedef struct tagPOINTER_TOUCH_INFO { #define IS_POINTER_CANCELED_WPARAM(wParam) \ IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_CANCELED) -typedef BOOL(API *GHOST_WIN32_GetPointerInfo)(UINT32 pointerId, POINTER_INFO *pointerInfo); -typedef BOOL(API *GHOST_WIN32_GetPointerPenInfo)(UINT32 pointerId, POINTER_PEN_INFO *penInfo); -typedef BOOL(API *GHOST_WIN32_GetPointerTouchInfo)(UINT32 pointerId, POINTER_TOUCH_INFO *penInfo); +typedef BOOL(WINAPI *GHOST_WIN32_GetPointerInfoHistory)(UINT32 pointerId, + UINT32 *entriesCount, + POINTER_INFO *pointerInfo); +typedef BOOL(WINAPI *GHOST_WIN32_GetPointerPenInfoHistory)(UINT32 pointerId, + UINT32 *entriesCount, + POINTER_PEN_INFO *penInfo); +typedef BOOL(WINAPI *GHOST_WIN32_GetPointerTouchInfoHistory)(UINT32 pointerId, + UINT32 *entriesCount, + POINTER_TOUCH_INFO *touchInfo); struct GHOST_PointerInfoWin32 { GHOST_TInt32 pointerId; - GHOST_TInt32 isInContact; GHOST_TInt32 isPrimary; - GHOST_TSuccess hasButtonMask; GHOST_TButtonMask buttonMask; POINT pixelLocation; + GHOST_TUns64 time; + GHOST_TabletData tabletData; }; +typedef enum { + MousePressed, + MouseReleased, + OperatorGrab, + OperatorUngrab +} GHOST_MouseCaptureEventWin32; + /** * GHOST window on M$ Windows OSs. */ @@ -226,7 +261,7 @@ class GHOST_WindowWin32 : public GHOST_Window { * \param parentWindowHwnd */ GHOST_WindowWin32(GHOST_SystemWin32 *system, - const STR_String &title, + const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -261,13 +296,13 @@ class GHOST_WindowWin32 : public GHOST_Window { * Sets the title displayed in the title bar. * \param title The title to display in the title bar. */ - void setTitle(const STR_String &title); + void setTitle(const char *title); /** * Returns the title displayed in the title bar. - * \param title The title displayed in the title bar. + * \return The title displayed in the title bar. */ - void getTitle(STR_String &title) const; + std::string getTitle() const; /** * Returns the window rectangle dimensions. @@ -364,17 +399,14 @@ class GHOST_WindowWin32 : public GHOST_Window { GHOST_TSuccess endProgressBar(); /** - * Register a mouse click event (should be called + * Register a mouse capture state (should be called * for any real button press, controls mouse * capturing). * - * \param press - * 0 - mouse pressed - * 1 - mouse released - * 2 - operator grab - * 3 - operator ungrab + * \param event Whether mouse was pressed and released, or an operator grabbed or ungrabbed the + * mouse */ - void registerMouseClickEvent(int press); + void updateMouseCapture(GHOST_MouseCaptureEventWin32 event); /** * Inform the window that it has lost mouse capture, @@ -392,16 +424,24 @@ class GHOST_WindowWin32 : public GHOST_Window { HCURSOR getStandardCursor(GHOST_TStandardCursor shape) const; void loadCursor(bool visible, GHOST_TStandardCursor cursorShape) const; - const GHOST_TabletData &GetTabletData() + const GHOST_TabletData &getTabletData() { return m_tabletData; } void setTabletData(GHOST_TabletData *tabletData); bool useTabletAPI(GHOST_TTabletAPI api) const; - void getPointerInfo(WPARAM wParam); - void processWin32PointerEvent(WPARAM wParam); + /** + * Translate WM_POINTER events into GHOST_PointerInfoWin32 structs. + * \param outPointerInfo Storage to return resulting GHOST_PointerInfoWin32 structs + * \param wParam WPARAM of the event + * \param lParam LPARAM of the event + */ + GHOST_TSuccess getPointerInfo(std::vector<GHOST_PointerInfoWin32> &outPointerInfo, + WPARAM wParam, + LPARAM lParam); + void processWin32TabletActivateEvent(WORD state); void processWin32TabletInitEvent(); void processWin32TabletEvent(WPARAM wParam, LPARAM lParam); @@ -419,7 +459,14 @@ class GHOST_WindowWin32 : public GHOST_Window { GHOST_TUns16 getDPIHint() override; - GHOST_TSuccess getPointerInfo(GHOST_PointerInfoWin32 *pointerInfo, WPARAM wParam, LPARAM lParam); + /** + * Get whether there are currently any mouse buttons pressed + * \return True if there are any currently pressed mouse buttons + */ + bool getMousePressed() const; + + /** Whether a tablet stylus is being tracked */ + bool m_tabletInRange; /** if the window currently resizing */ bool m_inLiveResize; @@ -527,9 +574,9 @@ class GHOST_WindowWin32 : public GHOST_Window { /** user32 dll handle*/ HMODULE m_user32; - GHOST_WIN32_GetPointerInfo m_fpGetPointerInfo; - GHOST_WIN32_GetPointerPenInfo m_fpGetPointerPenInfo; - GHOST_WIN32_GetPointerTouchInfo m_fpGetPointerTouchInfo; + GHOST_WIN32_GetPointerInfoHistory m_fpGetPointerInfoHistory; + GHOST_WIN32_GetPointerPenInfoHistory m_fpGetPointerPenInfoHistory; + GHOST_WIN32_GetPointerTouchInfoHistory m_fpGetPointerTouchInfoHistory; HWND m_parentWindowHwnd; diff --git a/intern/ghost/intern/GHOST_WindowX11.cpp b/intern/ghost/intern/GHOST_WindowX11.cpp index 3bece605d45..691f1790a2d 100644 --- a/intern/ghost/intern/GHOST_WindowX11.cpp +++ b/intern/ghost/intern/GHOST_WindowX11.cpp @@ -33,7 +33,6 @@ #include "GHOST_IconX11.h" #include "GHOST_SystemX11.h" #include "GHOST_WindowX11.h" -#include "STR_String.h" #ifdef WITH_XDND # include "GHOST_DropTargetX11.h" @@ -61,6 +60,7 @@ #include <unistd.h> #include <algorithm> +#include <limits.h> #include <math.h> #include <string> @@ -212,7 +212,7 @@ static XVisualInfo *x11_visualinfo_from_glx(Display *display, GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system, Display *display, - const STR_String &title, + const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -239,6 +239,7 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system, #ifdef WITH_XDND m_dropTarget(NULL), #endif + m_tabletData(GHOST_TABLET_DATA_NONE), #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) m_xic(NULL), #endif @@ -413,9 +414,9 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system, /* XClassHint, title */ { XClassHint *xclasshint = XAllocClassHint(); - const int len = title.Length() + 1; + const int len = strlen(title) + 1; char *wmclass = (char *)malloc(sizeof(char) * len); - memcpy(wmclass, title.ReadPtr(), len * sizeof(char)); + memcpy(wmclass, title, len * sizeof(char)); xclasshint->res_name = wmclass; xclasshint->res_class = wmclass; XSetClassHint(m_display, m_window, xclasshint); @@ -498,8 +499,6 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system, #ifdef WITH_X11_XINPUT refreshXInputDevices(); - - m_tabletData = GHOST_TABLET_DATA_DEFAULT; #endif /* now set up the rendering context. */ @@ -618,7 +617,7 @@ bool GHOST_WindowX11::getValid() const return GHOST_Window::getValid() && m_valid_setup; } -void GHOST_WindowX11::setTitle(const STR_String &title) +void GHOST_WindowX11::setTitle(const char *title) { Atom name = XInternAtom(m_display, "_NET_WM_NAME", 0); Atom utf8str = XInternAtom(m_display, "UTF8_STRING", 0); @@ -628,8 +627,8 @@ void GHOST_WindowX11::setTitle(const STR_String &title) utf8str, 8, PropModeReplace, - (const unsigned char *)title.ReadPtr(), - title.Length()); + (const unsigned char *)title, + strlen(title)); /* This should convert to valid x11 string * and getTitle would need matching change */ @@ -638,13 +637,14 @@ void GHOST_WindowX11::setTitle(const STR_String &title) XFlush(m_display); } -void GHOST_WindowX11::getTitle(STR_String &title) const +std::string GHOST_WindowX11::getTitle() const { char *name = NULL; XFetchName(m_display, m_window, &name); - title = name ? name : "untitled"; + std::string title = name ? name : "untitled"; XFree(name); + return title; } void GHOST_WindowX11::getWindowBounds(GHOST_Rect &bounds) const diff --git a/intern/ghost/intern/GHOST_WindowX11.h b/intern/ghost/intern/GHOST_WindowX11.h index a9914d88340..4232ff40b52 100644 --- a/intern/ghost/intern/GHOST_WindowX11.h +++ b/intern/ghost/intern/GHOST_WindowX11.h @@ -37,7 +37,6 @@ #include <map> -class STR_String; class GHOST_SystemX11; #ifdef WITH_XDND @@ -69,7 +68,7 @@ class GHOST_WindowX11 : public GHOST_Window { */ GHOST_WindowX11(GHOST_SystemX11 *system, Display *display, - const STR_String &title, + const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -85,9 +84,9 @@ class GHOST_WindowX11 : public GHOST_Window { bool getValid() const; - void setTitle(const STR_String &title); + void setTitle(const char *title); - void getTitle(STR_String &title) const; + std::string getTitle() const; void getWindowBounds(GHOST_Rect &bounds) const; @@ -144,12 +143,11 @@ class GHOST_WindowX11 : public GHOST_Window { * Return a handle to the x11 window type. */ Window getXWindow(); -#ifdef WITH_X11_XINPUT + GHOST_TabletData &GetTabletData() { return m_tabletData; } -#endif // WITH_X11_XINPUT #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) XIC getX11_XIC() @@ -269,9 +267,7 @@ class GHOST_WindowX11 : public GHOST_Window { GHOST_DropTargetX11 *m_dropTarget; #endif -#ifdef WITH_X11_XINPUT GHOST_TabletData m_tabletData; -#endif #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) XIC m_xic; diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp index a757aa9a555..3aab420a9b6 100644 --- a/intern/ghost/intern/GHOST_XrContext.cpp +++ b/intern/ghost/intern/GHOST_XrContext.cpp @@ -465,7 +465,14 @@ void GHOST_XrContext::startSession(const GHOST_XrSessionBeginInfo *begin_info) void GHOST_XrContext::endSession() { - m_session->requestEnd(); + if (m_session) { + if (m_session->isRunning()) { + m_session->requestEnd(); + } + else { + m_session = nullptr; + } + } } bool GHOST_XrContext::isSessionRunning() const @@ -512,6 +519,13 @@ void GHOST_XrContext::setDrawViewFunc(GHOST_XrDrawViewFn draw_view_fn) m_custom_funcs.draw_view_fn = draw_view_fn; } +bool GHOST_XrContext::needsUpsideDownDrawing() const +{ + /* Must only be called after the session was started */ + assert(m_session); + return m_session->needsUpsideDownDrawing(); +} + /** \} */ /* Public Accessors and Mutators */ /* -------------------------------------------------------------------- */ diff --git a/intern/ghost/intern/GHOST_XrContext.h b/intern/ghost/intern/GHOST_XrContext.h index af65f262323..7dbd0a0d011 100644 --- a/intern/ghost/intern/GHOST_XrContext.h +++ b/intern/ghost/intern/GHOST_XrContext.h @@ -78,6 +78,7 @@ class GHOST_XrContext : public GHOST_IXrContext { void setGraphicsContextBindFuncs(GHOST_XrGraphicsContextBindFn bind_fn, GHOST_XrGraphicsContextUnbindFn unbind_fn) override; void setDrawViewFunc(GHOST_XrDrawViewFn draw_view_fn) override; + bool needsUpsideDownDrawing() const override; void handleSessionStateChange(const XrEventDataSessionStateChanged *lifecycle); diff --git a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp index ea97b3a50cf..71e6af3fa4f 100644 --- a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp +++ b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp @@ -22,11 +22,12 @@ #include <list> #include <sstream> -#if defined(WITH_X11) +#if defined(WITH_GHOST_X11) # include "GHOST_ContextGLX.h" #elif defined(WIN32) # include "GHOST_ContextD3D.h" # include "GHOST_ContextWGL.h" +# include "GHOST_SystemWin32.h" #endif #include "GHOST_C-api.h" #include "GHOST_Xr_intern.h" @@ -67,7 +68,7 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding { XrSystemId system_id, std::string *r_requirement_info) const override { -#if defined(WITH_X11) +#if defined(WITH_GHOST_X11) GHOST_ContextGLX *ctx_gl = static_cast<GHOST_ContextGLX *>(ghost_ctx); #else GHOST_ContextWGL *ctx_gl = static_cast<GHOST_ContextWGL *>(ghost_ctx); @@ -106,7 +107,7 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding { void initFromGhostContext(GHOST_Context *ghost_ctx) override { -#if defined(WITH_X11) +#if defined(WITH_GHOST_X11) GHOST_ContextGLX *ctx_glx = static_cast<GHOST_ContextGLX *>(ghost_ctx); XVisualInfo *visual_info = glXGetVisualFromFBConfig(ctx_glx->m_display, ctx_glx->m_fbconfig); @@ -180,6 +181,11 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding { glBindFramebuffer(GL_FRAMEBUFFER, 0); } + bool needsUpsideDownDrawing(GHOST_Context &ghost_ctx) const override + { + return ghost_ctx.isUpsideDown(); + } + private: std::list<std::vector<XrSwapchainImageOpenGLKHR>> m_image_cache; GLuint m_fbo = 0; @@ -188,19 +194,27 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding { #ifdef WIN32 class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding { public: + GHOST_XrGraphicsBindingD3D(GHOST_Context *ghost_ctx) + : GHOST_IXrGraphicsBinding(), m_ghost_wgl_ctx(*static_cast<GHOST_ContextWGL *>(ghost_ctx)) + { + m_ghost_d3d_ctx = GHOST_SystemWin32::createOffscreenContextD3D(); + } ~GHOST_XrGraphicsBindingD3D() { if (m_shared_resource) { - m_ghost_ctx->disposeSharedOpenGLResource(m_shared_resource); + m_ghost_d3d_ctx->disposeSharedOpenGLResource(m_shared_resource); + } + if (m_ghost_d3d_ctx) { + GHOST_SystemWin32::disposeContextD3D(m_ghost_d3d_ctx); } } - bool checkVersionRequirements(GHOST_Context *ghost_ctx, - XrInstance instance, - XrSystemId system_id, - std::string *r_requirement_info) const override + bool checkVersionRequirements( + GHOST_Context * /*ghost_ctx*/, /* Remember: This is the OpenGL context! */ + XrInstance instance, + XrSystemId system_id, + std::string *r_requirement_info) const override { - GHOST_ContextD3D *ctx_dx = static_cast<GHOST_ContextD3D *>(ghost_ctx); static PFN_xrGetD3D11GraphicsRequirementsKHR s_xrGetD3D11GraphicsRequirementsKHR_fn = nullptr; XrGraphicsRequirementsD3D11KHR gpu_requirements = {XR_TYPE_GRAPHICS_REQUIREMENTS_D3D11_KHR}; @@ -222,16 +236,15 @@ class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding { *r_requirement_info = std::move(strstream.str()); } - return ctx_dx->m_device->GetFeatureLevel() >= gpu_requirements.minFeatureLevel; + return m_ghost_d3d_ctx->m_device->GetFeatureLevel() >= gpu_requirements.minFeatureLevel; } - void initFromGhostContext(GHOST_Context *ghost_ctx) override + void initFromGhostContext( + GHOST_Context * /*ghost_ctx*/ /* Remember: This is the OpenGL context! */ + ) override { - GHOST_ContextD3D *ctx_d3d = static_cast<GHOST_ContextD3D *>(ghost_ctx); - oxr_binding.d3d11.type = XR_TYPE_GRAPHICS_BINDING_D3D11_KHR; - oxr_binding.d3d11.device = ctx_d3d->m_device; - m_ghost_ctx = ctx_d3d; + oxr_binding.d3d11.device = m_ghost_d3d_ctx->m_device; } bool chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats, @@ -266,7 +279,7 @@ class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding { swapchain_image); # if 0 - /* Ideally we'd just create a render target view for the OpenXR swapchain image texture and + /* Ideally we'd just create a render target view for the OpenXR swap-chain image texture and * blit from the OpenGL context into it. The NV_DX_interop extension doesn't want to work with * this though. At least not with Optimus hardware. See: * https://github.com/mpv-player/mpv/issues/2949#issuecomment-197262807. @@ -284,35 +297,47 @@ class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding { m_ghost_ctx->blitFromOpenGLContext(m_shared_resource, draw_info->width, draw_info->height); # else if (!m_shared_resource) { - m_shared_resource = m_ghost_ctx->createSharedOpenGLResource(draw_info->width, - draw_info->height); + m_shared_resource = m_ghost_d3d_ctx->createSharedOpenGLResource(draw_info->width, + draw_info->height); } - m_ghost_ctx->blitFromOpenGLContext(m_shared_resource, draw_info->width, draw_info->height); + m_ghost_d3d_ctx->blitFromOpenGLContext(m_shared_resource, draw_info->width, draw_info->height); - m_ghost_ctx->m_device_ctx->OMSetRenderTargets(0, nullptr, nullptr); - m_ghost_ctx->m_device_ctx->CopyResource(d3d_swapchain_image->texture, - m_ghost_ctx->getSharedTexture2D(m_shared_resource)); + m_ghost_d3d_ctx->m_device_ctx->OMSetRenderTargets(0, nullptr, nullptr); + m_ghost_d3d_ctx->m_device_ctx->CopyResource( + d3d_swapchain_image->texture, m_ghost_d3d_ctx->getSharedTexture2D(m_shared_resource)); # endif } + bool needsUpsideDownDrawing(GHOST_Context &) const + { + return m_ghost_d3d_ctx->isUpsideDown(); + } + private: - GHOST_ContextD3D *m_ghost_ctx; - GHOST_SharedOpenGLResource *m_shared_resource; + /** Primary OpenGL context for Blender to use for drawing. */ + GHOST_ContextWGL &m_ghost_wgl_ctx; + /** Secondary DirectX 11 context to share with OpenGL context. */ + GHOST_ContextD3D *m_ghost_d3d_ctx = nullptr; + /** Handle to shared resource object. */ + GHOST_SharedOpenGLResource *m_shared_resource = nullptr; + std::list<std::vector<XrSwapchainImageD3D11KHR>> m_image_cache; }; #endif // WIN32 std::unique_ptr<GHOST_IXrGraphicsBinding> GHOST_XrGraphicsBindingCreateFromType( - GHOST_TXrGraphicsBinding type) + GHOST_TXrGraphicsBinding type, GHOST_Context *context) { switch (type) { case GHOST_kXrGraphicsOpenGL: return std::unique_ptr<GHOST_XrGraphicsBindingOpenGL>(new GHOST_XrGraphicsBindingOpenGL()); #ifdef WIN32 case GHOST_kXrGraphicsD3D11: - return std::unique_ptr<GHOST_XrGraphicsBindingD3D>(new GHOST_XrGraphicsBindingD3D()); + return std::unique_ptr<GHOST_XrGraphicsBindingD3D>(new GHOST_XrGraphicsBindingD3D(context)); #endif default: return nullptr; } + + (void)context; /* Might be unused. */ } diff --git a/intern/ghost/intern/GHOST_XrSession.cpp b/intern/ghost/intern/GHOST_XrSession.cpp index 53d22cbd679..edc4960cf32 100644 --- a/intern/ghost/intern/GHOST_XrSession.cpp +++ b/intern/ghost/intern/GHOST_XrSession.cpp @@ -45,7 +45,7 @@ struct OpenXRSessionData { XrSpace reference_space; XrSpace view_space; std::vector<XrView> views; - std::vector<std::unique_ptr<GHOST_XrSwapchain>> swapchains; + std::vector<GHOST_XrSwapchain> swapchains; }; struct GHOST_XrDrawInfo { @@ -76,6 +76,9 @@ GHOST_XrSession::~GHOST_XrSession() if (m_oxr->reference_space != XR_NULL_HANDLE) { CHECK_XR_ASSERT(xrDestroySpace(m_oxr->reference_space)); } + if (m_oxr->view_space != XR_NULL_HANDLE) { + CHECK_XR_ASSERT(xrDestroySpace(m_oxr->view_space)); + } if (m_oxr->session != XR_NULL_HANDLE) { CHECK_XR_ASSERT(xrDestroySession(m_oxr->session)); } @@ -169,7 +172,8 @@ void GHOST_XrSession::start(const GHOST_XrSessionBeginInfo *begin_info) } std::string requirement_str; - m_gpu_binding = GHOST_XrGraphicsBindingCreateFromType(m_context->getGraphicsBindingType()); + m_gpu_binding = GHOST_XrGraphicsBindingCreateFromType(m_context->getGraphicsBindingType(), + m_gpu_ctx); if (!m_gpu_binding->checkVersionRequirements( m_gpu_ctx, m_context->getInstance(), m_oxr->system_id, &requirement_str)) { std::ostringstream strstream; @@ -199,13 +203,17 @@ void GHOST_XrSession::requestEnd() xrRequestExitSession(m_oxr->session); } -void GHOST_XrSession::end() +void GHOST_XrSession::beginSession() { - assert(m_oxr->session != XR_NULL_HANDLE); + XrSessionBeginInfo begin_info = {XR_TYPE_SESSION_BEGIN_INFO}; + begin_info.primaryViewConfigurationType = m_oxr->view_type; + CHECK_XR(xrBeginSession(m_oxr->session, &begin_info), "Failed to cleanly begin the VR session."); +} +void GHOST_XrSession::endSession() +{ + assert(m_oxr->session != XR_NULL_HANDLE); CHECK_XR(xrEndSession(m_oxr->session), "Failed to cleanly end the VR session."); - unbindGraphicsContext(); - m_draw_info = nullptr; } GHOST_XrSession::LifeExpectancy GHOST_XrSession::handleStateChangeEvent( @@ -218,16 +226,11 @@ GHOST_XrSession::LifeExpectancy GHOST_XrSession::handleStateChangeEvent( switch (lifecycle->state) { case XR_SESSION_STATE_READY: { - XrSessionBeginInfo begin_info = {XR_TYPE_SESSION_BEGIN_INFO}; - - begin_info.primaryViewConfigurationType = m_oxr->view_type; - CHECK_XR(xrBeginSession(m_oxr->session, &begin_info), - "Failed to cleanly begin the VR session."); + beginSession(); break; } case XR_SESSION_STATE_STOPPING: - /* Runtime will change state to STATE_EXITING, don't destruct session yet. */ - end(); + endSession(); break; case XR_SESSION_STATE_EXITING: case XR_SESSION_STATE_LOSS_PENDING: @@ -264,8 +267,7 @@ void GHOST_XrSession::prepareDrawing() "Failed to get count of view configurations."); for (const XrViewConfigurationView &view_config : view_configs) { - m_oxr->swapchains.push_back(std::unique_ptr<GHOST_XrSwapchain>( - new GHOST_XrSwapchain(*m_gpu_binding, m_oxr->session, view_config))); + m_oxr->swapchains.emplace_back(*m_gpu_binding, m_oxr->session, view_config); } m_oxr->views.resize(view_count, {XR_TYPE_VIEW}); @@ -440,7 +442,7 @@ XrCompositionLayerProjection GHOST_XrSession::drawLayer( r_proj_layer_views.resize(view_count); for (uint32_t view_idx = 0; view_idx < view_count; view_idx++) { - drawView(*m_oxr->swapchains[view_idx], + drawView(m_oxr->swapchains[view_idx], r_proj_layer_views[view_idx], view_location, m_oxr->views[view_idx], @@ -454,6 +456,11 @@ XrCompositionLayerProjection GHOST_XrSession::drawLayer( return layer; } +bool GHOST_XrSession::needsUpsideDownDrawing() const +{ + return m_gpu_binding && m_gpu_binding->needsUpsideDownDrawing(*m_gpu_ctx); +} + /** \} */ /* Drawing */ /* -------------------------------------------------------------------- */ @@ -493,16 +500,14 @@ void GHOST_XrSession::bindGraphicsContext() { const GHOST_XrCustomFuncs &custom_funcs = m_context->getCustomFuncs(); assert(custom_funcs.gpu_ctx_bind_fn); - m_gpu_ctx = static_cast<GHOST_Context *>( - custom_funcs.gpu_ctx_bind_fn(m_context->getGraphicsBindingType())); + m_gpu_ctx = static_cast<GHOST_Context *>(custom_funcs.gpu_ctx_bind_fn()); } void GHOST_XrSession::unbindGraphicsContext() { const GHOST_XrCustomFuncs &custom_funcs = m_context->getCustomFuncs(); if (custom_funcs.gpu_ctx_unbind_fn) { - custom_funcs.gpu_ctx_unbind_fn(m_context->getGraphicsBindingType(), - (GHOST_ContextHandle)m_gpu_ctx); + custom_funcs.gpu_ctx_unbind_fn((GHOST_ContextHandle)m_gpu_ctx); } m_gpu_ctx = nullptr; } diff --git a/intern/ghost/intern/GHOST_XrSession.h b/intern/ghost/intern/GHOST_XrSession.h index 9c1ed3756d4..da0128b2851 100644 --- a/intern/ghost/intern/GHOST_XrSession.h +++ b/intern/ghost/intern/GHOST_XrSession.h @@ -47,6 +47,7 @@ class GHOST_XrSession { LifeExpectancy handleStateChangeEvent(const XrEventDataSessionStateChanged *lifecycle); bool isRunning() const; + bool needsUpsideDownDrawing() const; void unbindGraphicsContext(); /* Public so context can ensure it's unbound as needed. */ @@ -67,7 +68,8 @@ class GHOST_XrSession { std::unique_ptr<GHOST_XrDrawInfo> m_draw_info; void initSystem(); - void end(); + void beginSession(); + void endSession(); void bindGraphicsContext(); diff --git a/intern/ghost/intern/GHOST_XrSwapchain.cpp b/intern/ghost/intern/GHOST_XrSwapchain.cpp index 4c91bcc2dc3..f50cfde0687 100644 --- a/intern/ghost/intern/GHOST_XrSwapchain.cpp +++ b/intern/ghost/intern/GHOST_XrSwapchain.cpp @@ -92,9 +92,19 @@ GHOST_XrSwapchain::GHOST_XrSwapchain(GHOST_IXrGraphicsBinding &gpu_binding, m_oxr->swapchain_images = swapchain_images_create(m_oxr->swapchain, gpu_binding); } +GHOST_XrSwapchain::GHOST_XrSwapchain(GHOST_XrSwapchain &&other) + : m_oxr(std::move(other.m_oxr)), + m_image_width(other.m_image_width), + m_image_height(other.m_image_height) +{ + /* Prevent xrDestroySwapchain call for the moved out item. */ + other.m_oxr = nullptr; +} + GHOST_XrSwapchain::~GHOST_XrSwapchain() { - if (m_oxr->swapchain != XR_NULL_HANDLE) { + /* m_oxr may be NULL after move. */ + if (m_oxr && m_oxr->swapchain != XR_NULL_HANDLE) { CHECK_XR_ASSERT(xrDestroySwapchain(m_oxr->swapchain)); } } diff --git a/intern/ghost/intern/GHOST_XrSwapchain.h b/intern/ghost/intern/GHOST_XrSwapchain.h index df9cd924dd0..ab0a6736c9c 100644 --- a/intern/ghost/intern/GHOST_XrSwapchain.h +++ b/intern/ghost/intern/GHOST_XrSwapchain.h @@ -30,6 +30,7 @@ class GHOST_XrSwapchain { GHOST_XrSwapchain(GHOST_IXrGraphicsBinding &gpu_binding, const XrSession &session, const XrViewConfigurationView &view_config); + GHOST_XrSwapchain(GHOST_XrSwapchain &&other); ~GHOST_XrSwapchain(); XrSwapchainImageBaseHeader *acquireDrawableSwapchainImage(); diff --git a/intern/ghost/intern/GHOST_Xr_openxr_includes.h b/intern/ghost/intern/GHOST_Xr_openxr_includes.h index 925d6037750..9cac43b1549 100644 --- a/intern/ghost/intern/GHOST_Xr_openxr_includes.h +++ b/intern/ghost/intern/GHOST_Xr_openxr_includes.h @@ -42,7 +42,7 @@ #ifdef XR_USE_GRAPHICS_API_D3D12 # include <d3d12.h> #endif -#ifdef WITH_X11 +#ifdef WITH_GHOST_X11 # include <GL/glxew.h> #endif |