Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'intern/ghost/intern')
-rw-r--r--intern/ghost/intern/GHOST_Buttons.cpp4
-rw-r--r--intern/ghost/intern/GHOST_Buttons.h4
-rw-r--r--intern/ghost/intern/GHOST_C-api.cpp64
-rw-r--r--intern/ghost/intern/GHOST_Context.cpp16
-rw-r--r--intern/ghost/intern/GHOST_Context.h23
-rw-r--r--intern/ghost/intern/GHOST_ContextCGL.h2
-rw-r--r--intern/ghost/intern/GHOST_ContextCGL.mm175
-rw-r--r--intern/ghost/intern/GHOST_ContextD3D.cpp14
-rw-r--r--intern/ghost/intern/GHOST_ContextEGL.cpp106
-rw-r--r--intern/ghost/intern/GHOST_ContextEGL.h9
-rw-r--r--intern/ghost/intern/GHOST_ContextGLX.cpp16
-rw-r--r--intern/ghost/intern/GHOST_ContextGLX.h4
-rw-r--r--intern/ghost/intern/GHOST_ContextSDL.cpp2
-rw-r--r--intern/ghost/intern/GHOST_ContextWGL.cpp406
-rw-r--r--intern/ghost/intern/GHOST_ContextWGL.h5
-rw-r--r--intern/ghost/intern/GHOST_Debug.h15
-rw-r--r--intern/ghost/intern/GHOST_DisplayManagerNULL.h26
-rw-r--r--intern/ghost/intern/GHOST_DisplayManagerWin32.cpp52
-rw-r--r--intern/ghost/intern/GHOST_DropTargetWin32.cpp219
-rw-r--r--intern/ghost/intern/GHOST_DropTargetWin32.h33
-rw-r--r--intern/ghost/intern/GHOST_DropTargetX11.cpp85
-rw-r--r--intern/ghost/intern/GHOST_DropTargetX11.h8
-rw-r--r--intern/ghost/intern/GHOST_Event.h2
-rw-r--r--intern/ghost/intern/GHOST_EventButton.h2
-rw-r--r--intern/ghost/intern/GHOST_EventKey.h17
-rw-r--r--intern/ghost/intern/GHOST_ISystem.cpp81
-rw-r--r--intern/ghost/intern/GHOST_IXrGraphicsBinding.h3
-rw-r--r--intern/ghost/intern/GHOST_ImeWin32.cpp2
-rw-r--r--intern/ghost/intern/GHOST_ImeWin32.h2
-rw-r--r--intern/ghost/intern/GHOST_ModifierKeys.cpp6
-rw-r--r--intern/ghost/intern/GHOST_ModifierKeys.h6
-rw-r--r--intern/ghost/intern/GHOST_NDOFManager.cpp6
-rw-r--r--intern/ghost/intern/GHOST_NDOFManagerWin32.cpp6
-rw-r--r--intern/ghost/intern/GHOST_Path-api.cpp4
-rw-r--r--intern/ghost/intern/GHOST_PathUtils.cpp101
-rw-r--r--intern/ghost/intern/GHOST_PathUtils.h24
-rw-r--r--intern/ghost/intern/GHOST_System.cpp40
-rw-r--r--intern/ghost/intern/GHOST_System.h19
-rw-r--r--intern/ghost/intern/GHOST_SystemCocoa.h4
-rw-r--r--intern/ghost/intern/GHOST_SystemCocoa.mm32
-rw-r--r--intern/ghost/intern/GHOST_SystemHeadless.h166
-rw-r--r--intern/ghost/intern/GHOST_SystemNULL.h122
-rw-r--r--intern/ghost/intern/GHOST_SystemSDL.cpp279
-rw-r--r--intern/ghost/intern/GHOST_SystemSDL.h4
-rw-r--r--intern/ghost/intern/GHOST_SystemWayland.cpp3276
-rw-r--r--intern/ghost/intern/GHOST_SystemWayland.h109
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.cpp826
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.h52
-rw-r--r--intern/ghost/intern/GHOST_SystemX11.cpp336
-rw-r--r--intern/ghost/intern/GHOST_SystemX11.h2
-rw-r--r--intern/ghost/intern/GHOST_TimerManager.cpp12
-rw-r--r--intern/ghost/intern/GHOST_TimerManager.h2
-rw-r--r--intern/ghost/intern/GHOST_WaylandCursorSettings.h15
-rw-r--r--intern/ghost/intern/GHOST_WaylandUtils.h19
-rw-r--r--intern/ghost/intern/GHOST_Window.cpp20
-rw-r--r--intern/ghost/intern/GHOST_Window.h9
-rw-r--r--intern/ghost/intern/GHOST_WindowManager.cpp14
-rw-r--r--intern/ghost/intern/GHOST_WindowManager.h6
-rw-r--r--intern/ghost/intern/GHOST_WindowNULL.h84
-rw-r--r--intern/ghost/intern/GHOST_WindowSDL.cpp1
-rw-r--r--intern/ghost/intern/GHOST_WindowWayland.cpp636
-rw-r--r--intern/ghost/intern/GHOST_WindowWayland.h39
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.cpp115
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.h6
-rw-r--r--intern/ghost/intern/GHOST_WindowX11.cpp330
-rw-r--r--intern/ghost/intern/GHOST_WindowX11.h2
-rw-r--r--intern/ghost/intern/GHOST_Wintab.cpp4
-rw-r--r--intern/ghost/intern/GHOST_Wintab.h4
-rw-r--r--intern/ghost/intern/GHOST_XrContext.cpp8
-rw-r--r--intern/ghost/intern/GHOST_XrGraphicsBinding.cpp124
-rw-r--r--intern/ghost/intern/GHOST_Xr_openxr_includes.h9
-rw-r--r--intern/ghost/intern/GHOST_utildefines.h210
-rw-r--r--intern/ghost/intern/GHOST_utildefines_variadic.h36
73 files changed, 5255 insertions, 3267 deletions
diff --git a/intern/ghost/intern/GHOST_Buttons.cpp b/intern/ghost/intern/GHOST_Buttons.cpp
index 3367d256325..6382729c579 100644
--- a/intern/ghost/intern/GHOST_Buttons.cpp
+++ b/intern/ghost/intern/GHOST_Buttons.cpp
@@ -12,7 +12,7 @@ GHOST_Buttons::GHOST_Buttons()
clear();
}
-bool GHOST_Buttons::get(GHOST_TButtonMask mask) const
+bool GHOST_Buttons::get(GHOST_TButton mask) const
{
switch (mask) {
case GHOST_kButtonMaskLeft:
@@ -34,7 +34,7 @@ bool GHOST_Buttons::get(GHOST_TButtonMask mask) const
}
}
-void GHOST_Buttons::set(GHOST_TButtonMask mask, bool down)
+void GHOST_Buttons::set(GHOST_TButton mask, bool down)
{
switch (mask) {
case GHOST_kButtonMaskLeft:
diff --git a/intern/ghost/intern/GHOST_Buttons.h b/intern/ghost/intern/GHOST_Buttons.h
index 72cb17a3322..e42376b1c76 100644
--- a/intern/ghost/intern/GHOST_Buttons.h
+++ b/intern/ghost/intern/GHOST_Buttons.h
@@ -27,14 +27,14 @@ struct GHOST_Buttons {
* \param mask: Key button to return.
* \return The state of the button (pressed == true).
*/
- bool get(GHOST_TButtonMask mask) const;
+ bool get(GHOST_TButton mask) const;
/**
* Updates the state of a single button.
* \param mask: Button state to update.
* \param down: The new state of the button.
*/
- void set(GHOST_TButtonMask mask, bool down);
+ void set(GHOST_TButton mask, bool down);
/**
* Sets the state of all buttons to up.
diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp
index c8127f59941..710512881e8 100644
--- a/intern/ghost/intern/GHOST_C-api.cpp
+++ b/intern/ghost/intern/GHOST_C-api.cpp
@@ -30,6 +30,14 @@ GHOST_SystemHandle GHOST_CreateSystem(void)
return (GHOST_SystemHandle)system;
}
+GHOST_SystemHandle GHOST_CreateSystemBackground(void)
+{
+ GHOST_ISystem::createSystemBackground();
+ GHOST_ISystem *system = GHOST_ISystem::getSystem();
+
+ return (GHOST_SystemHandle)system;
+}
+
void GHOST_SystemInitDebug(GHOST_SystemHandle systemhandle, GHOST_Debug debug)
{
GHOST_ISystem *system = (GHOST_ISystem *)systemhandle;
@@ -326,6 +334,14 @@ GHOST_TSuccess GHOST_SetCustomCursorShape(GHOST_WindowHandle windowhandle,
return window->setCustomCursorShape(bitmap, mask, sizex, sizey, hotX, hotY, canInvertColor);
}
+GHOST_TSuccess GHOST_GetCursorBitmap(GHOST_WindowHandle windowhandle,
+ GHOST_CursorBitmapRef *bitmap)
+{
+ GHOST_IWindow *window = (GHOST_IWindow *)windowhandle;
+
+ return window->getCursorBitmap(bitmap);
+}
+
bool GHOST_GetCursorVisibility(GHOST_WindowHandle windowhandle)
{
GHOST_IWindow *window = (GHOST_IWindow *)windowhandle;
@@ -340,19 +356,49 @@ GHOST_TSuccess GHOST_SetCursorVisibility(GHOST_WindowHandle windowhandle, bool v
return window->setCursorVisibility(visible);
}
-GHOST_TSuccess GHOST_GetCursorPosition(GHOST_SystemHandle systemhandle, int32_t *x, int32_t *y)
+/* Unused, can expose again if needed although WAYLAND
+ * can only properly use client relative coordinates, so leave disabled if possible. */
+#if 0
+GHOST_TSuccess GHOST_GetCursorPositionScreenCoords(GHOST_SystemHandle systemhandle,
+ int32_t *x,
+ int32_t *y)
{
GHOST_ISystem *system = (GHOST_ISystem *)systemhandle;
return system->getCursorPosition(*x, *y);
}
-GHOST_TSuccess GHOST_SetCursorPosition(GHOST_SystemHandle systemhandle, int32_t x, int32_t y)
+GHOST_TSuccess GHOST_SetCursorPositionScreenCoords(GHOST_SystemHandle systemhandle,
+ int32_t x,
+ int32_t y)
{
GHOST_ISystem *system = (GHOST_ISystem *)systemhandle;
return system->setCursorPosition(x, y);
}
+#endif
+
+GHOST_TSuccess GHOST_GetCursorPosition(const GHOST_SystemHandle systemhandle,
+ const GHOST_WindowHandle windowhandle,
+ int32_t *x,
+ int32_t *y)
+{
+ const GHOST_ISystem *system = (const GHOST_ISystem *)systemhandle;
+ const GHOST_IWindow *window = (const GHOST_IWindow *)windowhandle;
+
+ return system->getCursorPositionClientRelative(window, *x, *y);
+}
+
+GHOST_TSuccess GHOST_SetCursorPosition(GHOST_SystemHandle systemhandle,
+ GHOST_WindowHandle windowhandle,
+ int32_t x,
+ int32_t y)
+{
+ GHOST_ISystem *system = (GHOST_ISystem *)systemhandle;
+ GHOST_IWindow *window = (GHOST_IWindow *)windowhandle;
+
+ return system->setCursorPositionClientRelative(window, x, y);
+}
GHOST_TSuccess GHOST_SetCursorGrab(GHOST_WindowHandle windowhandle,
GHOST_TGrabCursorMode mode,
@@ -379,19 +425,22 @@ GHOST_TSuccess GHOST_SetCursorGrab(GHOST_WindowHandle windowhandle,
void GHOST_GetCursorGrabState(GHOST_WindowHandle windowhandle,
GHOST_TGrabCursorMode *r_mode,
GHOST_TAxisFlag *r_axis_flag,
- int r_bounds[4])
+ int r_bounds[4],
+ bool *r_use_software_cursor)
{
GHOST_IWindow *window = (GHOST_IWindow *)windowhandle;
GHOST_Rect bounds_rect;
- window->getCursorGrabState(*r_mode, *r_axis_flag, bounds_rect);
+ bool use_software_cursor;
+ window->getCursorGrabState(*r_mode, *r_axis_flag, bounds_rect, use_software_cursor);
r_bounds[0] = bounds_rect.m_l;
r_bounds[1] = bounds_rect.m_t;
r_bounds[2] = bounds_rect.m_r;
r_bounds[3] = bounds_rect.m_b;
+ *r_use_software_cursor = use_software_cursor;
}
GHOST_TSuccess GHOST_GetModifierKeyState(GHOST_SystemHandle systemhandle,
- GHOST_TModifierKeyMask mask,
+ GHOST_TModifierKey mask,
bool *r_is_down)
{
GHOST_ISystem *system = (GHOST_ISystem *)systemhandle;
@@ -405,7 +454,7 @@ GHOST_TSuccess GHOST_GetModifierKeyState(GHOST_SystemHandle systemhandle,
}
GHOST_TSuccess GHOST_GetButtonState(GHOST_SystemHandle systemhandle,
- GHOST_TButtonMask mask,
+ GHOST_TButton mask,
bool *r_is_down)
{
GHOST_ISystem *system = (GHOST_ISystem *)systemhandle;
@@ -820,8 +869,7 @@ void GHOST_putClipboard(const char *buffer, bool selection)
bool GHOST_setConsoleWindowState(GHOST_TConsoleWindowState action)
{
GHOST_ISystem *system = GHOST_ISystem::getSystem();
- /* FIXME: use `bool` instead of int for this value. */
- return (bool)system->setConsoleWindowState(action);
+ return system->setConsoleWindowState(action);
}
bool GHOST_UseNativePixels(void)
diff --git a/intern/ghost/intern/GHOST_Context.cpp b/intern/ghost/intern/GHOST_Context.cpp
index f9aa80dc13d..aa379efbc1f 100644
--- a/intern/ghost/intern/GHOST_Context.cpp
+++ b/intern/ghost/intern/GHOST_Context.cpp
@@ -10,7 +10,7 @@
#include "GHOST_Context.h"
#ifdef _WIN32
-# include <GL/wglew.h> // only for symbolic constants, do not use API functions
+# include <epoxy/wgl.h>
# include <tchar.h>
#
# ifndef ERROR_PROFILE_DOES_NOT_MATCH_DEVICE
@@ -35,7 +35,7 @@ bool win32_silent_chk(bool result)
bool win32_chk(bool result, const char *file, int line, const char *text)
{
if (!result) {
- LPTSTR formattedMsg = NULL;
+ LPTSTR formattedMsg = nullptr;
DWORD error = GetLastError();
@@ -87,12 +87,12 @@ bool win32_chk(bool result, const char *file, int line, const char *text)
default: {
count = FormatMessage((FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS),
- NULL,
+ nullptr,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)(&formattedMsg),
0,
- NULL);
+ nullptr);
msg = count > 0 ? formattedMsg : "<no system message>\n";
break;
@@ -113,8 +113,9 @@ bool win32_chk(bool result, const char *file, int line, const char *text)
SetLastError(NO_ERROR);
- if (count != 0)
+ if (count != 0) {
LocalFree(formattedMsg);
+ }
}
return result;
@@ -122,11 +123,6 @@ bool win32_chk(bool result, const char *file, int line, const char *text)
#endif // _WIN32
-void GHOST_Context::initContextGLEW()
-{
- GLEW_CHK(glewInit());
-}
-
void GHOST_Context::initClearGL()
{
glClearColor(0.294, 0.294, 0.294, 0.000);
diff --git a/intern/ghost/intern/GHOST_Context.h b/intern/ghost/intern/GHOST_Context.h
index d9c2cdce258..3546fb6bbc7 100644
--- a/intern/ghost/intern/GHOST_Context.h
+++ b/intern/ghost/intern/GHOST_Context.h
@@ -11,7 +11,7 @@
#include "GHOST_IContext.h"
#include "GHOST_Types.h"
-#include "glew-mx.h"
+#include <epoxy/gl.h>
#include <cstdlib> // for NULL
@@ -93,6 +93,22 @@ class GHOST_Context : public GHOST_IContext {
}
/**
+ * Get user data.
+ */
+ void *getUserData()
+ {
+ return m_user_data;
+ }
+
+ /**
+ * Set user data (intended for the caller to use as needed).
+ */
+ void setUserData(void *user_data)
+ {
+ m_user_data = user_data;
+ }
+
+ /**
* Stereo visual created. Only necessary for 'real' stereo support,
* ie quad buffered stereo. This is not always possible, depends on
* the graphics h/w
@@ -120,10 +136,11 @@ class GHOST_Context : public GHOST_IContext {
}
protected:
- void initContextGLEW();
-
bool m_stereoVisual;
+ /** Caller specified, not for internal use. */
+ void *m_user_data = nullptr;
+
static void initClearGL();
#ifdef WITH_CXX_GUARDEDALLOC
diff --git a/intern/ghost/intern/GHOST_ContextCGL.h b/intern/ghost/intern/GHOST_ContextCGL.h
index badc3241107..fa6d6fc6fa0 100644
--- a/intern/ghost/intern/GHOST_ContextCGL.h
+++ b/intern/ghost/intern/GHOST_ContextCGL.h
@@ -105,8 +105,6 @@ class GHOST_ContextCGL : public GHOST_Context {
/** The virtualized default frame-buffer's texture. */
MTLTexture *m_defaultFramebufferMetalTexture;
- bool m_coreProfile;
-
const bool m_debug;
/** The first created OpenGL context (for sharing display lists) */
diff --git a/intern/ghost/intern/GHOST_ContextCGL.mm b/intern/ghost/intern/GHOST_ContextCGL.mm
index dd800ef52a3..488aa58aa59 100644
--- a/intern/ghost/intern/GHOST_ContextCGL.mm
+++ b/intern/ghost/intern/GHOST_ContextCGL.mm
@@ -58,12 +58,6 @@ GHOST_ContextCGL::GHOST_ContextCGL(bool stereoVisual,
m_defaultFramebufferMetalTexture(nil),
m_debug(false)
{
-#if defined(WITH_GL_PROFILE_CORE)
- m_coreProfile = true;
-#else
- m_coreProfile = false;
-#endif
-
if (m_metalView) {
metalInit();
}
@@ -197,17 +191,17 @@ GHOST_TSuccess GHOST_ContextCGL::updateDrawingContext()
}
static void makeAttribList(std::vector<NSOpenGLPixelFormatAttribute> &attribs,
- bool coreProfile,
bool stereoVisual,
bool needAlpha,
- bool softwareGL)
+ bool softwareGL,
+ bool increasedSamplerLimit)
{
attribs.clear();
attribs.push_back(NSOpenGLPFAOpenGLProfile);
- attribs.push_back(coreProfile ? NSOpenGLProfileVersion3_2Core : NSOpenGLProfileVersionLegacy);
+ attribs.push_back(NSOpenGLProfileVersion3_2Core);
- /* Pixel Format Attributes for the windowed NSOpenGLContext. */
+ /* Pixel Format Attributes for the windowed #NSOpenGLContext. */
attribs.push_back(NSOpenGLPFADoubleBuffer);
if (softwareGL) {
@@ -217,6 +211,12 @@ static void makeAttribList(std::vector<NSOpenGLPixelFormatAttribute> &attribs,
else {
attribs.push_back(NSOpenGLPFAAccelerated);
attribs.push_back(NSOpenGLPFANoRecovery);
+
+ /* Attempt to initialize device with extended sampler limit.
+ * Resolves EEVEE purple rendering artifacts on macOS. */
+ if (increasedSamplerLimit) {
+ attribs.push_back((NSOpenGLPixelFormatAttribute)400);
+ }
}
if (stereoVisual)
@@ -232,82 +232,123 @@ static void makeAttribList(std::vector<NSOpenGLPixelFormatAttribute> &attribs,
GHOST_TSuccess GHOST_ContextCGL::initializeDrawingContext()
{
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ @autoreleasepool {
#ifdef GHOST_OPENGL_ALPHA
- static const bool needAlpha = true;
+ static const bool needAlpha = true;
#else
- static const bool needAlpha = false;
+ static const bool needAlpha = false;
#endif
- /* Command-line argument would be better. */
- static bool softwareGL = getenv("BLENDER_SOFTWAREGL");
-
- std::vector<NSOpenGLPixelFormatAttribute> attribs;
- attribs.reserve(40);
- makeAttribList(attribs, m_coreProfile, m_stereoVisual, needAlpha, softwareGL);
+ /* Command-line argument would be better. */
+ static bool softwareGL = getenv("BLENDER_SOFTWAREGL");
+
+ NSOpenGLPixelFormat *pixelFormat = nil;
+ std::vector<NSOpenGLPixelFormatAttribute> attribs;
+ bool increasedSamplerLimit = false;
+
+ /* Attempt to initialize device with increased sampler limit.
+ * If this is unsupported and initialization fails, initialize GL Context as normal.
+ *
+ * NOTE: This is not available when using the SoftwareGL path, or for Intel-based
+ * platforms. */
+ if (!softwareGL) {
+ if (@available(macos 11.0, *)) {
+ increasedSamplerLimit = true;
+ }
+ }
+ const int max_ctx_attempts = increasedSamplerLimit ? 2 : 1;
+ for (int ctx_create_attempt = 0; ctx_create_attempt < max_ctx_attempts; ctx_create_attempt++) {
+
+ attribs.clear();
+ attribs.reserve(40);
+ makeAttribList(attribs, m_stereoVisual, needAlpha, softwareGL, increasedSamplerLimit);
+
+ pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:&attribs[0]];
+ if (pixelFormat == nil) {
+ /* If pixel format creation fails when testing increased sampler limit,
+ * attempt initialization again with feature disabled, otherwise, fail. */
+ if (increasedSamplerLimit) {
+ increasedSamplerLimit = false;
+ continue;
+ }
+ return GHOST_kFailure;
+ }
- NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:&attribs[0]];
- if (pixelFormat == nil) {
- goto error;
- }
+ /* Attempt to create context. */
+ m_openGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
+ shareContext:s_sharedOpenGLContext];
+ [pixelFormat release];
+
+ if (m_openGLContext == nil) {
+ /* If context creation fails when testing increased sampler limit,
+ * attempt re-creation with feature disabled. Otherwise, error. */
+ if (increasedSamplerLimit) {
+ increasedSamplerLimit = false;
+ continue;
+ }
+
+ /* Default context creation attempt failed. */
+ return GHOST_kFailure;
+ }
- m_openGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
- shareContext:s_sharedOpenGLContext];
- [pixelFormat release];
+ /* Created GL context successfully, activate. */
+ [m_openGLContext makeCurrentContext];
- [m_openGLContext makeCurrentContext];
+ /* When increasing sampler limit, verify created context is a supported configuration. */
+ if (increasedSamplerLimit) {
+ const char *vendor = (const char *)glGetString(GL_VENDOR);
+ const char *renderer = (const char *)glGetString(GL_RENDERER);
+
+ /* If generated context type is unsupported, release existing context and
+ * fallback to creating a normal context below. */
+ if (strstr(vendor, "Intel") || strstr(renderer, "Software")) {
+ [m_openGLContext release];
+ m_openGLContext = nil;
+ increasedSamplerLimit = false;
+ continue;
+ }
+ }
+ }
- if (m_debug) {
- GLint major = 0, minor = 0;
- glGetIntegerv(GL_MAJOR_VERSION, &major);
- glGetIntegerv(GL_MINOR_VERSION, &minor);
- fprintf(stderr, "OpenGL version %d.%d%s\n", major, minor, softwareGL ? " (software)" : "");
- fprintf(stderr, "Renderer: %s\n", glGetString(GL_RENDERER));
- }
+ if (m_debug) {
+ GLint major = 0, minor = 0;
+ glGetIntegerv(GL_MAJOR_VERSION, &major);
+ glGetIntegerv(GL_MINOR_VERSION, &minor);
+ fprintf(stderr, "OpenGL version %d.%d%s\n", major, minor, softwareGL ? " (software)" : "");
+ fprintf(stderr, "Renderer: %s\n", glGetString(GL_RENDERER));
+ }
#ifdef GHOST_WAIT_FOR_VSYNC
- {
- GLint swapInt = 1;
- /* Wait for vertical-sync, to avoid tearing artifacts. */
- [m_openGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
- }
+ {
+ GLint swapInt = 1;
+ /* Wait for vertical-sync, to avoid tearing artifacts. */
+ [m_openGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
+ }
#endif
- initContextGLEW();
-
- if (m_metalView) {
- if (m_defaultFramebuffer == 0) {
- /* Create a virtual frame-buffer. */
- [m_openGLContext makeCurrentContext];
- metalInitFramebuffer();
+ if (m_metalView) {
+ if (m_defaultFramebuffer == 0) {
+ /* Create a virtual frame-buffer. */
+ [m_openGLContext makeCurrentContext];
+ metalInitFramebuffer();
+ initClearGL();
+ }
+ }
+ else if (m_openGLView) {
+ [m_openGLView setOpenGLContext:m_openGLContext];
+ [m_openGLContext setView:m_openGLView];
initClearGL();
}
- }
- else if (m_openGLView) {
- [m_openGLView setOpenGLContext:m_openGLContext];
- [m_openGLContext setView:m_openGLView];
- initClearGL();
- }
-
- [m_openGLContext flushBuffer];
- if (s_sharedCount == 0)
- s_sharedOpenGLContext = m_openGLContext;
+ [m_openGLContext flushBuffer];
- s_sharedCount++;
-
- [pool drain];
+ if (s_sharedCount == 0)
+ s_sharedOpenGLContext = m_openGLContext;
+ s_sharedCount++;
+ }
return GHOST_kSuccess;
-
-error:
-
- [pixelFormat release];
-
- [pool drain];
-
- return GHOST_kFailure;
}
GHOST_TSuccess GHOST_ContextCGL::releaseNativeHandles()
diff --git a/intern/ghost/intern/GHOST_ContextD3D.cpp b/intern/ghost/intern/GHOST_ContextD3D.cpp
index ded76daa145..857323941ea 100644
--- a/intern/ghost/intern/GHOST_ContextD3D.cpp
+++ b/intern/ghost/intern/GHOST_ContextD3D.cpp
@@ -10,8 +10,7 @@
#include <iostream>
#include <string>
-#include <GL/glew.h>
-#include <GL/wglew.h>
+#include <epoxy/wgl.h>
#include "GHOST_ContextD3D.h"
#include "GHOST_ContextWGL.h" /* For shared drawing */
@@ -124,8 +123,6 @@ class GHOST_SharedOpenGLResource {
ID3D11RenderTargetView *render_target = nullptr)
: m_device(device), m_device_ctx(device_ctx), m_cur_width(width), m_cur_height(height)
{
- ID3D11Resource *backbuffer_res;
-
if (!render_target) {
D3D11_TEXTURE2D_DESC texDesc{};
D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc{};
@@ -158,11 +155,12 @@ class GHOST_SharedOpenGLResource {
m_render_target = render_target;
if (m_render_target) {
+ ID3D11Resource *backbuffer_res = nullptr;
m_render_target->GetResource(&backbuffer_res);
- }
- if (backbuffer_res) {
- backbuffer_res->QueryInterface<ID3D11Texture2D>(&m_render_target_tex);
- backbuffer_res->Release();
+ if (backbuffer_res) {
+ backbuffer_res->QueryInterface<ID3D11Texture2D>(&m_render_target_tex);
+ backbuffer_res->Release();
+ }
}
if (!m_render_target || !m_render_target_tex) {
diff --git a/intern/ghost/intern/GHOST_ContextEGL.cpp b/intern/ghost/intern/GHOST_ContextEGL.cpp
index 8c44dfe0158..ef13133d3a3 100644
--- a/intern/ghost/intern/GHOST_ContextEGL.cpp
+++ b/intern/ghost/intern/GHOST_ContextEGL.cpp
@@ -151,19 +151,6 @@ static bool egl_chk(bool result,
# define EGL_CHK(x) egl_chk(x)
#endif
-static inline bool bindAPI(EGLenum api)
-{
- if (EGLEW_VERSION_1_2) {
- return (EGL_CHK(eglBindAPI(api)) == EGL_TRUE);
- }
-
- return false;
-}
-
-#ifdef WITH_GL_ANGLE
-HMODULE GHOST_ContextEGL::s_d3dcompiler = nullptr;
-#endif
-
EGLContext GHOST_ContextEGL::s_gl_sharedContext = EGL_NO_CONTEXT;
EGLint GHOST_ContextEGL::s_gl_sharedCount = 0;
@@ -256,7 +243,7 @@ GHOST_TSuccess GHOST_ContextEGL::swapBuffers()
GHOST_TSuccess GHOST_ContextEGL::setSwapInterval(int interval)
{
- if (EGLEW_VERSION_1_1) {
+ if (epoxy_egl_version(m_display) >= 11) {
if (EGL_CHK(::eglSwapInterval(m_display, interval))) {
m_swap_interval = interval;
@@ -313,26 +300,13 @@ GHOST_TSuccess GHOST_ContextEGL::releaseDrawingContext()
return GHOST_kFailure;
}
-bool GHOST_ContextEGL::initContextEGLEW()
+inline bool GHOST_ContextEGL::bindAPI(EGLenum api)
{
- /* We have to manually get this function before we can call eglewInit, since
- * it requires a display argument. glewInit() does the same, but we only want
- * to initialize EGLEW here. */
- eglGetDisplay = (PFNEGLGETDISPLAYPROC)eglGetProcAddress("eglGetDisplay");
- if (eglGetDisplay == nullptr) {
- return false;
- }
-
- if (!EGL_CHK((m_display = ::eglGetDisplay(m_nativeDisplay)) != EGL_NO_DISPLAY)) {
- return false;
- }
-
- if (GLEW_CHK(eglewInit(m_display)) != GLEW_OK) {
- fprintf(stderr, "Warning! EGLEW failed to initialize properly.\n");
- return false;
+ if (epoxy_egl_version(m_display) >= 12) {
+ return (EGL_CHK(eglBindAPI(api)) == EGL_TRUE);
}
- return true;
+ return false;
}
static const std::string &api_string(EGLenum api)
@@ -355,34 +329,41 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
}
m_stereoVisual = false; /* It doesn't matter what the Window wants. */
- if (!initContextEGLEW()) {
- return GHOST_kFailure;
- }
-
-#ifdef WITH_GL_ANGLE
- /* `d3dcompiler_XX.dll` needs to be loaded before ANGLE will work. */
- if (s_d3dcompiler == nullptr) {
- s_d3dcompiler = LoadLibrary(D3DCOMPILER);
-
- WIN32_CHK(s_d3dcompiler != nullptr);
-
- if (s_d3dcompiler == nullptr) {
- fprintf(stderr, "LoadLibrary(\"" D3DCOMPILER "\") failed!\n");
- return GHOST_kFailure;
- }
- }
-#endif
-
EGLDisplay prev_display = eglGetCurrentDisplay();
EGLSurface prev_draw = eglGetCurrentSurface(EGL_DRAW);
EGLSurface prev_read = eglGetCurrentSurface(EGL_READ);
EGLContext prev_context = eglGetCurrentContext();
- EGLint egl_major, egl_minor;
+ EGLint egl_major = 0, egl_minor = 0;
- if (!EGL_CHK(::eglInitialize(m_display, &egl_major, &egl_minor))) {
+ if (!EGL_CHK((m_display = ::eglGetDisplay(m_nativeDisplay)) != EGL_NO_DISPLAY)) {
goto error;
}
+
+ if (!EGL_CHK(::eglInitialize(m_display, &egl_major, &egl_minor)) ||
+ (egl_major == 0 && egl_minor == 0)) {
+ /* We failed to create a regular render window, retry and see if we can create a headless
+ * render context. */
+ ::eglTerminate(m_display);
+
+ const char *egl_extension_st = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+ assert(egl_extension_st != nullptr);
+ assert(strstr(egl_extension_st, "EGL_MESA_platform_surfaceless") != nullptr);
+ if (egl_extension_st == nullptr ||
+ strstr(egl_extension_st, "EGL_MESA_platform_surfaceless") == nullptr) {
+ goto error;
+ }
+
+ m_display = eglGetPlatformDisplayEXT(
+ EGL_PLATFORM_SURFACELESS_MESA, EGL_DEFAULT_DISPLAY, nullptr);
+
+ if (!EGL_CHK(::eglInitialize(m_display, &egl_major, &egl_minor))) {
+ goto error;
+ }
+ /* Because the first eglInitialize will print an error to the terminal, print a "success"
+ * message here to let the user know that we successfully recovered from the error. */
+ fprintf(stderr, "\nManaged to successfully fallback to surfaceless EGL rendering!\n\n");
+ }
#ifdef WITH_GHOST_DEBUG
fprintf(stderr, "EGL Version %d.%d\n", egl_major, egl_minor);
#endif
@@ -398,7 +379,7 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
attrib_list.reserve(20);
- if (m_api == EGL_OPENGL_ES_API && EGLEW_VERSION_1_2) {
+ if (m_api == EGL_OPENGL_ES_API && epoxy_egl_version(m_display) >= 12) {
/* According to the spec it seems that you are required to set EGL_RENDERABLE_TYPE,
* but some implementations (ANGLE) do not seem to care. */
@@ -421,9 +402,11 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
m_contextMinorVersion);
}
- if (!((m_contextMajorVersion == 1) || (m_contextMajorVersion == 2 && EGLEW_VERSION_1_3) ||
- (m_contextMajorVersion == 3 && /*EGLEW_VERSION_1_4 &&*/ EGLEW_KHR_create_context) ||
- (m_contextMajorVersion == 3 && EGLEW_VERSION_1_5))) {
+ if (!((m_contextMajorVersion == 1) ||
+ (m_contextMajorVersion == 2 && epoxy_egl_version(m_display) >= 13) ||
+ (m_contextMajorVersion == 3 &&
+ epoxy_has_egl_extension(m_display, "KHR_create_context")) ||
+ (m_contextMajorVersion == 3 && epoxy_egl_version(m_display) >= 15))) {
fprintf(stderr,
"Warning! May not be able to create a version %d.%d ES context with version %d.%d "
"of EGL\n",
@@ -488,7 +471,8 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
}
attrib_list.clear();
- if (EGLEW_VERSION_1_5 || EGLEW_KHR_create_context) {
+ if (epoxy_egl_version(m_display) >= 15 ||
+ epoxy_has_egl_extension(m_display, "KHR_create_context")) {
if (m_api == EGL_OPENGL_API || m_api == EGL_OPENGL_ES_API) {
if (m_contextMajorVersion != 0) {
attrib_list.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR);
@@ -530,7 +514,7 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
}
}
- if (m_api == EGL_OPENGL_API || EGLEW_VERSION_1_5) {
+ if (m_api == EGL_OPENGL_API || epoxy_egl_version(m_display) >= 15) {
if (m_contextResetNotificationStrategy != 0) {
attrib_list.push_back(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR);
attrib_list.push_back(m_contextResetNotificationStrategy);
@@ -598,10 +582,10 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
goto error;
}
- initContextGLEW();
-
- initClearGL();
- ::eglSwapBuffers(m_display, m_surface);
+ if (m_nativeWindow != 0) {
+ initClearGL();
+ ::eglSwapBuffers(m_display, m_surface);
+ }
return GHOST_kSuccess;
diff --git a/intern/ghost/intern/GHOST_ContextEGL.h b/intern/ghost/intern/GHOST_ContextEGL.h
index 3250dc94978..3ccd34f0338 100644
--- a/intern/ghost/intern/GHOST_ContextEGL.h
+++ b/intern/ghost/intern/GHOST_ContextEGL.h
@@ -10,7 +10,8 @@
#include "GHOST_Context.h"
#include "GHOST_System.h"
-#include <GL/eglew.h>
+#include <epoxy/egl.h>
+#include <epoxy/gl.h>
#ifndef GHOST_OPENGL_EGL_CONTEXT_FLAGS
# define GHOST_OPENGL_EGL_CONTEXT_FLAGS 0
@@ -96,7 +97,7 @@ class GHOST_ContextEGL : public GHOST_Context {
EGLContext getContext() const;
private:
- bool initContextEGLEW();
+ bool bindAPI(EGLenum api);
const GHOST_System *const m_system;
@@ -129,8 +130,4 @@ class GHOST_ContextEGL : public GHOST_Context {
static EGLContext s_vg_sharedContext;
static EGLint s_vg_sharedCount;
-
-#ifdef WITH_GL_ANGLE
- static HMODULE s_d3dcompiler;
-#endif
};
diff --git a/intern/ghost/intern/GHOST_ContextGLX.cpp b/intern/ghost/intern/GHOST_ContextGLX.cpp
index b4a076e4598..ed1c874c236 100644
--- a/intern/ghost/intern/GHOST_ContextGLX.cpp
+++ b/intern/ghost/intern/GHOST_ContextGLX.cpp
@@ -95,11 +95,6 @@ GHOST_TSuccess GHOST_ContextGLX::releaseDrawingContext()
return ::glXMakeCurrent(m_display, None, nullptr) ? GHOST_kSuccess : GHOST_kFailure;
}
-void GHOST_ContextGLX::initContextGLXEW()
-{
- initContextGLEW();
-}
-
GHOST_TSuccess GHOST_ContextGLX::initializeDrawingContext()
{
GHOST_X11_ERROR_HANDLERS_OVERRIDE(handler_store);
@@ -278,18 +273,11 @@ GHOST_TSuccess GHOST_ContextGLX::initializeDrawingContext()
glXMakeCurrent(m_display, m_window, m_context);
- /* Seems that this has to be called after #glXMakeCurrent,
- * which means we cannot use `glX` extensions until after we create a context. */
- initContextGLXEW();
-
if (m_window) {
initClearGL();
::glXSwapBuffers(m_display, m_window);
}
- /* re initialize to get the extensions properly */
- initContextGLXEW();
-
version = glGetString(GL_VERSION);
if (!version || version[0] < '3' || ((version[0] == '3') && (version[2] < '3'))) {
@@ -318,7 +306,7 @@ GHOST_TSuccess GHOST_ContextGLX::releaseNativeHandles()
GHOST_TSuccess GHOST_ContextGLX::setSwapInterval(int interval)
{
- if (!GLXEW_EXT_swap_control) {
+ if (!epoxy_has_glx_extension(m_display, DefaultScreen(m_display), "GLX_EXT_swap_control")) {
::glXSwapIntervalEXT(m_display, m_window, interval);
return GHOST_kSuccess;
}
@@ -327,7 +315,7 @@ GHOST_TSuccess GHOST_ContextGLX::setSwapInterval(int interval)
GHOST_TSuccess GHOST_ContextGLX::getSwapInterval(int &intervalOut)
{
- if (GLXEW_EXT_swap_control) {
+ if (epoxy_has_glx_extension(m_display, DefaultScreen(m_display), "GLX_EXT_swap_control")) {
unsigned int interval = 0;
::glXQueryDrawable(m_display, m_window, GLX_SWAP_INTERVAL_EXT, &interval);
diff --git a/intern/ghost/intern/GHOST_ContextGLX.h b/intern/ghost/intern/GHOST_ContextGLX.h
index c6184bbd3da..d526e6b1b32 100644
--- a/intern/ghost/intern/GHOST_ContextGLX.h
+++ b/intern/ghost/intern/GHOST_ContextGLX.h
@@ -9,7 +9,7 @@
#include "GHOST_Context.h"
-#include <GL/glxew.h>
+#include <epoxy/glx.h>
#ifndef GHOST_OPENGL_GLX_CONTEXT_FLAGS
/* leave as convenience define for the future */
@@ -89,8 +89,6 @@ class GHOST_ContextGLX : public GHOST_Context {
GHOST_TSuccess getSwapInterval(int &intervalOut);
private:
- void initContextGLXEW();
-
Display *m_display;
GLXFBConfig m_fbconfig;
Window m_window;
diff --git a/intern/ghost/intern/GHOST_ContextSDL.cpp b/intern/ghost/intern/GHOST_ContextSDL.cpp
index 5b02fe1c1e6..63b5927895d 100644
--- a/intern/ghost/intern/GHOST_ContextSDL.cpp
+++ b/intern/ghost/intern/GHOST_ContextSDL.cpp
@@ -138,8 +138,6 @@ GHOST_TSuccess GHOST_ContextSDL::initializeDrawingContext()
success = (SDL_GL_MakeCurrent(m_window, m_context) < 0) ? GHOST_kFailure : GHOST_kSuccess;
- initContextGLEW();
-
initClearGL();
SDL_GL_SwapWindow(m_window);
diff --git a/intern/ghost/intern/GHOST_ContextWGL.cpp b/intern/ghost/intern/GHOST_ContextWGL.cpp
index 7417358e9ae..d3c190a13b1 100644
--- a/intern/ghost/intern/GHOST_ContextWGL.cpp
+++ b/intern/ghost/intern/GHOST_ContextWGL.cpp
@@ -87,7 +87,7 @@ GHOST_TSuccess GHOST_ContextWGL::swapBuffers()
GHOST_TSuccess GHOST_ContextWGL::setSwapInterval(int interval)
{
- if (WGLEW_EXT_swap_control)
+ if (epoxy_has_wgl_extension(m_hDC, "WGL_EXT_swap_control"))
return WIN32_CHK(::wglSwapIntervalEXT(interval)) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
else
return GHOST_kFailure;
@@ -95,7 +95,7 @@ GHOST_TSuccess GHOST_ContextWGL::setSwapInterval(int interval)
GHOST_TSuccess GHOST_ContextWGL::getSwapInterval(int &intervalOut)
{
- if (WGLEW_EXT_swap_control) {
+ if (epoxy_has_wgl_extension(m_hDC, "WGL_EXT_swap_control")) {
intervalOut = ::wglGetSwapIntervalEXT();
return GHOST_kSuccess;
}
@@ -266,89 +266,6 @@ static HWND clone_window(HWND hWnd, LPVOID lpParam)
return hwndCloned;
}
-void GHOST_ContextWGL::initContextWGLEW(PIXELFORMATDESCRIPTOR &preferredPFD)
-{
- HWND dummyHWND = NULL;
-
- HDC dummyHDC = NULL;
- HGLRC dummyHGLRC = NULL;
-
- HDC prevHDC;
- HGLRC prevHGLRC;
-
- int iPixelFormat;
-
- SetLastError(NO_ERROR);
-
- prevHDC = ::wglGetCurrentDC();
- WIN32_CHK(GetLastError() == NO_ERROR);
-
- prevHGLRC = ::wglGetCurrentContext();
- WIN32_CHK(GetLastError() == NO_ERROR);
-
- iPixelFormat = choose_pixel_format_legacy(m_hDC, preferredPFD);
-
- if (iPixelFormat == 0)
- goto finalize;
-
- PIXELFORMATDESCRIPTOR chosenPFD;
- if (!WIN32_CHK(
- ::DescribePixelFormat(m_hDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &chosenPFD)))
- goto finalize;
-
- if (m_hWnd) {
- dummyHWND = clone_window(m_hWnd, NULL);
-
- if (dummyHWND == NULL)
- goto finalize;
-
- dummyHDC = GetDC(dummyHWND);
- }
-
- if (!WIN32_CHK(dummyHDC != NULL))
- goto finalize;
-
- if (!WIN32_CHK(::SetPixelFormat(dummyHDC, iPixelFormat, &chosenPFD)))
- goto finalize;
-
- dummyHGLRC = ::wglCreateContext(dummyHDC);
-
- if (!WIN32_CHK(dummyHGLRC != NULL))
- goto finalize;
-
- if (!WIN32_CHK(::wglMakeCurrent(dummyHDC, dummyHGLRC)))
- goto finalize;
-
- if (GLEW_CHK(glewInit()) != GLEW_OK) {
- fprintf(stderr, "Warning! Dummy GLEW/WGLEW failed to initialize properly.\n");
- }
-
- /* The following are not technically WGLEW, but they also require a context to work. */
-
-#ifndef NDEBUG
- free((void *)m_dummyRenderer);
- free((void *)m_dummyVendor);
- free((void *)m_dummyVersion);
-
- m_dummyRenderer = _strdup(reinterpret_cast<const char *>(glGetString(GL_RENDERER)));
- m_dummyVendor = _strdup(reinterpret_cast<const char *>(glGetString(GL_VENDOR)));
- m_dummyVersion = _strdup(reinterpret_cast<const char *>(glGetString(GL_VERSION)));
-#endif
-
-finalize:
- WIN32_CHK(::wglMakeCurrent(prevHDC, prevHGLRC));
-
- if (dummyHGLRC != NULL)
- WIN32_CHK(::wglDeleteContext(dummyHGLRC));
-
- if (dummyHWND != NULL) {
- if (dummyHDC != NULL)
- WIN32_CHK(::ReleaseDC(dummyHWND, dummyHDC));
-
- WIN32_CHK(::DestroyWindow(dummyHWND));
- }
-}
-
static void makeAttribList(std::vector<int> &out, bool stereoVisual, bool needAlpha)
{
out.clear();
@@ -385,6 +302,130 @@ static void makeAttribList(std::vector<int> &out, bool stereoVisual, bool needAl
out.push_back(0);
}
+/* Temporary context used to create the actual context. We need ARB pixel format
+ * and context extensions, which are only available within a context. */
+struct DummyContextWGL {
+ HWND dummyHWND = NULL;
+
+ HDC dummyHDC = NULL;
+ HGLRC dummyHGLRC = NULL;
+
+ HDC prevHDC = NULL;
+ HGLRC prevHGLRC = NULL;
+
+ int dummyPixelFormat = 0;
+
+ PIXELFORMATDESCRIPTOR preferredPFD;
+
+ bool has_WGL_ARB_pixel_format = false;
+ bool has_WGL_ARB_create_context = false;
+ bool has_WGL_ARB_create_context_profile = false;
+ bool has_WGL_ARB_create_context_robustness = false;
+
+ DummyContextWGL(HDC hDC, HWND hWnd, bool stereoVisual, bool needAlpha)
+ {
+ preferredPFD = {
+ sizeof(PIXELFORMATDESCRIPTOR), /* size */
+ 1, /* version */
+ (DWORD)(PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW |
+ PFD_DOUBLEBUFFER | /* support double-buffering */
+ (stereoVisual ? PFD_STEREO : 0) | /* support stereo */
+ (
+#ifdef WIN32_COMPOSITING
+ /* Support composition for transparent background. */
+ needAlpha ? PFD_SUPPORT_COMPOSITION :
+#endif
+ 0)),
+ PFD_TYPE_RGBA, /* color type */
+ (BYTE)(needAlpha ? 32 : 24), /* preferred color depth */
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0, /* color bits (ignored) */
+ (BYTE)(needAlpha ? 8 : 0), /* alpha buffer */
+ 0, /* alpha shift (ignored) */
+ 0, /* no accumulation buffer */
+ 0,
+ 0,
+ 0,
+ 0, /* accum bits (ignored) */
+ 0, /* depth buffer */
+ 0, /* stencil buffer */
+ 0, /* no auxiliary buffers */
+ PFD_MAIN_PLANE, /* main layer */
+ 0, /* reserved */
+ 0,
+ 0,
+ 0 /* layer, visible, and damage masks (ignored) */
+ };
+
+ SetLastError(NO_ERROR);
+
+ prevHDC = ::wglGetCurrentDC();
+ WIN32_CHK(GetLastError() == NO_ERROR);
+
+ prevHGLRC = ::wglGetCurrentContext();
+ WIN32_CHK(GetLastError() == NO_ERROR);
+
+ dummyPixelFormat = choose_pixel_format_legacy(hDC, preferredPFD);
+
+ if (dummyPixelFormat == 0)
+ return;
+
+ PIXELFORMATDESCRIPTOR chosenPFD;
+ if (!WIN32_CHK(::DescribePixelFormat(
+ hDC, dummyPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &chosenPFD)))
+ return;
+
+ if (hWnd) {
+ dummyHWND = clone_window(hWnd, NULL);
+
+ if (dummyHWND == NULL)
+ return;
+
+ dummyHDC = GetDC(dummyHWND);
+ }
+
+ if (!WIN32_CHK(dummyHDC != NULL))
+ return;
+
+ if (!WIN32_CHK(::SetPixelFormat(dummyHDC, dummyPixelFormat, &chosenPFD)))
+ return;
+
+ dummyHGLRC = ::wglCreateContext(dummyHDC);
+
+ if (!WIN32_CHK(dummyHGLRC != NULL))
+ return;
+
+ if (!WIN32_CHK(::wglMakeCurrent(dummyHDC, dummyHGLRC)))
+ return;
+
+ has_WGL_ARB_pixel_format = epoxy_has_wgl_extension(hDC, "WGL_ARB_pixel_format");
+ has_WGL_ARB_create_context = epoxy_has_wgl_extension(hDC, "WGL_ARB_create_context");
+ has_WGL_ARB_create_context_profile = epoxy_has_wgl_extension(hDC,
+ "WGL_ARB_create_context_profile");
+ has_WGL_ARB_create_context_robustness = epoxy_has_wgl_extension(
+ hDC, "WGL_ARB_create_context_robustness");
+ }
+
+ ~DummyContextWGL()
+ {
+ WIN32_CHK(::wglMakeCurrent(prevHDC, prevHGLRC));
+
+ if (dummyHGLRC != NULL)
+ WIN32_CHK(::wglDeleteContext(dummyHGLRC));
+
+ if (dummyHWND != NULL) {
+ if (dummyHDC != NULL)
+ WIN32_CHK(::ReleaseDC(dummyHWND, dummyHDC));
+
+ WIN32_CHK(::DestroyWindow(dummyHWND));
+ }
+ }
+};
+
int GHOST_ContextWGL::_choose_pixel_format_arb_1(bool stereoVisual, bool needAlpha)
{
std::vector<int> iAttributes;
@@ -454,58 +495,6 @@ int GHOST_ContextWGL::choose_pixel_format_arb(bool stereoVisual, bool needAlpha)
return iPixelFormat;
}
-int GHOST_ContextWGL::choose_pixel_format(bool stereoVisual, bool needAlpha)
-{
- PIXELFORMATDESCRIPTOR preferredPFD = {
- sizeof(PIXELFORMATDESCRIPTOR), /* size */
- 1, /* version */
- (DWORD)(PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW |
- PFD_DOUBLEBUFFER | /* support double-buffering */
- (stereoVisual ? PFD_STEREO : 0) | /* support stereo */
- (
-#ifdef WIN32_COMPOSITING
- /* Support composition for transparent background. */
- needAlpha ? PFD_SUPPORT_COMPOSITION :
-#endif
- 0)),
- PFD_TYPE_RGBA, /* color type */
- (BYTE)(needAlpha ? 32 : 24), /* preferred color depth */
- 0,
- 0,
- 0,
- 0,
- 0,
- 0, /* color bits (ignored) */
- (BYTE)(needAlpha ? 8 : 0), /* alpha buffer */
- 0, /* alpha shift (ignored) */
- 0, /* no accumulation buffer */
- 0,
- 0,
- 0,
- 0, /* accum bits (ignored) */
- 0, /* depth buffer */
- 0, /* stencil buffer */
- 0, /* no auxiliary buffers */
- PFD_MAIN_PLANE, /* main layer */
- 0, /* reserved */
- 0,
- 0,
- 0 /* layer, visible, and damage masks (ignored) */
- };
-
- initContextWGLEW(preferredPFD);
-
- int iPixelFormat = 0;
-
- if (WGLEW_ARB_pixel_format)
- iPixelFormat = choose_pixel_format_arb(stereoVisual, needAlpha);
-
- if (iPixelFormat == 0)
- iPixelFormat = choose_pixel_format_legacy(m_hDC, preferredPFD);
-
- return iPixelFormat;
-}
-
#ifndef NDEBUG
static void reportContextString(const char *name, const char *dummy, const char *context)
{
@@ -526,107 +515,96 @@ GHOST_TSuccess GHOST_ContextWGL::initializeDrawingContext()
HDC prevHDC = ::wglGetCurrentDC();
WIN32_CHK(GetLastError() == NO_ERROR);
- if (!WGLEW_ARB_create_context || ::GetPixelFormat(m_hDC) == 0) {
+ {
const bool needAlpha = m_alphaBackground;
- int iPixelFormat;
- int lastPFD;
-
- PIXELFORMATDESCRIPTOR chosenPFD;
-
- iPixelFormat = choose_pixel_format(m_stereoVisual, needAlpha);
+ DummyContextWGL dummy(m_hDC, m_hWnd, m_stereoVisual, needAlpha);
- if (iPixelFormat == 0) {
- goto error;
- }
+ if (!dummy.has_WGL_ARB_create_context || ::GetPixelFormat(m_hDC) == 0) {
+ int iPixelFormat = 0;
- lastPFD = ::DescribePixelFormat(
- m_hDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &chosenPFD);
-
- if (!WIN32_CHK(lastPFD != 0)) {
- goto error;
- }
+ if (dummy.has_WGL_ARB_pixel_format)
+ iPixelFormat = choose_pixel_format_arb(m_stereoVisual, needAlpha);
- if (needAlpha && chosenPFD.cAlphaBits == 0)
- fprintf(stderr, "Warning! Unable to find a pixel format with an alpha channel.\n");
+ if (iPixelFormat == 0)
+ iPixelFormat = choose_pixel_format_legacy(m_hDC, dummy.preferredPFD);
- if (!WIN32_CHK(::SetPixelFormat(m_hDC, iPixelFormat, &chosenPFD))) {
- goto error;
- }
- }
+ if (iPixelFormat == 0) {
+ goto error;
+ }
- if (WGLEW_ARB_create_context) {
- int profileBitCore = m_contextProfileMask & WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
- int profileBitCompat = m_contextProfileMask & WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
+ PIXELFORMATDESCRIPTOR chosenPFD;
+ int lastPFD = ::DescribePixelFormat(
+ m_hDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &chosenPFD);
-#ifdef WITH_GLEW_ES
- int profileBitES = m_contextProfileMask & WGL_CONTEXT_ES_PROFILE_BIT_EXT;
-#endif
+ if (!WIN32_CHK(lastPFD != 0)) {
+ goto error;
+ }
- if (!WGLEW_ARB_create_context_profile && profileBitCore)
- fprintf(stderr, "Warning! OpenGL core profile not available.\n");
+ if (needAlpha && chosenPFD.cAlphaBits == 0)
+ fprintf(stderr, "Warning! Unable to find a pixel format with an alpha channel.\n");
- if (!WGLEW_ARB_create_context_profile && profileBitCompat)
- fprintf(stderr, "Warning! OpenGL compatibility profile not available.\n");
+ if (!WIN32_CHK(::SetPixelFormat(m_hDC, iPixelFormat, &chosenPFD))) {
+ goto error;
+ }
+ }
-#ifdef WITH_GLEW_ES
- if (!WGLEW_EXT_create_context_es_profile && profileBitES && m_contextMajorVersion == 1)
- fprintf(stderr, "Warning! OpenGL ES profile not available.\n");
+ if (dummy.has_WGL_ARB_create_context) {
+ int profileBitCore = m_contextProfileMask & WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
+ int profileBitCompat = m_contextProfileMask & WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
- if (!WGLEW_EXT_create_context_es2_profile && profileBitES && m_contextMajorVersion == 2)
- fprintf(stderr, "Warning! OpenGL ES2 profile not available.\n");
-#endif
+ if (!dummy.has_WGL_ARB_create_context_profile && profileBitCore)
+ fprintf(stderr, "Warning! OpenGL core profile not available.\n");
- int profileMask = 0;
+ if (!dummy.has_WGL_ARB_create_context_profile && profileBitCompat)
+ fprintf(stderr, "Warning! OpenGL compatibility profile not available.\n");
- if (WGLEW_ARB_create_context_profile && profileBitCore)
- profileMask |= profileBitCore;
+ int profileMask = 0;
- if (WGLEW_ARB_create_context_profile && profileBitCompat)
- profileMask |= profileBitCompat;
+ if (dummy.has_WGL_ARB_create_context_profile && profileBitCore)
+ profileMask |= profileBitCore;
-#ifdef WITH_GLEW_ES
- if (WGLEW_EXT_create_context_es_profile && profileBitES)
- profileMask |= profileBitES;
-#endif
+ if (dummy.has_WGL_ARB_create_context_profile && profileBitCompat)
+ profileMask |= profileBitCompat;
- if (profileMask != m_contextProfileMask)
- fprintf(stderr, "Warning! Ignoring untested OpenGL context profile mask bits.");
+ if (profileMask != m_contextProfileMask)
+ fprintf(stderr, "Warning! Ignoring untested OpenGL context profile mask bits.");
- std::vector<int> iAttributes;
+ std::vector<int> iAttributes;
- if (profileMask) {
- iAttributes.push_back(WGL_CONTEXT_PROFILE_MASK_ARB);
- iAttributes.push_back(profileMask);
- }
-
- if (m_contextMajorVersion != 0) {
- iAttributes.push_back(WGL_CONTEXT_MAJOR_VERSION_ARB);
- iAttributes.push_back(m_contextMajorVersion);
- }
+ if (profileMask) {
+ iAttributes.push_back(WGL_CONTEXT_PROFILE_MASK_ARB);
+ iAttributes.push_back(profileMask);
+ }
- if (m_contextMinorVersion != 0) {
- iAttributes.push_back(WGL_CONTEXT_MINOR_VERSION_ARB);
- iAttributes.push_back(m_contextMinorVersion);
- }
+ if (m_contextMajorVersion != 0) {
+ iAttributes.push_back(WGL_CONTEXT_MAJOR_VERSION_ARB);
+ iAttributes.push_back(m_contextMajorVersion);
+ }
- if (m_contextFlags != 0) {
- iAttributes.push_back(WGL_CONTEXT_FLAGS_ARB);
- iAttributes.push_back(m_contextFlags);
- }
+ if (m_contextMinorVersion != 0) {
+ iAttributes.push_back(WGL_CONTEXT_MINOR_VERSION_ARB);
+ iAttributes.push_back(m_contextMinorVersion);
+ }
- if (m_contextResetNotificationStrategy != 0) {
- if (WGLEW_ARB_create_context_robustness) {
- iAttributes.push_back(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB);
- iAttributes.push_back(m_contextResetNotificationStrategy);
+ if (m_contextFlags != 0) {
+ iAttributes.push_back(WGL_CONTEXT_FLAGS_ARB);
+ iAttributes.push_back(m_contextFlags);
}
- else {
- fprintf(stderr, "Warning! Cannot set the reset notification strategy.");
+
+ if (m_contextResetNotificationStrategy != 0) {
+ if (dummy.has_WGL_ARB_create_context_robustness) {
+ iAttributes.push_back(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB);
+ iAttributes.push_back(m_contextResetNotificationStrategy);
+ }
+ else {
+ fprintf(stderr, "Warning! Cannot set the reset notification strategy.");
+ }
}
- }
- iAttributes.push_back(0);
+ iAttributes.push_back(0);
- m_hGLRC = ::wglCreateContextAttribsARB(m_hDC, NULL, &(iAttributes[0]));
+ m_hGLRC = ::wglCreateContextAttribsARB(m_hDC, NULL, &(iAttributes[0]));
+ }
}
/* Silence warnings interpreted as errors by users when trying to get
@@ -651,8 +629,6 @@ GHOST_TSuccess GHOST_ContextWGL::initializeDrawingContext()
goto error;
}
- initContextGLEW();
-
if (is_crappy_intel_card()) {
/* Some Intel cards with context 4.1 or 4.2
* don't have the point sprite enabled by default.
diff --git a/intern/ghost/intern/GHOST_ContextWGL.h b/intern/ghost/intern/GHOST_ContextWGL.h
index ca0bf70b128..c02c0616422 100644
--- a/intern/ghost/intern/GHOST_ContextWGL.h
+++ b/intern/ghost/intern/GHOST_ContextWGL.h
@@ -11,7 +11,7 @@
#include "GHOST_Context.h"
-#include <GL/wglew.h>
+#include <epoxy/wgl.h>
#ifndef GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY
# define GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY 0
@@ -86,12 +86,9 @@ class GHOST_ContextWGL : public GHOST_Context {
GHOST_TSuccess getSwapInterval(int &intervalOut);
private:
- int choose_pixel_format(bool stereoVisual, bool needAlpha);
int choose_pixel_format_arb(bool stereoVisual, bool needAlpha);
int _choose_pixel_format_arb_1(bool stereoVisual, bool needAlpha);
- void initContextWGLEW(PIXELFORMATDESCRIPTOR &preferredPFD);
-
HWND m_hWnd;
HDC m_hDC;
diff --git a/intern/ghost/intern/GHOST_Debug.h b/intern/ghost/intern/GHOST_Debug.h
index e6bdf974d59..ec1a0b34be6 100644
--- a/intern/ghost/intern/GHOST_Debug.h
+++ b/intern/ghost/intern/GHOST_Debug.h
@@ -15,12 +15,12 @@
# endif
#endif
-#ifdef WITH_GHOST_DEBUG
+#if defined(WITH_GHOST_DEBUG) || (!defined(NDEBUG))
# include <iostream>
# include <stdio.h> //for printf()
#endif // WITH_GHOST_DEBUG
-#ifdef WITH_GHOST_DEBUG
+#if defined(WITH_GHOST_DEBUG)
# define GHOST_PRINT(x) \
{ \
std::cout << x; \
@@ -31,10 +31,10 @@
printf(x, __VA_ARGS__); \
} \
(void)0
-#else // WITH_GHOST_DEBUG
+#else
# define GHOST_PRINT(x)
# define GHOST_PRINTF(x, ...)
-#endif // WITH_GHOST_DEBUG
+#endif /* `!defined(WITH_GHOST_DEBUG)` */
#ifdef WITH_ASSERT_ABORT
# include <stdio.h> //for fprintf()
@@ -49,7 +49,8 @@
} \
} \
(void)0
-#elif defined(WITH_GHOST_DEBUG)
+/* Assert in non-release builds too. */
+#elif defined(WITH_GHOST_DEBUG) || (!defined(NDEBUG))
# define GHOST_ASSERT(x, info) \
{ \
if (!(x)) { \
@@ -59,6 +60,6 @@
} \
} \
(void)0
-#else // WITH_GHOST_DEBUG
+#else /* `defined(WITH_GHOST_DEBUG) || (!defined(NDEBUG))` */
# define GHOST_ASSERT(x, info) ((void)0)
-#endif // WITH_GHOST_DEBUG
+#endif /* `defined(WITH_GHOST_DEBUG) || (!defined(NDEBUG))` */
diff --git a/intern/ghost/intern/GHOST_DisplayManagerNULL.h b/intern/ghost/intern/GHOST_DisplayManagerNULL.h
index ba72dcbe8dd..dbef4fafd8f 100644
--- a/intern/ghost/intern/GHOST_DisplayManagerNULL.h
+++ b/intern/ghost/intern/GHOST_DisplayManagerNULL.h
@@ -8,38 +8,38 @@
#pragma once
#include "GHOST_DisplayManager.h"
-#include "GHOST_SystemNULL.h"
+#include "GHOST_SystemHeadless.h"
-class GHOST_SystemNULL;
+class GHOST_SystemHeadless;
class GHOST_DisplayManagerNULL : public GHOST_DisplayManager {
public:
- GHOST_DisplayManagerNULL(GHOST_SystemNULL *system) : GHOST_DisplayManager(), m_system(system)
+ GHOST_DisplayManagerNULL() : GHOST_DisplayManager()
{ /* nop */
}
- GHOST_TSuccess getNumDisplays(uint8_t &numDisplays) const
+ GHOST_TSuccess getNumDisplays(uint8_t & /*numDisplays*/) const override
{
return GHOST_kFailure;
}
- GHOST_TSuccess getNumDisplaySettings(uint8_t display, int32_t &numSettings) const
+ GHOST_TSuccess getNumDisplaySettings(uint8_t /*display*/,
+ int32_t & /*numSettings*/) const override
{
return GHOST_kFailure;
}
- GHOST_TSuccess getDisplaySetting(uint8_t display,
- int32_t index,
- GHOST_DisplaySetting &setting) const
+ GHOST_TSuccess getDisplaySetting(uint8_t /*display*/,
+ int32_t /*index*/,
+ GHOST_DisplaySetting & /*setting*/) const override
{
return GHOST_kFailure;
}
- GHOST_TSuccess getCurrentDisplaySetting(uint8_t display, GHOST_DisplaySetting &setting) const
+ GHOST_TSuccess getCurrentDisplaySetting(uint8_t display,
+ GHOST_DisplaySetting &setting) const override
{
return getDisplaySetting(display, int32_t(0), setting);
}
- GHOST_TSuccess setCurrentDisplaySetting(uint8_t display, const GHOST_DisplaySetting &setting)
+ GHOST_TSuccess setCurrentDisplaySetting(uint8_t /*display*/,
+ const GHOST_DisplaySetting & /*setting*/) override
{
return GHOST_kSuccess;
}
-
- private:
- GHOST_SystemNULL *m_system;
};
diff --git a/intern/ghost/intern/GHOST_DisplayManagerWin32.cpp b/intern/ghost/intern/GHOST_DisplayManagerWin32.cpp
index 3d8920d7c52..ee79792bb7e 100644
--- a/intern/ghost/intern/GHOST_DisplayManagerWin32.cpp
+++ b/intern/ghost/intern/GHOST_DisplayManagerWin32.cpp
@@ -11,8 +11,9 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
-// We do not support multiple monitors at the moment
+/* We do not support multiple monitors at the moment. */
#define COMPILE_MULTIMON_STUBS
+
#include <multimon.h>
GHOST_DisplayManagerWin32::GHOST_DisplayManagerWin32(void)
@@ -31,16 +32,15 @@ static BOOL get_dd(DWORD d, DISPLAY_DEVICE *dd)
return ::EnumDisplayDevices(NULL, d, dd, 0);
}
-/*
- * When you call EnumDisplaySettings with iModeNum set to zero, the operating system
- * initializes and caches information about the display device. When you call
- * EnumDisplaySettings with iModeNum set to a non-zero value, the function returns
- * the information that was cached the last time the function was called with iModeNum
- * set to zero.
- */
GHOST_TSuccess GHOST_DisplayManagerWin32::getNumDisplaySettings(uint8_t display,
int32_t &numSettings) const
{
+ /* When you call #EnumDisplaySettings with #iModeNum set to zero, the operating system
+ * initializes and caches information about the display device.
+ * When you call #EnumDisplaySettings with #iModeNum set to a non-zero value,
+ * the function returns the information that was cached the last time the
+ * function was called with #iModeNum set to zero. */
+
DISPLAY_DEVICE display_device;
if (!get_dd(display, &display_device))
return GHOST_kFailure;
@@ -70,21 +70,20 @@ GHOST_TSuccess GHOST_DisplayManagerWin32::getDisplaySetting(uint8_t display,
dm.dmPelsHeight,
dm.dmBitsPerPel,
dm.dmDisplayFrequency);
-#endif // WITH_GHOST_DEBUG
+#endif /* WITH_GHOST_DEBUG */
setting.xPixels = dm.dmPelsWidth;
setting.yPixels = dm.dmPelsHeight;
setting.bpp = dm.dmBitsPerPel;
- /* When you call the EnumDisplaySettings function, the dmDisplayFrequency member
+ /* When you call the #EnumDisplaySettings function, the #dmDisplayFrequency member
* may return with the value 0 or 1. These values represent the display hardware's
* default refresh rate. This default rate is typically set by switches on a display
* card or computer motherboard, or by a configuration program that does not use
- * Win32 display functions such as ChangeDisplaySettings.
- */
- /* First, we tried to explicitly set the frequency to 60 if EnumDisplaySettings
+ * Win32 display functions such as #ChangeDisplaySettings. */
+
+ /* First, we tried to explicitly set the frequency to 60 if #EnumDisplaySettings
* returned 0 or 1 but this doesn't work since later on an exact match will
* be searched. And this will never happen if we change it to 60. Now we rely
- * on the default h/w setting.
- */
+ * on the default hardware setting. */
setting.frequency = dm.dmDisplayFrequency;
success = GHOST_kSuccess;
}
@@ -117,22 +116,23 @@ GHOST_TSuccess GHOST_DisplayManagerWin32::setCurrentDisplaySetting(
break;
}
}
- /*
- * dm.dmBitsPerPel = match.bpp;
- * dm.dmPelsWidth = match.xPixels;
- * dm.dmPelsHeight = match.yPixels;
- * dm.dmDisplayFrequency = match.frequency;
- * dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
- * dm.dmSize = sizeof(DEVMODE);
- * dm.dmDriverExtra = 0;
- */
+#if 0
+ dm.dmBitsPerPel = match.bpp;
+ dm.dmPelsWidth = match.xPixels;
+ dm.dmPelsHeight = match.yPixels;
+ dm.dmDisplayFrequency = match.frequency;
+ dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
+ dm.dmSize = sizeof(DEVMODE);
+ dm.dmDriverExtra = 0;
+#endif
+
#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 // WITH_GHOST_DEBUG
+#endif /* WITH_GHOST_DEBUG */
LONG status = ::ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
#ifdef WITH_GHOST_DEBUG
@@ -166,6 +166,6 @@ GHOST_TSuccess GHOST_DisplayManagerWin32::setCurrentDisplaySetting(
printf("display change: Return value invalid\n");
break;
}
-#endif // WITH_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 a82a31e7386..2831f2ee8ad 100644
--- a/intern/ghost/intern/GHOST_DropTargetWin32.cpp
+++ b/intern/ghost/intern/GHOST_DropTargetWin32.cpp
@@ -13,9 +13,9 @@
#include "utfconv.h"
#ifdef WITH_GHOST_DEBUG
-// utility
+/* utility */
void printLastError(void);
-#endif // WITH_GHOST_DEBUG
+#endif /* WITH_GHOST_DEBUG */
GHOST_DropTargetWin32::GHOST_DropTargetWin32(GHOST_WindowWin32 *window, GHOST_SystemWin32 *system)
: m_window(window), m_system(system)
@@ -32,22 +32,21 @@ GHOST_DropTargetWin32::~GHOST_DropTargetWin32()
/*
* IUnknown::QueryInterface
*/
-HRESULT __stdcall GHOST_DropTargetWin32::QueryInterface(REFIID riid, void **ppvObj)
+HRESULT __stdcall GHOST_DropTargetWin32::QueryInterface(REFIID riid, void **ppv_obj)
{
- if (!ppvObj)
+ if (!ppv_obj) {
return E_INVALIDARG;
- *ppvObj = NULL;
+ }
+ *ppv_obj = NULL;
if (riid == IID_IUnknown || riid == IID_IDropTarget) {
AddRef();
- *ppvObj = (void *)this;
+ *ppv_obj = (void *)this;
return S_OK;
}
- else {
- *ppvObj = NULL;
- return E_NOINTERFACE;
- }
+ *ppv_obj = NULL;
+ return E_NOINTERFACE;
}
/*
@@ -78,16 +77,16 @@ ULONG __stdcall GHOST_DropTargetWin32::Release(void)
/*
* Implementation of IDropTarget::DragEnter
*/
-HRESULT __stdcall GHOST_DropTargetWin32::DragEnter(IDataObject *pDataObject,
- DWORD grfKeyState,
+HRESULT __stdcall GHOST_DropTargetWin32::DragEnter(IDataObject *p_data_object,
+ DWORD grf_key_state,
POINTL pt,
- DWORD *pdwEffect)
+ DWORD *pdw_effect)
{
- // we accept all drop by default
+ /* We accept all drop by default. */
m_window->setAcceptDragOperation(true);
- *pdwEffect = DROPEFFECT_NONE;
+ *pdw_effect = DROPEFFECT_NONE;
- m_draggedObjectType = getGhostType(pDataObject);
+ m_draggedObjectType = getGhostType(p_data_object);
m_system->pushDragDropEvent(
GHOST_kEventDraggingEntered, m_draggedObjectType, m_window, pt.x, pt.y, NULL);
return S_OK;
@@ -96,15 +95,17 @@ HRESULT __stdcall GHOST_DropTargetWin32::DragEnter(IDataObject *pDataObject,
/*
* Implementation of IDropTarget::DragOver
*/
-HRESULT __stdcall GHOST_DropTargetWin32::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
+HRESULT __stdcall GHOST_DropTargetWin32::DragOver(DWORD grf_key_state,
+ POINTL pt,
+ DWORD *pdw_effect)
{
if (m_window->canAcceptDragOperation()) {
- *pdwEffect = allowedDropEffect(*pdwEffect);
+ *pdw_effect = allowedDropEffect(*pdw_effect);
}
else {
- *pdwEffect = DROPEFFECT_NONE;
- // XXX Uncomment to test drop. Drop will not be called if pdwEffect == DROPEFFECT_NONE.
- // *pdwEffect = DROPEFFECT_COPY;
+ *pdw_effect = DROPEFFECT_NONE;
+ /* XXX Uncomment to test drop. Drop will not be called if `pdw_effect == DROPEFFECT_NONE`. */
+ // *pdw_effect = DROPEFFECT_COPY;
}
m_system->pushDragDropEvent(
GHOST_kEventDraggingUpdated, m_draggedObjectType, m_window, pt.x, pt.y, NULL);
@@ -123,25 +124,25 @@ HRESULT __stdcall GHOST_DropTargetWin32::DragLeave(void)
}
/* Implementation of IDropTarget::Drop
- * This function will not be called if pdwEffect is set to DROPEFFECT_NONE in
+ * This function will not be called if pdw_effect is set to DROPEFFECT_NONE in
* the implementation of IDropTarget::DragOver
*/
-HRESULT __stdcall GHOST_DropTargetWin32::Drop(IDataObject *pDataObject,
- DWORD grfKeyState,
+HRESULT __stdcall GHOST_DropTargetWin32::Drop(IDataObject *p_data_object,
+ DWORD grf_key_state,
POINTL pt,
- DWORD *pdwEffect)
+ DWORD *pdw_effect)
{
- void *data = getGhostData(pDataObject);
+ void *data = getGhostData(p_data_object);
if (m_window->canAcceptDragOperation()) {
- *pdwEffect = allowedDropEffect(*pdwEffect);
+ *pdw_effect = allowedDropEffect(*pdw_effect);
}
else {
- *pdwEffect = DROPEFFECT_NONE;
+ *pdw_effect = DROPEFFECT_NONE;
}
- if (data)
+ if (data) {
m_system->pushDragDropEvent(
GHOST_kEventDraggingDropDone, m_draggedObjectType, m_window, pt.x, pt.y, data);
-
+ }
m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
return S_OK;
}
@@ -150,150 +151,132 @@ HRESULT __stdcall GHOST_DropTargetWin32::Drop(IDataObject *pDataObject,
* Helpers
*/
-DWORD GHOST_DropTargetWin32::allowedDropEffect(DWORD dwAllowed)
+DWORD GHOST_DropTargetWin32::allowedDropEffect(DWORD dw_allowed)
{
- DWORD dwEffect = DROPEFFECT_NONE;
- if (dwAllowed & DROPEFFECT_COPY)
- dwEffect = DROPEFFECT_COPY;
-
- return dwEffect;
+ DWORD dw_effect = DROPEFFECT_NONE;
+ if (dw_allowed & DROPEFFECT_COPY) {
+ dw_effect = DROPEFFECT_COPY;
+ }
+ return dw_effect;
}
-GHOST_TDragnDropTypes GHOST_DropTargetWin32::getGhostType(IDataObject *pDataObject)
+GHOST_TDragnDropTypes GHOST_DropTargetWin32::getGhostType(IDataObject *p_data_object)
{
/* Text
* NOTE: Unicode text is available as CF_TEXT too, the system can do the
* conversion, but we do the conversion our self with #WC_NO_BEST_FIT_CHARS.
*/
FORMATETC fmtetc = {CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
- if (pDataObject->QueryGetData(&fmtetc) == S_OK) {
+ if (p_data_object->QueryGetData(&fmtetc) == S_OK) {
return GHOST_kDragnDropTypeString;
}
- // Filesnames
+ /* Files-names. */
fmtetc.cfFormat = CF_HDROP;
- if (pDataObject->QueryGetData(&fmtetc) == S_OK) {
+ if (p_data_object->QueryGetData(&fmtetc) == S_OK) {
return GHOST_kDragnDropTypeFilenames;
}
return GHOST_kDragnDropTypeUnknown;
}
-void *GHOST_DropTargetWin32::getGhostData(IDataObject *pDataObject)
+void *GHOST_DropTargetWin32::getGhostData(IDataObject *p_data_object)
{
- GHOST_TDragnDropTypes type = getGhostType(pDataObject);
+ GHOST_TDragnDropTypes type = getGhostType(p_data_object);
switch (type) {
case GHOST_kDragnDropTypeFilenames:
- return getDropDataAsFilenames(pDataObject);
- break;
+ return getDropDataAsFilenames(p_data_object);
case GHOST_kDragnDropTypeString:
- return getDropDataAsString(pDataObject);
- break;
+ return getDropDataAsString(p_data_object);
case GHOST_kDragnDropTypeBitmap:
- // return getDropDataAsBitmap(pDataObject);
+ // return getDropDataAsBitmap(p_data_object);
break;
default:
#ifdef WITH_GHOST_DEBUG
::printf("\nGHOST_kDragnDropTypeUnknown");
-#endif // WITH_GHOST_DEBUG
+#endif /* WITH_GHOST_DEBUG */
return NULL;
- break;
}
return NULL;
}
-void *GHOST_DropTargetWin32::getDropDataAsFilenames(IDataObject *pDataObject)
+void *GHOST_DropTargetWin32::getDropDataAsFilenames(IDataObject *p_data_object)
{
- UINT totfiles, nvalid = 0;
- WCHAR fpath[MAX_PATH];
- char *temp_path;
- GHOST_TStringArray *strArray = NULL;
+ GHOST_TStringArray *str_array = NULL;
FORMATETC fmtetc = {CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
- STGMEDIUM stgmed;
- HDROP hdrop;
-
- // Check if dataobject supplies the format we want.
- // Double checking here, first in getGhostType.
- if (pDataObject->QueryGetData(&fmtetc) == S_OK) {
- if (pDataObject->GetData(&fmtetc, &stgmed) == S_OK) {
- hdrop = (HDROP)::GlobalLock(stgmed.hGlobal);
-
- totfiles = ::DragQueryFileW(hdrop, -1, NULL, 0);
- if (!totfiles) {
- ::GlobalUnlock(stgmed.hGlobal);
- return NULL;
- }
- strArray = (GHOST_TStringArray *)::malloc(sizeof(GHOST_TStringArray));
- strArray->count = 0;
- strArray->strings = (uint8_t **)::malloc(totfiles * sizeof(uint8_t *));
-
- for (UINT nfile = 0; nfile < totfiles; nfile++) {
- if (::DragQueryFileW(hdrop, nfile, fpath, MAX_PATH) > 0) {
- if (!(temp_path = alloc_utf_8_from_16(fpath, 0))) {
- continue;
+ /* Check if data-object supplies the format we want.
+ * Double checking here, first in #getGhostType. */
+ if (p_data_object->QueryGetData(&fmtetc) == S_OK) {
+ STGMEDIUM stgmed;
+ if (p_data_object->GetData(&fmtetc, &stgmed) == S_OK) {
+ const HDROP hdrop = (HDROP)::GlobalLock(stgmed.hGlobal);
+
+ const UINT totfiles = ::DragQueryFileW(hdrop, -1, NULL, 0);
+ if (totfiles) {
+ str_array = (GHOST_TStringArray *)::malloc(sizeof(GHOST_TStringArray));
+ str_array->count = 0;
+ str_array->strings = (uint8_t **)::malloc(totfiles * sizeof(uint8_t *));
+
+ for (UINT nfile = 0; nfile < totfiles; nfile++) {
+ WCHAR fpath[MAX_PATH];
+ if (::DragQueryFileW(hdrop, nfile, fpath, MAX_PATH) > 0) {
+ char *temp_path;
+ if (!(temp_path = alloc_utf_8_from_16(fpath, 0))) {
+ /* Just ignore paths that could not be converted verbatim. */
+ continue;
+ }
+ str_array->strings[str_array->count++] = (uint8_t *)temp_path;
}
- // Just ignore paths that could not be converted verbatim.
-
- strArray->strings[nvalid] = (uint8_t *)temp_path;
- strArray->count = nvalid + 1;
- nvalid++;
}
}
- // Free up memory.
+ /* Free up memory. */
::GlobalUnlock(stgmed.hGlobal);
::ReleaseStgMedium(&stgmed);
-
- return strArray;
}
}
- return NULL;
+ return str_array;
}
-void *GHOST_DropTargetWin32::getDropDataAsString(IDataObject *pDataObject)
+void *GHOST_DropTargetWin32::getDropDataAsString(IDataObject *p_data_object)
{
char *tmp_string;
FORMATETC fmtetc = {CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
STGMEDIUM stgmed;
- // Try unicode first.
- // Check if dataobject supplies the format we want.
- if (pDataObject->QueryGetData(&fmtetc) == S_OK) {
- if (pDataObject->GetData(&fmtetc, &stgmed) == S_OK) {
+ /* Try unicode first.
+ * Check if data-object supplies the format we want. */
+ if (p_data_object->QueryGetData(&fmtetc) == S_OK) {
+ if (p_data_object->GetData(&fmtetc, &stgmed) == S_OK) {
LPCWSTR wstr = (LPCWSTR)::GlobalLock(stgmed.hGlobal);
- if (!(tmp_string = alloc_utf_8_from_16((wchar_t *)wstr, 0))) {
- ::GlobalUnlock(stgmed.hGlobal);
- return NULL;
- }
- // Free memory
+
+ tmp_string = alloc_utf_8_from_16((wchar_t *)wstr, 0);
+
+ /* Free memory. */
::GlobalUnlock(stgmed.hGlobal);
::ReleaseStgMedium(&stgmed);
+
#ifdef WITH_GHOST_DEBUG
- ::printf("\n<converted droped unicode string>\n%s\n</droped converted unicode string>\n",
- tmp_string);
-#endif // WITH_GHOST_DEBUG
+ if (tmp_string) {
+ ::printf("\n<converted droped unicode string>\n%s\n</droped converted unicode string>\n",
+ tmp_string);
+ }
+#endif /* WITH_GHOST_DEBUG */
return tmp_string;
}
}
fmtetc.cfFormat = CF_TEXT;
- if (pDataObject->QueryGetData(&fmtetc) == S_OK) {
- if (pDataObject->GetData(&fmtetc, &stgmed) == S_OK) {
+ if (p_data_object->QueryGetData(&fmtetc) == S_OK) {
+ if (p_data_object->GetData(&fmtetc, &stgmed) == S_OK) {
char *str = (char *)::GlobalLock(stgmed.hGlobal);
tmp_string = (char *)::malloc(::strlen(str) + 1);
- if (!tmp_string) {
- ::GlobalUnlock(stgmed.hGlobal);
- return NULL;
- }
-
- if (!::strcpy(tmp_string, str)) {
- ::free(tmp_string);
- ::GlobalUnlock(stgmed.hGlobal);
- return NULL;
+ if (tmp_string) {
+ ::strcpy(tmp_string, str);
}
- // Free memory
+ /* Free memory. */
::GlobalUnlock(stgmed.hGlobal);
::ReleaseStgMedium(&stgmed);
@@ -307,13 +290,13 @@ void *GHOST_DropTargetWin32::getDropDataAsString(IDataObject *pDataObject)
int GHOST_DropTargetWin32::WideCharToANSI(LPCWSTR in, char *&out)
{
int size;
- out = NULL; // caller should free if != NULL
+ out = NULL; /* caller should free if != NULL */
- // Get the required size.
- size = ::WideCharToMultiByte(CP_ACP, // System Default Codepage
- 0x00000400, // WC_NO_BEST_FIT_CHARS
+ /* Get the required size. */
+ size = ::WideCharToMultiByte(CP_ACP, /* System Default Codepage */
+ 0x00000400, /* WC_NO_BEST_FIT_CHARS */
in,
- -1, //-1 null terminated, makes output null terminated too.
+ -1, /* -1 null terminated, makes output null terminated too. */
NULL,
0,
NULL,
@@ -322,7 +305,7 @@ int GHOST_DropTargetWin32::WideCharToANSI(LPCWSTR in, char *&out)
if (!size) {
#ifdef WITH_GHOST_DEBUG
::printLastError();
-#endif // WITH_GHOST_DEBUG
+#endif /* WITH_GHOST_DEBUG */
return 0;
}
@@ -337,7 +320,7 @@ int GHOST_DropTargetWin32::WideCharToANSI(LPCWSTR in, char *&out)
if (!size) {
#ifdef WITH_GHOST_DEBUG
::printLastError();
-#endif // WITH_GHOST_DEBUG
+#endif /* WITH_GHOST_DEBUG */
::free(out);
out = NULL;
}
@@ -362,4 +345,4 @@ void printLastError(void)
LocalFree(s);
}
}
-#endif // WITH_GHOST_DEBUG
+#endif /* WITH_GHOST_DEBUG */
diff --git a/intern/ghost/intern/GHOST_DropTargetWin32.h b/intern/ghost/intern/GHOST_DropTargetWin32.h
index 2f628e81b56..03e5492fcb1 100644
--- a/intern/ghost/intern/GHOST_DropTargetWin32.h
+++ b/intern/ghost/intern/GHOST_DropTargetWin32.h
@@ -21,7 +21,7 @@ class GHOST_DropTargetWin32 : public IDropTarget {
* inherited, directly or indirectly, from IUnknown. Therefore, the three
* methods in IUnknown are the first entries in the VTable for every interface.
*/
- HRESULT __stdcall QueryInterface(REFIID riid, void **ppvObj);
+ HRESULT __stdcall QueryInterface(REFIID riid, void **ppv_obj);
ULONG __stdcall AddRef(void);
ULONG __stdcall Release(void);
@@ -44,13 +44,16 @@ class GHOST_DropTargetWin32 : public IDropTarget {
* RevokeDragDrop functions.
*/
- HRESULT __stdcall DragEnter(IDataObject *pDataObject,
- DWORD grfKeyState,
+ HRESULT __stdcall DragEnter(IDataObject *p_data_object,
+ DWORD grf_key_state,
POINTL pt,
- DWORD *pdwEffect);
- HRESULT __stdcall DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
+ DWORD *pdw_effect);
+ HRESULT __stdcall DragOver(DWORD grf_key_state, POINTL pt, DWORD *pdw_effect);
HRESULT __stdcall DragLeave(void);
- HRESULT __stdcall Drop(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
+ HRESULT __stdcall Drop(IDataObject *p_data_object,
+ DWORD grf_key_state,
+ POINTL pt,
+ DWORD *pdw_effect);
/**
* Constructor
@@ -76,36 +79,36 @@ class GHOST_DropTargetWin32 : public IDropTarget {
* \param dwAllowed: Drop sources allowed drop effect.
* \return The allowed drop effect.
*/
- DWORD allowedDropEffect(DWORD dwAllowed);
+ DWORD allowedDropEffect(DWORD dw_allowed);
/**
* Query DataObject for the data types it supports.
- * \param pDataObject: Pointer to the DataObject.
+ * \param p_data_object: Pointer to the DataObject.
* \return GHOST data type.
*/
- GHOST_TDragnDropTypes getGhostType(IDataObject *pDataObject);
+ GHOST_TDragnDropTypes getGhostType(IDataObject *p_data_object);
/**
* Get data to pass in event.
* It checks the type and calls specific functions for each type.
- * \param pDataObject: Pointer to the DataObject.
+ * \param p_data_object: Pointer to the DataObject.
* \return Pointer to data.
*/
- void *getGhostData(IDataObject *pDataObject);
+ void *getGhostData(IDataObject *p_data_object);
/**
* Allocate data as file array to pass in event.
- * \param pDataObject: Pointer to the DataObject.
+ * \param p_data_object: Pointer to the DataObject.
* \return Pointer to data.
*/
- void *getDropDataAsFilenames(IDataObject *pDataObject);
+ void *getDropDataAsFilenames(IDataObject *p_data_object);
/**
* Allocate data as string to pass in event.
- * \param pDataObject: Pointer to the DataObject.
+ * \param p_data_object: Pointer to the DataObject.
* \return Pointer to data.
*/
- void *getDropDataAsString(IDataObject *pDataObject);
+ void *getDropDataAsString(IDataObject *p_data_object);
/**
* Convert Unicode to ANSI, replacing uncomfortable chars with '?'.
diff --git a/intern/ghost/intern/GHOST_DropTargetX11.cpp b/intern/ghost/intern/GHOST_DropTargetX11.cpp
index 900e46c3732..4da3c7c996d 100644
--- a/intern/ghost/intern/GHOST_DropTargetX11.cpp
+++ b/intern/ghost/intern/GHOST_DropTargetX11.cpp
@@ -7,6 +7,8 @@
#include "GHOST_DropTargetX11.h"
#include "GHOST_Debug.h"
+#include "GHOST_PathUtils.h"
+#include "GHOST_utildefines.h"
#include <cassert>
#include <cctype>
@@ -34,7 +36,7 @@ int GHOST_DropTargetX11::m_refCounter = 0;
void GHOST_DropTargetX11::Initialize()
{
Display *display = m_system->getXDisplay();
- int dndTypesCount = sizeof(m_dndMimeTypes) / sizeof(char *);
+ int dndTypesCount = ARRAY_SIZE(m_dndMimeTypes);
int counter;
xdnd_init(&m_dndClass, display);
@@ -96,89 +98,10 @@ GHOST_DropTargetX11::~GHOST_DropTargetX11()
}
}
-/* Based on: https://stackoverflow.com/a/2766963/432509 */
-
-using DecodeState_e = enum DecodeState_e {
- /** Searching for an ampersand to convert. */
- STATE_SEARCH = 0,
- /** Convert the two proceeding characters from hex. */
- STATE_CONVERTING
-};
-
-void GHOST_DropTargetX11::UrlDecode(char *decodedOut, int bufferSize, const char *encodedIn)
-{
- unsigned int i;
- unsigned int len = strlen(encodedIn);
- DecodeState_e state = STATE_SEARCH;
- int j;
- unsigned int asciiCharacter;
- char tempNumBuf[3] = {0};
- bool bothDigits = true;
-
- memset(decodedOut, 0, bufferSize);
-
- for (i = 0; i < len; ++i) {
- switch (state) {
- case STATE_SEARCH:
- if (encodedIn[i] != '%') {
- strncat(decodedOut, &encodedIn[i], 1);
- assert((int)strlen(decodedOut) < bufferSize);
- break;
- }
-
- /* We are now converting */
- state = STATE_CONVERTING;
- break;
-
- case STATE_CONVERTING:
- bothDigits = true;
-
- /* Create a buffer to hold the hex. For example, if %20, this
- * buffer would hold 20 (in ASCII) */
- memset(tempNumBuf, 0, sizeof(tempNumBuf));
-
- /* Conversion complete (i.e. don't convert again next iter) */
- state = STATE_SEARCH;
-
- strncpy(tempNumBuf, &encodedIn[i], 2);
-
- /* Ensure both characters are hexadecimal */
-
- for (j = 0; j < 2; ++j) {
- if (!isxdigit(tempNumBuf[j])) {
- bothDigits = false;
- }
- }
-
- if (!bothDigits) {
- break;
- }
- /* Convert two hexadecimal characters into one character */
- sscanf(tempNumBuf, "%x", &asciiCharacter);
-
- /* Ensure we aren't going to overflow */
- assert((int)strlen(decodedOut) < bufferSize);
-
- /* Concatenate this character onto the output */
- strncat(decodedOut, (char *)&asciiCharacter, 1);
-
- /* Skip the next character */
- i++;
- break;
- }
- }
-}
-
char *GHOST_DropTargetX11::FileUrlDecode(char *fileUrl)
{
if (strncmp(fileUrl, "file://", 7) == 0) {
- /* assume one character of encoded URL can be expanded to 4 chars max */
- int decodedSize = 4 * strlen(fileUrl) + 1;
- char *decodedPath = (char *)malloc(decodedSize);
-
- UrlDecode(decodedPath, decodedSize, fileUrl + 7);
-
- return decodedPath;
+ return GHOST_URL_decode_alloc(fileUrl + 7);
}
return nullptr;
diff --git a/intern/ghost/intern/GHOST_DropTargetX11.h b/intern/ghost/intern/GHOST_DropTargetX11.h
index f0ef27697e1..db73ddff70f 100644
--- a/intern/ghost/intern/GHOST_DropTargetX11.h
+++ b/intern/ghost/intern/GHOST_DropTargetX11.h
@@ -65,14 +65,6 @@ class GHOST_DropTargetX11 {
void *getURIListGhostData(unsigned char *dropBuffer, int dropBufferSize);
/**
- * Decode URL (i.e. converts `file:///a%20b/test` to `file:///a b/test`)
- * \param decodedOut: - buffer for decoded URL.
- * \param bufferSize: - size of output buffer.
- * \param encodedIn: - input encoded buffer to be decoded.
- */
- void UrlDecode(char *decodedOut, int bufferSize, const char *encodedIn);
-
- /**
* Fully decode file URL (i.e. converts `file:///a%20b/test` to `/a b/test`)
* \param fileUrl: - file path URL to be fully decoded.
* \return decoded file path (result should be free-d).
diff --git a/intern/ghost/intern/GHOST_Event.h b/intern/ghost/intern/GHOST_Event.h
index 0813378a819..1e1c428e2ae 100644
--- a/intern/ghost/intern/GHOST_Event.h
+++ b/intern/ghost/intern/GHOST_Event.h
@@ -22,7 +22,7 @@ class GHOST_Event : public GHOST_IEvent {
* \param window: The generating window (or NULL if system event).
*/
GHOST_Event(uint64_t msec, GHOST_TEventType type, GHOST_IWindow *window)
- : m_type(type), m_time(msec), m_window(window), m_data(NULL)
+ : m_type(type), m_time(msec), m_window(window), m_data(nullptr)
{
}
diff --git a/intern/ghost/intern/GHOST_EventButton.h b/intern/ghost/intern/GHOST_EventButton.h
index d68e401ffc4..f42805e2c6b 100644
--- a/intern/ghost/intern/GHOST_EventButton.h
+++ b/intern/ghost/intern/GHOST_EventButton.h
@@ -27,7 +27,7 @@ class GHOST_EventButton : public GHOST_Event {
GHOST_EventButton(uint64_t time,
GHOST_TEventType type,
GHOST_IWindow *window,
- GHOST_TButtonMask button,
+ GHOST_TButton button,
const GHOST_TabletData &tablet)
: GHOST_Event(time, type, window), m_buttonEventData({button, tablet})
{
diff --git a/intern/ghost/intern/GHOST_EventKey.h b/intern/ghost/intern/GHOST_EventKey.h
index 1c3156c27d2..f85a674be9f 100644
--- a/intern/ghost/intern/GHOST_EventKey.h
+++ b/intern/ghost/intern/GHOST_EventKey.h
@@ -22,13 +22,13 @@ class GHOST_EventKey : public GHOST_Event {
* \param msec: The time this event was generated.
* \param type: The type of key event.
* \param key: The key code of the key.
+ * \param is_repeat: Enabled for key repeat events (only for press events).
*/
GHOST_EventKey(
uint64_t msec, GHOST_TEventType type, GHOST_IWindow *window, GHOST_TKey key, bool is_repeat)
: GHOST_Event(msec, type, window)
{
m_keyEventData.key = key;
- m_keyEventData.ascii = '\0';
m_keyEventData.utf8_buf[0] = '\0';
m_keyEventData.is_repeat = is_repeat;
m_data = &m_keyEventData;
@@ -39,23 +39,24 @@ class GHOST_EventKey : public GHOST_Event {
* \param msec: The time this event was generated.
* \param type: The type of key event.
* \param key: The key code of the key.
- * \param ascii: The ascii code for the key event.
+ * \param is_repeat: Enabled for key repeat events (only for press events).
+ * \param utf8_buf: The text associated with this key event (only for press events).
*/
GHOST_EventKey(uint64_t msec,
GHOST_TEventType type,
GHOST_IWindow *window,
GHOST_TKey key,
- char ascii,
- const char utf8_buf[6],
- bool is_repeat)
+ bool is_repeat,
+ const char utf8_buf[6])
: GHOST_Event(msec, type, window)
{
m_keyEventData.key = key;
- m_keyEventData.ascii = ascii;
- if (utf8_buf)
+ if (utf8_buf) {
memcpy(m_keyEventData.utf8_buf, utf8_buf, sizeof(m_keyEventData.utf8_buf));
- else
+ }
+ else {
m_keyEventData.utf8_buf[0] = '\0';
+ }
m_keyEventData.is_repeat = is_repeat;
m_data = &m_keyEventData;
}
diff --git a/intern/ghost/intern/GHOST_ISystem.cpp b/intern/ghost/intern/GHOST_ISystem.cpp
index 745d5faeed4..13eccf661f5 100644
--- a/intern/ghost/intern/GHOST_ISystem.cpp
+++ b/intern/ghost/intern/GHOST_ISystem.cpp
@@ -9,14 +9,14 @@
* Copyright (C) 2001 NaN Technologies B.V.
*/
+#include <stdexcept>
+
#include "GHOST_ISystem.h"
+#include "GHOST_SystemHeadless.h"
-#if defined(WITH_HEADLESS)
-# include "GHOST_SystemNULL.h"
-#elif defined(WITH_GHOST_X11) && defined(WITH_GHOST_WAYLAND)
+#if defined(WITH_GHOST_X11) && defined(WITH_GHOST_WAYLAND)
# include "GHOST_SystemWayland.h"
# include "GHOST_SystemX11.h"
-# include <stdexcept>
#elif defined(WITH_GHOST_X11)
# include "GHOST_SystemX11.h"
#elif defined(WITH_GHOST_WAYLAND)
@@ -37,27 +37,62 @@ GHOST_TSuccess GHOST_ISystem::createSystem()
{
GHOST_TSuccess success;
if (!m_system) {
+
#if defined(WITH_HEADLESS)
- m_system = new GHOST_SystemNULL();
+ /* Pass. */
+#elif defined(WITH_GHOST_WAYLAND)
+# if defined(WITH_GHOST_WAYLAND_DYNLOAD)
+ const bool has_wayland_libraries = ghost_wl_dynload_libraries();
+# else
+ const bool has_wayland_libraries = true;
+# endif
+#endif
+
+#if defined(WITH_HEADLESS)
+ /* Pass. */
#elif defined(WITH_GHOST_X11) && defined(WITH_GHOST_WAYLAND)
/* Special case, try Wayland, fall back to X11. */
try {
- m_system = new GHOST_SystemWayland();
+ m_system = has_wayland_libraries ? new GHOST_SystemWayland() : nullptr;
}
catch (const std::runtime_error &) {
- /* fallback to X11. */
delete m_system;
m_system = nullptr;
}
if (!m_system) {
- m_system = new GHOST_SystemX11();
+ /* Try to fallback to X11. */
+ try {
+ m_system = new GHOST_SystemX11();
+ }
+ catch (const std::runtime_error &) {
+ delete m_system;
+ m_system = nullptr;
+ }
}
#elif defined(WITH_GHOST_X11)
- m_system = new GHOST_SystemX11();
+ try {
+ m_system = new GHOST_SystemX11();
+ }
+ catch (const std::runtime_error &) {
+ delete m_system;
+ m_system = nullptr;
+ }
#elif defined(WITH_GHOST_WAYLAND)
- m_system = new GHOST_SystemWayland();
+ try {
+ m_system = has_wayland_libraries ? new GHOST_SystemWayland() : nullptr;
+ }
+ catch (const std::runtime_error &) {
+ delete m_system;
+ m_system = nullptr;
+ }
#elif defined(WITH_GHOST_SDL)
- m_system = new GHOST_SystemSDL();
+ try {
+ m_system = new GHOST_SystemSDL();
+ }
+ catch (const std::runtime_error &) {
+ delete m_system;
+ m_system = nullptr;
+ }
#elif defined(WIN32)
m_system = new GHOST_SystemWin32();
#elif defined(__APPLE__)
@@ -74,6 +109,30 @@ GHOST_TSuccess GHOST_ISystem::createSystem()
return success;
}
+GHOST_TSuccess GHOST_ISystem::createSystemBackground()
+{
+ GHOST_TSuccess success;
+ if (!m_system) {
+#if !defined(WITH_HEADLESS)
+ /* Try to create a off-screen render surface with the graphical systems. */
+ success = createSystem();
+ if (success) {
+ return success;
+ }
+ /* Try to fallback to headless mode if all else fails. */
+#endif
+ m_system = new GHOST_SystemHeadless();
+ success = m_system != nullptr ? GHOST_kSuccess : GHOST_kFailure;
+ }
+ else {
+ success = GHOST_kFailure;
+ }
+ if (success) {
+ success = m_system->init();
+ }
+ return success;
+}
+
GHOST_TSuccess GHOST_ISystem::disposeSystem()
{
GHOST_TSuccess success = GHOST_kSuccess;
diff --git a/intern/ghost/intern/GHOST_IXrGraphicsBinding.h b/intern/ghost/intern/GHOST_IXrGraphicsBinding.h
index 1246aa19a99..152b6b68026 100644
--- a/intern/ghost/intern/GHOST_IXrGraphicsBinding.h
+++ b/intern/ghost/intern/GHOST_IXrGraphicsBinding.h
@@ -17,11 +17,8 @@ class GHOST_IXrGraphicsBinding {
public:
union {
#if defined(WITH_GHOST_X11)
-# if defined(WITH_GL_EGL)
XrGraphicsBindingEGLMNDX egl;
-# else
XrGraphicsBindingOpenGLXlibKHR glx;
-# endif
#elif defined(WIN32)
XrGraphicsBindingOpenGLWin32KHR wgl;
XrGraphicsBindingD3D11KHR d3d11;
diff --git a/intern/ghost/intern/GHOST_ImeWin32.cpp b/intern/ghost/intern/GHOST_ImeWin32.cpp
index c3fcd7214ca..0a62359cd77 100644
--- a/intern/ghost/intern/GHOST_ImeWin32.cpp
+++ b/intern/ghost/intern/GHOST_ImeWin32.cpp
@@ -512,4 +512,4 @@ void GHOST_ImeWin32::UpdateInfo(HWND window_handle)
}
}
-#endif // WITH_INPUT_IME
+#endif /* WITH_INPUT_IME */
diff --git a/intern/ghost/intern/GHOST_ImeWin32.h b/intern/ghost/intern/GHOST_ImeWin32.h
index 0ae2bbc59e9..85c8ed7b4bd 100644
--- a/intern/ghost/intern/GHOST_ImeWin32.h
+++ b/intern/ghost/intern/GHOST_ImeWin32.h
@@ -351,4 +351,4 @@ class GHOST_ImeWin32 {
bool is_first, is_enable;
};
-#endif // WITH_INPUT_IME
+#endif /* WITH_INPUT_IME */
diff --git a/intern/ghost/intern/GHOST_ModifierKeys.cpp b/intern/ghost/intern/GHOST_ModifierKeys.cpp
index e6e433ba332..d31dc8f0770 100644
--- a/intern/ghost/intern/GHOST_ModifierKeys.cpp
+++ b/intern/ghost/intern/GHOST_ModifierKeys.cpp
@@ -20,7 +20,7 @@ GHOST_ModifierKeys::~GHOST_ModifierKeys()
{
}
-GHOST_TKey GHOST_ModifierKeys::getModifierKeyCode(GHOST_TModifierKeyMask mask)
+GHOST_TKey GHOST_ModifierKeys::getModifierKeyCode(GHOST_TModifierKey mask)
{
GHOST_TKey key;
switch (mask) {
@@ -53,7 +53,7 @@ GHOST_TKey GHOST_ModifierKeys::getModifierKeyCode(GHOST_TModifierKeyMask mask)
return key;
}
-bool GHOST_ModifierKeys::get(GHOST_TModifierKeyMask mask) const
+bool GHOST_ModifierKeys::get(GHOST_TModifierKey mask) const
{
switch (mask) {
case GHOST_kModifierKeyLeftShift:
@@ -75,7 +75,7 @@ bool GHOST_ModifierKeys::get(GHOST_TModifierKeyMask mask) const
}
}
-void GHOST_ModifierKeys::set(GHOST_TModifierKeyMask mask, bool down)
+void GHOST_ModifierKeys::set(GHOST_TModifierKey mask, bool down)
{
switch (mask) {
case GHOST_kModifierKeyLeftShift:
diff --git a/intern/ghost/intern/GHOST_ModifierKeys.h b/intern/ghost/intern/GHOST_ModifierKeys.h
index ca76ba6c704..ce1bf3df2ae 100644
--- a/intern/ghost/intern/GHOST_ModifierKeys.h
+++ b/intern/ghost/intern/GHOST_ModifierKeys.h
@@ -27,21 +27,21 @@ struct GHOST_ModifierKeys {
* \param mask: The mask of the modifier key.
* \return The modifier key's key code.
*/
- static GHOST_TKey getModifierKeyCode(GHOST_TModifierKeyMask mask);
+ static GHOST_TKey getModifierKeyCode(GHOST_TModifierKey mask);
/**
* Returns the state of a single modifier key.
* \param mask: Key state to return.
* \return The state of the key (pressed == true).
*/
- bool get(GHOST_TModifierKeyMask mask) const;
+ bool get(GHOST_TModifierKey mask) const;
/**
* Updates the state of a single modifier key.
* \param mask: Key state to update.
* \param down: The new state of the key.
*/
- void set(GHOST_TModifierKeyMask mask, bool down);
+ void set(GHOST_TModifierKey mask, bool down);
/**
* Sets the state of all modifier keys to up.
diff --git a/intern/ghost/intern/GHOST_NDOFManager.cpp b/intern/ghost/intern/GHOST_NDOFManager.cpp
index 2298ba86521..746e3532b03 100644
--- a/intern/ghost/intern/GHOST_NDOFManager.cpp
+++ b/intern/ghost/intern/GHOST_NDOFManager.cpp
@@ -5,6 +5,7 @@
#include "GHOST_EventKey.h"
#include "GHOST_EventNDOF.h"
#include "GHOST_WindowManager.h"
+#include "GHOST_utildefines.h"
#include <climits>
#include <cmath>
@@ -128,7 +129,7 @@ static const NDOF_ButtonT Generic_HID_map[] = {
NDOF_BUTTON_C,
};
-static const int genericButtonCount = sizeof(Generic_HID_map) / sizeof(NDOF_ButtonT);
+static const int genericButtonCount = ARRAY_SIZE(Generic_HID_map);
GHOST_NDOFManager::GHOST_NDOFManager(GHOST_System &sys)
: m_system(sys),
@@ -410,8 +411,9 @@ static bool nearHomePosition(GHOST_TEventNDOFMotionData *ndof, float threshold)
bool GHOST_NDOFManager::sendMotionEvent()
{
- if (!m_motionEventPending)
+ if (!m_motionEventPending) {
return false;
+ }
m_motionEventPending = false; /* Any pending motion is handled right now. */
diff --git a/intern/ghost/intern/GHOST_NDOFManagerWin32.cpp b/intern/ghost/intern/GHOST_NDOFManagerWin32.cpp
index 36202278ea1..43f31cb2368 100644
--- a/intern/ghost/intern/GHOST_NDOFManagerWin32.cpp
+++ b/intern/ghost/intern/GHOST_NDOFManagerWin32.cpp
@@ -7,10 +7,10 @@ GHOST_NDOFManagerWin32::GHOST_NDOFManagerWin32(GHOST_System &sys) : GHOST_NDOFMa
/* pass */
}
-// whether multi-axis functionality is available (via the OS or driver)
-// does not imply that a device is plugged in or being used
+/* Whether multi-axis functionality is available (via the OS or driver)
+ * does not imply that a device is plugged in or being used. */
bool GHOST_NDOFManagerWin32::available()
{
- // always available since RawInput is built into Windows
+ /* Always available since RawInput is built into Windows. */
return true;
}
diff --git a/intern/ghost/intern/GHOST_Path-api.cpp b/intern/ghost/intern/GHOST_Path-api.cpp
index 1b1c72d8a4b..58f36dc096d 100644
--- a/intern/ghost/intern/GHOST_Path-api.cpp
+++ b/intern/ghost/intern/GHOST_Path-api.cpp
@@ -49,10 +49,10 @@ const char *GHOST_getBinaryDir()
return systemPaths ? systemPaths->getBinaryDir() : nullptr;
}
-void GHOST_addToSystemRecentFiles(const char *filename)
+void GHOST_addToSystemRecentFiles(const char *filepath)
{
GHOST_ISystemPaths *systemPaths = GHOST_ISystemPaths::get();
if (systemPaths) {
- systemPaths->addToSystemRecentFiles(filename);
+ systemPaths->addToSystemRecentFiles(filepath);
}
}
diff --git a/intern/ghost/intern/GHOST_PathUtils.cpp b/intern/ghost/intern/GHOST_PathUtils.cpp
new file mode 100644
index 00000000000..3b57480039a
--- /dev/null
+++ b/intern/ghost/intern/GHOST_PathUtils.cpp
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2010 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup GHOST
+ */
+
+#include <cassert>
+#include <cctype>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "GHOST_PathUtils.h"
+#include "GHOST_Types.h"
+
+/* Based on: https://stackoverflow.com/a/2766963/432509 */
+
+using DecodeState_e = enum DecodeState_e {
+ /** Searching for an ampersand to convert. */
+ STATE_SEARCH = 0,
+ /** Convert the two proceeding characters from hex. */
+ STATE_CONVERTING
+};
+
+void GHOST_URL_decode(char *buf_dst, int buf_dst_size, const char *buf_src)
+{
+ const unsigned int buf_src_len = strlen(buf_src);
+ DecodeState_e state = STATE_SEARCH;
+ unsigned int ascii_character;
+ char temp_num_buf[3] = {0};
+
+ memset(buf_dst, 0, buf_dst_size);
+
+ for (unsigned int i = 0; i < buf_src_len; i++) {
+ switch (state) {
+ case STATE_SEARCH: {
+ if (buf_src[i] != '%') {
+ strncat(buf_dst, &buf_src[i], 1);
+ assert((int)strlen(buf_dst) < buf_dst_size);
+ break;
+ }
+
+ /* We are now converting. */
+ state = STATE_CONVERTING;
+ break;
+ }
+ case STATE_CONVERTING: {
+ bool both_digits = true;
+
+ /* Create a buffer to hold the hex. For example, if `%20`,
+ * this buffer would hold 20 (in ASCII). */
+ memset(temp_num_buf, 0, sizeof(temp_num_buf));
+
+ /* Conversion complete (i.e. don't convert again next iteration). */
+ state = STATE_SEARCH;
+
+ strncpy(temp_num_buf, &buf_src[i], 2);
+
+ /* Ensure both characters are hexadecimal. */
+ for (int j = 0; j < 2; j++) {
+ if (!isxdigit(temp_num_buf[j])) {
+ both_digits = false;
+ }
+ }
+
+ if (!both_digits) {
+ break;
+ }
+ /* Convert two hexadecimal characters into one character. */
+ sscanf(temp_num_buf, "%x", &ascii_character);
+
+ /* Ensure we aren't going to overflow. */
+ assert((int)strlen(buf_dst) < buf_dst_size);
+
+ /* Concatenate this character onto the output. */
+ strncat(buf_dst, (char *)&ascii_character, 1);
+
+ /* Skip the next character. */
+ i++;
+ break;
+ }
+ }
+ }
+}
+
+char *GHOST_URL_decode_alloc(const char *buf_src)
+{
+ /* Assume one character of encoded URL can be expanded to 4 chars max. */
+ const size_t decoded_size_max = 4 * strlen(buf_src) + 1;
+ char *buf_dst = (char *)malloc(decoded_size_max);
+ GHOST_URL_decode(buf_dst, decoded_size_max, buf_src);
+ const size_t decoded_size = strlen(buf_dst) + 1;
+ if (decoded_size != decoded_size_max) {
+ char *buf_dst_trim = (char *)malloc(decoded_size);
+ memcpy(buf_dst_trim, buf_dst, decoded_size);
+ free(buf_dst);
+ buf_dst = buf_dst_trim;
+ }
+ return buf_dst;
+}
diff --git a/intern/ghost/intern/GHOST_PathUtils.h b/intern/ghost/intern/GHOST_PathUtils.h
new file mode 100644
index 00000000000..26a31d1f5c6
--- /dev/null
+++ b/intern/ghost/intern/GHOST_PathUtils.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2010 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup GHOST
+ */
+
+#pragma once
+
+/**
+ * Decode URL (i.e. converts `file:///a%20b/test` to `file:///a b/test`)
+ *
+ * \param buf_dst: Buffer for decoded URL.
+ * \param buf_dst_maxlen: Size of output buffer.
+ * \param buf_src: Input encoded buffer to be decoded.
+ */
+void GHOST_URL_decode(char *buf_dst, int buf_dst_size, const char *buf_src);
+/**
+ * A version of #GHOST_URL_decode that allocates the string & returns it.
+ *
+ * \param buf_src: Input encoded buffer to be decoded.
+ * \return The decoded output buffer.
+ */
+char *GHOST_URL_decode_alloc(const char *buf_src);
diff --git a/intern/ghost/intern/GHOST_System.cpp b/intern/ghost/intern/GHOST_System.cpp
index cc8d0915c5a..bfb7c958048 100644
--- a/intern/ghost/intern/GHOST_System.cpp
+++ b/intern/ghost/intern/GHOST_System.cpp
@@ -110,8 +110,7 @@ bool GHOST_System::validWindow(GHOST_IWindow *window)
GHOST_TSuccess GHOST_System::beginFullScreen(const GHOST_DisplaySetting &setting,
GHOST_IWindow **window,
- const bool stereoVisual,
- const bool alphaBackground)
+ const bool stereoVisual)
{
GHOST_TSuccess success = GHOST_kFailure;
GHOST_ASSERT(m_windowManager, "GHOST_System::beginFullScreen(): invalid window manager");
@@ -125,8 +124,7 @@ GHOST_TSuccess GHOST_System::beginFullScreen(const GHOST_DisplaySetting &setting
setting);
if (success == GHOST_kSuccess) {
// GHOST_PRINT("GHOST_System::beginFullScreen(): creating full-screen window\n");
- success = createFullScreenWindow(
- (GHOST_Window **)window, setting, stereoVisual, alphaBackground);
+ success = createFullScreenWindow((GHOST_Window **)window, setting, stereoVisual);
if (success == GHOST_kSuccess) {
m_windowManager->beginFullScreen(*window, stereoVisual);
}
@@ -260,7 +258,30 @@ GHOST_TSuccess GHOST_System::pushEvent(GHOST_IEvent *event)
return success;
}
-GHOST_TSuccess GHOST_System::getModifierKeyState(GHOST_TModifierKeyMask mask, bool &isDown) const
+GHOST_TSuccess GHOST_System::getCursorPositionClientRelative(const GHOST_IWindow *window,
+ int32_t &x,
+ int32_t &y) const
+{
+ /* Sub-classes that can implement this directly should do so. */
+ int32_t screen_x, screen_y;
+ GHOST_TSuccess success = getCursorPosition(screen_x, screen_y);
+ if (success == GHOST_kSuccess) {
+ window->screenToClient(screen_x, screen_y, x, y);
+ }
+ return success;
+}
+
+GHOST_TSuccess GHOST_System::setCursorPositionClientRelative(GHOST_IWindow *window,
+ int32_t x,
+ int32_t y)
+{
+ /* Sub-classes that can implement this directly should do so. */
+ int32_t screen_x, screen_y;
+ window->clientToScreen(x, y, screen_x, screen_y);
+ return setCursorPosition(screen_x, screen_y);
+}
+
+GHOST_TSuccess GHOST_System::getModifierKeyState(GHOST_TModifierKey mask, bool &isDown) const
{
GHOST_ModifierKeys keys;
/* Get the state of all modifier keys. */
@@ -272,7 +293,7 @@ GHOST_TSuccess GHOST_System::getModifierKeyState(GHOST_TModifierKeyMask mask, bo
return success;
}
-GHOST_TSuccess GHOST_System::getButtonState(GHOST_TButtonMask mask, bool &isDown) const
+GHOST_TSuccess GHOST_System::getButtonState(GHOST_TButton mask, bool &isDown) const
{
GHOST_Buttons buttons;
/* Get the state of all mouse buttons. */
@@ -350,18 +371,13 @@ GHOST_TSuccess GHOST_System::exit()
GHOST_TSuccess GHOST_System::createFullScreenWindow(GHOST_Window **window,
const GHOST_DisplaySetting &settings,
- const bool stereoVisual,
- const bool alphaBackground)
+ const bool stereoVisual)
{
GHOST_GLSettings glSettings = {0};
if (stereoVisual) {
glSettings.flags |= GHOST_glStereoVisual;
}
- if (alphaBackground) {
- glSettings.flags |= GHOST_glAlphaBackground;
- }
-
/* NOTE: don't use #getCurrentDisplaySetting() because on X11 we may
* be zoomed in and the desktop may be bigger than the viewport. */
GHOST_ASSERT(m_displayManager,
diff --git a/intern/ghost/intern/GHOST_System.h b/intern/ghost/intern/GHOST_System.h
index b60ce09f743..8c51b3421b2 100644
--- a/intern/ghost/intern/GHOST_System.h
+++ b/intern/ghost/intern/GHOST_System.h
@@ -120,8 +120,7 @@ class GHOST_System : public GHOST_ISystem {
*/
GHOST_TSuccess beginFullScreen(const GHOST_DisplaySetting &setting,
GHOST_IWindow **window,
- const bool stereoVisual,
- const bool alphaBackground);
+ const bool stereoVisual);
/**
* Updates the resolution while in fullscreen mode.
@@ -203,6 +202,15 @@ class GHOST_System : public GHOST_ISystem {
* Cursor management functionality
***************************************************************************************/
+ /* Client relative functions use a default implementation
+ * that converts from screen-coordinates to client coordinates.
+ * Implementations may override. */
+
+ GHOST_TSuccess getCursorPositionClientRelative(const GHOST_IWindow *window,
+ int32_t &x,
+ int32_t &y) const;
+ GHOST_TSuccess setCursorPositionClientRelative(GHOST_IWindow *window, int32_t x, int32_t y);
+
/**
* Inherited from GHOST_ISystem but left pure virtual
* <pre>
@@ -221,7 +229,7 @@ class GHOST_System : public GHOST_ISystem {
* \param isDown: The state of a modifier key (true == pressed).
* \return Indication of success.
*/
- GHOST_TSuccess getModifierKeyState(GHOST_TModifierKeyMask mask, bool &isDown) const;
+ GHOST_TSuccess getModifierKeyState(GHOST_TModifierKey mask, bool &isDown) const;
/**
* Returns the state of a mouse button (outside the message queue).
@@ -229,7 +237,7 @@ class GHOST_System : public GHOST_ISystem {
* \param isDown: Button state.
* \return Indication of success.
*/
- GHOST_TSuccess getButtonState(GHOST_TButtonMask mask, bool &isDown) const;
+ GHOST_TSuccess getButtonState(GHOST_TButton mask, bool &isDown) const;
/**
* Set which tablet API to use. Only affects Windows, other platforms have a single API.
@@ -367,8 +375,7 @@ class GHOST_System : public GHOST_ISystem {
*/
GHOST_TSuccess createFullScreenWindow(GHOST_Window **window,
const GHOST_DisplaySetting &settings,
- const bool stereoVisual,
- const bool alphaBackground = 0);
+ const bool stereoVisual);
/** The display manager (platform dependent). */
GHOST_DisplayManager *m_displayManager;
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.h b/intern/ghost/intern/GHOST_SystemCocoa.h
index 8b6dfb4efed..a9e659d3565 100644
--- a/intern/ghost/intern/GHOST_SystemCocoa.h
+++ b/intern/ghost/intern/GHOST_SystemCocoa.h
@@ -236,9 +236,9 @@ class GHOST_SystemCocoa : public GHOST_System {
/**
* \see GHOST_ISystem
*/
- int setConsoleWindowState(GHOST_TConsoleWindowState action)
+ bool setConsoleWindowState(GHOST_TConsoleWindowState action)
{
- return 0;
+ return false;
}
/**
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm
index 8677c0b9552..5562db7d67f 100644
--- a/intern/ghost/intern/GHOST_SystemCocoa.mm
+++ b/intern/ghost/intern/GHOST_SystemCocoa.mm
@@ -37,7 +37,7 @@
#pragma mark KeyMap, mouse converters
-static GHOST_TButtonMask convertButton(int button)
+static GHOST_TButton convertButton(int button)
{
switch (button) {
case 0:
@@ -787,7 +787,7 @@ GHOST_IWindow *GHOST_SystemCocoa::getWindowUnderCursor(int32_t x, int32_t y)
}
/**
- * \note : returns coordinates in Cocoa screen coordinates
+ * \note returns coordinates in Cocoa screen coordinates.
*/
GHOST_TSuccess GHOST_SystemCocoa::getCursorPosition(int32_t &x, int32_t &y) const
{
@@ -800,7 +800,7 @@ GHOST_TSuccess GHOST_SystemCocoa::getCursorPosition(int32_t &x, int32_t &y) cons
}
/**
- * \note : expect Cocoa screen coordinates
+ * \note expect Cocoa screen coordinates.
*/
GHOST_TSuccess GHOST_SystemCocoa::setCursorPosition(int32_t x, int32_t y)
{
@@ -1123,6 +1123,7 @@ GHOST_TSuccess GHOST_SystemCocoa::handleDraggingEvent(GHOST_TEventType eventType
case GHOST_kEventDraggingEntered:
case GHOST_kEventDraggingUpdated:
case GHOST_kEventDraggingExited:
+ window->clientToScreenIntern(mouseX, mouseY, mouseX, mouseY);
pushEvent(new GHOST_EventDragnDrop(
getMilliSeconds(), eventType, draggedObjectType, window, mouseX, mouseY, NULL));
break;
@@ -1331,6 +1332,8 @@ GHOST_TSuccess GHOST_SystemCocoa::handleDraggingEvent(GHOST_TEventType eventType
return GHOST_kFailure;
break;
}
+
+ window->clientToScreenIntern(mouseX, mouseY, mouseX, mouseY);
pushEvent(new GHOST_EventDragnDrop(
getMilliSeconds(), eventType, draggedObjectType, window, mouseX, mouseY, eventData));
@@ -1779,7 +1782,6 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr)
NSString *characters;
NSData *convertedCharacters;
GHOST_TKey keyCode;
- unsigned char ascii;
NSString *charsIgnoringModifiers;
window = m_windowManager->getWindowAssociatedWithOSWindow((void *)[event window]);
@@ -1789,7 +1791,6 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr)
}
char utf8_buf[6] = {'\0'};
- ascii = 0;
switch ([event type]) {
@@ -1809,7 +1810,6 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr)
kUCKeyActionUp);
}
- /* handling both unicode or ascii */
characters = [event characters];
if ([characters length] > 0) {
convertedCharacters = [characters dataUsingEncoding:NSUTF8StringEncoding];
@@ -1835,41 +1835,31 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr)
if ((keyCode == GHOST_kKeyQ) && (m_modifierMask & NSEventModifierFlagCommand))
break; // Cmd-Q is directly handled by Cocoa
- /* ascii is a subset of unicode */
- if (utf8_buf[0] && !utf8_buf[1]) {
- ascii = utf8_buf[0];
- }
-
if ([event type] == NSEventTypeKeyDown) {
pushEvent(new GHOST_EventKey([event timestamp] * 1000,
GHOST_kEventKeyDown,
window,
keyCode,
- ascii,
- utf8_buf,
- [event isARepeat]));
+ [event isARepeat],
+ utf8_buf));
#if 0
- printf("Key down rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c utf8=%s\n",
+ printf("Key down rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u utf8=%s\n",
[event keyCode],
[charsIgnoringModifiers length] > 0 ? [charsIgnoringModifiers characterAtIndex:0] :
' ',
keyCode,
- ascii,
- ascii,
utf8_buf);
#endif
}
else {
pushEvent(new GHOST_EventKey(
- [event timestamp] * 1000, GHOST_kEventKeyUp, window, keyCode, 0, NULL, false));
+ [event timestamp] * 1000, GHOST_kEventKeyUp, window, keyCode, false, NULL));
#if 0
- printf("Key up rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c utf8=%s\n",
+ printf("Key up rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u utf8=%s\n",
[event keyCode],
[charsIgnoringModifiers length] > 0 ? [charsIgnoringModifiers characterAtIndex:0] :
' ',
keyCode,
- ascii,
- ascii,
utf8_buf);
#endif
}
diff --git a/intern/ghost/intern/GHOST_SystemHeadless.h b/intern/ghost/intern/GHOST_SystemHeadless.h
new file mode 100644
index 00000000000..b02a82fc9eb
--- /dev/null
+++ b/intern/ghost/intern/GHOST_SystemHeadless.h
@@ -0,0 +1,166 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup GHOST
+ * Declaration of GHOST_SystemHeadless class.
+ */
+
+#pragma once
+
+#include "../GHOST_Types.h"
+#include "GHOST_DisplayManagerNULL.h"
+#include "GHOST_System.h"
+#include "GHOST_WindowNULL.h"
+
+#ifdef __linux__
+# include "GHOST_ContextEGL.h"
+#endif
+#include "GHOST_ContextNone.h"
+
+class GHOST_WindowNULL;
+
+class GHOST_SystemHeadless : public GHOST_System {
+ public:
+ GHOST_SystemHeadless() : GHOST_System()
+ { /* nop */
+ }
+ ~GHOST_SystemHeadless() override = default;
+
+ bool processEvents(bool /*waitForEvent*/) override
+ {
+ return false;
+ }
+ bool setConsoleWindowState(GHOST_TConsoleWindowState /*action*/) override
+ {
+ return 0;
+ }
+ GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys & /*keys*/) const override
+ {
+ return GHOST_kSuccess;
+ }
+ GHOST_TSuccess getButtons(GHOST_Buttons & /*buttons*/) const override
+ {
+ return GHOST_kSuccess;
+ }
+ char *getClipboard(bool /*selection*/) const override
+ {
+ return nullptr;
+ }
+ void putClipboard(const char * /*buffer*/, bool /*selection*/) const override
+ { /* nop */
+ }
+ uint64_t getMilliSeconds() const override
+ {
+ return 0;
+ }
+ uint8_t getNumDisplays() const override
+ {
+ return uint8_t(1);
+ }
+ GHOST_TSuccess getCursorPosition(int32_t & /*x*/, int32_t & /*y*/) const override
+ {
+ return GHOST_kFailure;
+ }
+ GHOST_TSuccess setCursorPosition(int32_t /*x*/, int32_t /*y*/) override
+ {
+ return GHOST_kFailure;
+ }
+ void getMainDisplayDimensions(uint32_t & /*width*/, uint32_t & /*height*/) const override
+ { /* nop */
+ }
+ void getAllDisplayDimensions(uint32_t & /*width*/, uint32_t & /*height*/) const override
+ { /* nop */
+ }
+ GHOST_IContext *createOffscreenContext(GHOST_GLSettings /*glSettings*/) override
+ {
+#ifdef __linux__
+ GHOST_Context *context;
+ for (int minor = 6; minor >= 0; --minor) {
+ context = new GHOST_ContextEGL((GHOST_System *)this,
+ false,
+ EGLNativeWindowType(0),
+ EGLNativeDisplayType(EGL_DEFAULT_DISPLAY),
+ EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
+ 4,
+ minor,
+ GHOST_OPENGL_EGL_CONTEXT_FLAGS,
+ GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
+ EGL_OPENGL_API);
+
+ if (context->initializeDrawingContext()) {
+ return context;
+ }
+ delete context;
+ context = nullptr;
+ }
+
+ context = new GHOST_ContextEGL((GHOST_System *)this,
+ false,
+ EGLNativeWindowType(0),
+ EGLNativeDisplayType(EGL_DEFAULT_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() != GHOST_kSuccess) {
+ delete context;
+ context = nullptr;
+ }
+ return context;
+#else
+ return nullptr;
+#endif
+ }
+ GHOST_TSuccess disposeContext(GHOST_IContext *context) override
+ {
+ delete context;
+
+ return GHOST_kSuccess;
+ }
+
+ GHOST_TSuccess init() override
+ {
+ GHOST_TSuccess success = GHOST_System::init();
+
+ if (success) {
+ m_displayManager = new GHOST_DisplayManagerNULL();
+
+ if (m_displayManager) {
+ return GHOST_kSuccess;
+ }
+ }
+
+ return GHOST_kFailure;
+ }
+
+ GHOST_IWindow *createWindow(const char *title,
+ int32_t left,
+ int32_t top,
+ uint32_t width,
+ uint32_t height,
+ GHOST_TWindowState state,
+ GHOST_TDrawingContextType type,
+ GHOST_GLSettings glSettings,
+ const bool /*exclusive*/,
+ const bool /*is_dialog*/,
+ const GHOST_IWindow *parentWindow) override
+ {
+ return new GHOST_WindowNULL(title,
+ left,
+ top,
+ width,
+ height,
+ state,
+ parentWindow,
+ type,
+ ((glSettings.flags & GHOST_glStereoVisual) != 0));
+ }
+
+ GHOST_IWindow *getWindowUnderCursor(int32_t /*x*/, int32_t /*y*/) override
+ {
+ return nullptr;
+ }
+};
diff --git a/intern/ghost/intern/GHOST_SystemNULL.h b/intern/ghost/intern/GHOST_SystemNULL.h
deleted file mode 100644
index 644eb1ba0a5..00000000000
--- a/intern/ghost/intern/GHOST_SystemNULL.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-/** \file
- * \ingroup GHOST
- * Declaration of GHOST_SystemNULL class.
- */
-
-#pragma once
-
-#include "../GHOST_Types.h"
-#include "GHOST_DisplayManagerNULL.h"
-#include "GHOST_System.h"
-#include "GHOST_WindowNULL.h"
-
-class GHOST_WindowNULL;
-
-class GHOST_SystemNULL : public GHOST_System {
- public:
- GHOST_SystemNULL() : GHOST_System()
- { /* nop */
- }
- ~GHOST_SystemNULL()
- { /* nop */
- }
- bool processEvents(bool waitForEvent)
- {
- return false;
- }
- int setConsoleWindowState(GHOST_TConsoleWindowState action)
- {
- return 0;
- }
- GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const
- {
- return GHOST_kSuccess;
- }
- GHOST_TSuccess getButtons(GHOST_Buttons &buttons) const
- {
- return GHOST_kSuccess;
- }
- char *getClipboard(bool selection) const
- {
- return nullptr;
- }
- void putClipboard(const char *buffer, bool selection) const
- { /* nop */
- }
- uint64_t getMilliSeconds() const
- {
- return 0;
- }
- uint8_t getNumDisplays() const
- {
- return uint8_t(1);
- }
- GHOST_TSuccess getCursorPosition(int32_t &x, int32_t &y) const
- {
- return GHOST_kFailure;
- }
- GHOST_TSuccess setCursorPosition(int32_t x, int32_t y)
- {
- return GHOST_kFailure;
- }
- void getMainDisplayDimensions(uint32_t &width, uint32_t &height) const
- { /* nop */
- }
- void getAllDisplayDimensions(uint32_t &width, uint32_t &height) const
- { /* nop */
- }
- GHOST_IContext *createOffscreenContext(GHOST_GLSettings glSettings)
- {
- return nullptr;
- }
- GHOST_TSuccess disposeContext(GHOST_IContext *context)
- {
- return GHOST_kFailure;
- }
-
- GHOST_TSuccess init()
- {
- GHOST_TSuccess success = GHOST_System::init();
-
- if (success) {
- m_displayManager = new GHOST_DisplayManagerNULL(this);
-
- if (m_displayManager) {
- return GHOST_kSuccess;
- }
- }
-
- return GHOST_kFailure;
- }
-
- GHOST_IWindow *createWindow(const char *title,
- int32_t left,
- int32_t top,
- uint32_t width,
- uint32_t height,
- GHOST_TWindowState state,
- GHOST_TDrawingContextType type,
- GHOST_GLSettings glSettings,
- const bool exclusive,
- const bool is_dialog,
- const GHOST_IWindow *parentWindow)
- {
- return new GHOST_WindowNULL(this,
- title,
- left,
- top,
- width,
- height,
- state,
- parentWindow,
- type,
- ((glSettings.flags & GHOST_glStereoVisual) != 0));
- }
-
- GHOST_IWindow *getWindowUnderCursor(int32_t x, int32_t y)
- {
- return nullptr;
- }
-};
diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp
index 36c912d8821..6d0b2b8aa55 100644
--- a/intern/ghost/intern/GHOST_SystemSDL.cpp
+++ b/intern/ghost/intern/GHOST_SystemSDL.cpp
@@ -5,6 +5,7 @@
*/
#include <cassert>
+#include <stdexcept>
#include "GHOST_ContextSDL.h"
#include "GHOST_SystemSDL.h"
@@ -20,7 +21,7 @@
GHOST_SystemSDL::GHOST_SystemSDL() : GHOST_System()
{
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) {
- printf("Error initializing SDL: %s\n", SDL_GetError());
+ throw std::runtime_error("Error initializing SDL: " + std::string(SDL_GetError()));
}
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
@@ -279,6 +280,141 @@ static GHOST_TKey convertSDLKey(SDL_Scancode key)
}
#undef GXMAP
+static char convert_keyboard_event_to_ascii(const SDL_KeyboardEvent &sdl_sub_evt)
+{
+ SDL_Keycode sym = sdl_sub_evt.keysym.sym;
+ if (sym > 127) {
+ switch (sym) {
+ case SDLK_KP_DIVIDE:
+ sym = '/';
+ break;
+ case SDLK_KP_MULTIPLY:
+ sym = '*';
+ break;
+ case SDLK_KP_MINUS:
+ sym = '-';
+ break;
+ case SDLK_KP_PLUS:
+ sym = '+';
+ break;
+ case SDLK_KP_1:
+ sym = '1';
+ break;
+ case SDLK_KP_2:
+ sym = '2';
+ break;
+ case SDLK_KP_3:
+ sym = '3';
+ break;
+ case SDLK_KP_4:
+ sym = '4';
+ break;
+ case SDLK_KP_5:
+ sym = '5';
+ break;
+ case SDLK_KP_6:
+ sym = '6';
+ break;
+ case SDLK_KP_7:
+ sym = '7';
+ break;
+ case SDLK_KP_8:
+ sym = '8';
+ break;
+ case SDLK_KP_9:
+ sym = '9';
+ break;
+ case SDLK_KP_0:
+ sym = '0';
+ break;
+ case SDLK_KP_PERIOD:
+ sym = '.';
+ break;
+ default:
+ sym = 0;
+ break;
+ }
+ }
+ else {
+ if (sdl_sub_evt.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) {
+ /* Weak US keyboard assumptions. */
+ if (sym >= 'a' && sym <= ('a' + 32)) {
+ sym -= 32;
+ }
+ else {
+ switch (sym) {
+ case '`':
+ sym = '~';
+ break;
+ case '1':
+ sym = '!';
+ break;
+ case '2':
+ sym = '@';
+ break;
+ case '3':
+ sym = '#';
+ break;
+ case '4':
+ sym = '$';
+ break;
+ case '5':
+ sym = '%';
+ break;
+ case '6':
+ sym = '^';
+ break;
+ case '7':
+ sym = '&';
+ break;
+ case '8':
+ sym = '*';
+ break;
+ case '9':
+ sym = '(';
+ break;
+ case '0':
+ sym = ')';
+ break;
+ case '-':
+ sym = '_';
+ break;
+ case '=':
+ sym = '+';
+ break;
+ case '[':
+ sym = '{';
+ break;
+ case ']':
+ sym = '}';
+ break;
+ case '\\':
+ sym = '|';
+ break;
+ case ';':
+ sym = ':';
+ break;
+ case '\'':
+ sym = '"';
+ break;
+ case ',':
+ sym = '<';
+ break;
+ case '.':
+ sym = '>';
+ break;
+ case '/':
+ sym = '?';
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ return (char)sym;
+}
+
/**
* Events don't always have valid windows,
* but GHOST needs a window _always_. fallback to the GL window.
@@ -410,7 +546,7 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event)
case SDL_MOUSEBUTTONUP:
case SDL_MOUSEBUTTONDOWN: {
SDL_MouseButtonEvent &sdl_sub_evt = sdl_event->button;
- GHOST_TButtonMask gbmask = GHOST_kButtonMaskLeft;
+ GHOST_TButton gbmask = GHOST_kButtonMaskLeft;
GHOST_TEventType type = (sdl_sub_evt.state == SDL_PRESSED) ? GHOST_kEventButtonDown :
GHOST_kEventButtonUp;
@@ -454,9 +590,9 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event)
case SDL_KEYDOWN:
case SDL_KEYUP: {
SDL_KeyboardEvent &sdl_sub_evt = sdl_event->key;
- SDL_Keycode sym = sdl_sub_evt.keysym.sym;
GHOST_TEventType type = (sdl_sub_evt.state == SDL_PRESSED) ? GHOST_kEventKeyDown :
GHOST_kEventKeyUp;
+ const bool is_repeat = sdl_sub_evt.repeat != 0;
GHOST_WindowSDL *window = findGhostWindow(
SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID));
@@ -465,138 +601,15 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event)
GHOST_TKey gkey = convertSDLKey(sdl_sub_evt.keysym.scancode);
/* NOTE: the `sdl_sub_evt.keysym.sym` is truncated,
* for unicode support ghost has to be modified. */
- // printf("%d\n", sym);
- if (sym > 127) {
- switch (sym) {
- case SDLK_KP_DIVIDE:
- sym = '/';
- break;
- case SDLK_KP_MULTIPLY:
- sym = '*';
- break;
- case SDLK_KP_MINUS:
- sym = '-';
- break;
- case SDLK_KP_PLUS:
- sym = '+';
- break;
- case SDLK_KP_1:
- sym = '1';
- break;
- case SDLK_KP_2:
- sym = '2';
- break;
- case SDLK_KP_3:
- sym = '3';
- break;
- case SDLK_KP_4:
- sym = '4';
- break;
- case SDLK_KP_5:
- sym = '5';
- break;
- case SDLK_KP_6:
- sym = '6';
- break;
- case SDLK_KP_7:
- sym = '7';
- break;
- case SDLK_KP_8:
- sym = '8';
- break;
- case SDLK_KP_9:
- sym = '9';
- break;
- case SDLK_KP_0:
- sym = '0';
- break;
- case SDLK_KP_PERIOD:
- sym = '.';
- break;
- default:
- sym = 0;
- break;
- }
- }
- else {
- if (sdl_sub_evt.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) {
- /* lame US keyboard assumptions */
- if (sym >= 'a' && sym <= ('a' + 32)) {
- sym -= 32;
- }
- else {
- switch (sym) {
- case '`':
- sym = '~';
- break;
- case '1':
- sym = '!';
- break;
- case '2':
- sym = '@';
- break;
- case '3':
- sym = '#';
- break;
- case '4':
- sym = '$';
- break;
- case '5':
- sym = '%';
- break;
- case '6':
- sym = '^';
- break;
- case '7':
- sym = '&';
- break;
- case '8':
- sym = '*';
- break;
- case '9':
- sym = '(';
- break;
- case '0':
- sym = ')';
- break;
- case '-':
- sym = '_';
- break;
- case '=':
- sym = '+';
- break;
- case '[':
- sym = '{';
- break;
- case ']':
- sym = '}';
- break;
- case '\\':
- sym = '|';
- break;
- case ';':
- sym = ':';
- break;
- case '\'':
- sym = '"';
- break;
- case ',':
- sym = '<';
- break;
- case '.':
- sym = '>';
- break;
- case '/':
- sym = '?';
- break;
- default:
- break;
- }
- }
- }
+
+ /* TODO(@campbellbarton): support full unicode, SDL supports this but it needs to be
+ * explicitly enabled via #SDL_StartTextInput which GHOST would have to wrap. */
+ char utf8_buf[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'};
+ if (type == GHOST_kEventKeyDown) {
+ utf8_buf[0] = convert_keyboard_event_to_ascii(sdl_sub_evt);
}
- g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, sym, nullptr, false);
+ g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, is_repeat, utf8_buf);
break;
}
}
diff --git a/intern/ghost/intern/GHOST_SystemSDL.h b/intern/ghost/intern/GHOST_SystemSDL.h
index aefea5eda34..bee277ba674 100644
--- a/intern/ghost/intern/GHOST_SystemSDL.h
+++ b/intern/ghost/intern/GHOST_SystemSDL.h
@@ -33,9 +33,9 @@ class GHOST_SystemSDL : public GHOST_System {
bool processEvents(bool waitForEvent);
- int setConsoleWindowState(GHOST_TConsoleWindowState /*action*/)
+ bool setConsoleWindowState(GHOST_TConsoleWindowState /*action*/)
{
- return 0;
+ return false;
}
GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const;
diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp
index 2b0abd2cc41..4c663e98824 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.cpp
+++ b/intern/ghost/intern/GHOST_SystemWayland.cpp
@@ -11,12 +11,25 @@
#include "GHOST_EventDragnDrop.h"
#include "GHOST_EventKey.h"
#include "GHOST_EventWheel.h"
+#include "GHOST_PathUtils.h"
#include "GHOST_TimerManager.h"
+#include "GHOST_WaylandUtils.h"
#include "GHOST_WindowManager.h"
+#include "GHOST_utildefines.h"
#include "GHOST_ContextEGL.h"
-#include <EGL/egl.h>
+#ifdef WITH_INPUT_NDOF
+# include "GHOST_NDOFManagerUnix.h"
+#endif
+
+#ifdef WITH_GHOST_WAYLAND_DYNLOAD
+# include <wayland_dynload_API.h> /* For `ghost_wl_dynload_libraries`. */
+#endif
+
+#ifdef WITH_GHOST_WAYLAND_DYNLOAD
+# include <wayland_dynload_egl.h>
+#endif
#include <wayland-egl.h>
#include <algorithm>
@@ -26,15 +39,21 @@
#include <unordered_map>
#include <unordered_set>
-#include "GHOST_WaylandCursorSettings.h"
-#include <pointer-constraints-client-protocol.h>
-#include <relative-pointer-client-protocol.h>
-#include <tablet-client-protocol.h>
+#ifdef WITH_GHOST_WAYLAND_DYNLOAD
+# include <wayland_dynload_cursor.h>
+#endif
#include <wayland-cursor.h>
-#include <xdg-output-client-protocol.h>
+
+#include "GHOST_WaylandCursorSettings.h"
#include <xkbcommon/xkbcommon.h>
+/* Generated by `wayland-scanner`. */
+#include <pointer-constraints-unstable-v1-client-protocol.h>
+#include <relative-pointer-unstable-v1-client-protocol.h>
+#include <tablet-unstable-v2-client-protocol.h>
+#include <xdg-output-unstable-v1-client-protocol.h>
+
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
@@ -42,16 +61,45 @@
#include <cstring>
#include <mutex>
-static GHOST_WindowWayland *window_from_surface(struct wl_surface *surface);
+/* Logging, use `ghost.wl.*` prefix. */
+#include "CLG_log.h"
-/* -------------------------------------------------------------------- */
-/** \name Private Types & Defines
- * \{ */
+static void keyboard_handle_key_repeat_cancel(struct GWL_Seat *seat);
+static void output_handle_done(void *data, struct wl_output *wl_output);
+
+/**
+ * GNOME (mutter 42.2 had a bug with confine not respecting scale - Hi-DPI), See: T98793.
+ * Even though this has been fixed, at time of writing it's not yet in a release.
+ * Workaround the problem by implementing confine with a software cursor.
+ * While this isn't ideal, it's not adding a lot of overhead as software
+ * cursors are already used for warping (which WAYLAND doesn't support).
+ */
+#define USE_GNOME_CONFINE_HACK
/**
+ * Always use software confine (not just in GNOME).
+ * Useful for developing with compositors that don't need this workaround.
+ */
+// #define USE_GNOME_CONFINE_HACK_ALWAYS_ON
+
+#ifdef USE_GNOME_CONFINE_HACK
+static bool use_gnome_confine_hack = false;
+#endif
+
+#define XKB_STATE_MODS_ALL \
+ (enum xkb_state_component)(XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED | \
+ XKB_STATE_MODS_LOCKED | XKB_STATE_MODS_EFFECTIVE)
+
+/* -------------------------------------------------------------------- */
+/** \name Inline Event Codes
+ *
* Selected input event code defines from `linux/input-event-codes.h`
* We include some of the button input event codes here, since the header is
- * only available in more recent kernel versions. The event codes are used to
+ * only available in more recent kernel versions.
+ * \{ */
+
+/**
+ * The event codes are used to
* to differentiate from which mouse button an event comes from.
*/
#define BTN_LEFT 0x110
@@ -64,30 +112,50 @@ static GHOST_WindowWayland *window_from_surface(struct wl_surface *surface);
// #define BTN_TASK 0x117 /* UNUSED. */
/**
- * Tablet events, also from `linux/input-event-codes.h`.
+ * Tablet events.
*/
#define BTN_STYLUS 0x14b /* Use as right-mouse. */
#define BTN_STYLUS2 0x14c /* Use as middle-mouse. */
/* NOTE(@campbellbarton): Map to an additional button (not sure which hardware uses this). */
#define BTN_STYLUS3 0x149
-struct buffer_t {
- void *data = nullptr;
- size_t size = 0;
-};
+/**
+ * Keyboard scan-codes.
+ */
+#define KEY_GRAVE 41
-struct cursor_t {
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Private Types & Defines
+ * \{ */
+
+/**
+ * From XKB internals, use for converting a scan-code from WAYLAND to a #xkb_keycode_t.
+ * Ideally this wouldn't need a local define.
+ */
+#define EVDEV_OFFSET 8
+
+struct GWL_Cursor {
bool visible = false;
+ /**
+ * When false, hide the hardware cursor, while the cursor is still considered to be `visible`,
+ * since the grab-mode determines the state of the software cursor,
+ * this may change - removing the need for a software cursor and in this case it's important
+ * the hardware cursor is used.
+ */
+ bool is_hardware = true;
+ bool is_custom = false;
struct wl_surface *wl_surface = nullptr;
struct wl_buffer *wl_buffer = nullptr;
struct wl_cursor_image wl_image = {0};
struct wl_cursor_theme *wl_theme = nullptr;
- struct buffer_t *file_buffer = nullptr;
+ void *custom_data = nullptr;
+ size_t custom_data_size = 0;
int size = 0;
std::string theme_name;
- /** Outputs on which the cursor is visible. */
- std::unordered_set<const output_t *> outputs;
- int scale = 1;
+
+ int custom_scale = 1;
};
/**
@@ -95,76 +163,171 @@ struct cursor_t {
* WAYLAND exposes tools via #zwp_tablet_tool_v2.
* Since are no API's to access properties of the tool, store the values here.
*/
-struct tablet_tool_input_t {
- struct input_t *input = nullptr;
- struct wl_surface *cursor_surface = nullptr;
+struct GWL_TabletTool {
+ struct GWL_Seat *seat = nullptr;
+ struct wl_surface *wl_surface_cursor = nullptr;
+ /** Used to delay clearing tablet focused wl_surface until the frame is handled. */
+ bool proximity = false;
GHOST_TabletData data = GHOST_TABLET_DATA_NONE;
};
-struct data_offer_t {
+struct GWL_DataOffer {
std::unordered_set<std::string> types;
uint32_t source_actions = 0;
uint32_t dnd_action = 0;
struct wl_data_offer *id = nullptr;
std::atomic<bool> in_use = false;
struct {
- /** Compatible with #input_t.xy coordinates. */
+ /** Compatible with #GWL_Seat.xy coordinates. */
wl_fixed_t xy[2] = {0, 0};
} dnd;
};
-struct data_source_t {
+struct GWL_DataSource {
struct wl_data_source *data_source = nullptr;
char *buffer_out = nullptr;
};
-struct key_repeat_payload_t {
- GHOST_SystemWayland *system = nullptr;
- GHOST_IWindow *window = nullptr;
- GHOST_TEventKeyData key_data = {GHOST_kKeyUnknown};
-};
-
-struct input_t {
- GHOST_SystemWayland *system = nullptr;
-
- std::string name;
- struct wl_seat *wl_seat = nullptr;
- struct wl_pointer *wl_pointer = nullptr;
- struct wl_keyboard *wl_keyboard = nullptr;
- struct zwp_tablet_seat_v2 *tablet_seat = nullptr;
+/**
+ * Data used to implement client-side key-repeat.
+ *
+ * \note it's important not to store the target window here
+ * as it can be closed while the key is repeating,
+ * instead use the focused keyboard from #GWL_Seat which is cleared when windows are closed.
+ * Therefor keyboard events must always check the window has not been cleared.
+ */
+struct GWL_KeyRepeatPlayload {
+ struct GWL_Seat *seat = nullptr;
- /** All currently active tablet tools (needed for changing the cursor). */
- std::unordered_set<zwp_tablet_tool_v2 *> tablet_tools;
+ xkb_keycode_t key_code;
- uint32_t pointer_serial = 0;
- uint32_t tablet_serial = 0;
+ /**
+ * Don't cache the `utf8_buf` as this changes based on modifiers which may be pressed
+ * while key repeat is enabled.
+ */
+ struct {
+ GHOST_TKey gkey;
+ } key_data;
+};
- /** Use to check if the last cursor input was tablet or pointer. */
- uint32_t cursor_serial = 0;
+/** Internal variables used to track grab-state. */
+struct GWL_SeatStateGrab {
+ bool use_lock = false;
+ bool use_confine = false;
+};
+/**
+ * State of the pointing device (tablet or mouse).
+ */
+struct GWL_SeatStatePointer {
/**
- * High precision mouse coordinates (pointer or tablet).
+ * High precision coordinates.
*
+ * Mapping to pixels requires the window scale.
* The following example converts these values to screen coordinates.
* \code{.cc}
* const wl_fixed_t scale = win->scale();
* const int event_xy[2] = {
- * wl_fixed_to_int(scale * input->xy[0]),
- * wl_fixed_to_int(scale * input->xy[1]),
+ * wl_fixed_to_int(scale * seat_state_pointer->xy[0]),
+ * wl_fixed_to_int(scale * seat_state_pointer->xy[1]),
* };
* \endcode
*/
wl_fixed_t xy[2] = {0, 0};
+
+ /** Outputs on which the cursor is visible. */
+ std::unordered_set<const GWL_Output *> outputs;
+
+ int theme_scale = 1;
+
+ /** The serial of the last used pointer or tablet. */
+ uint32_t serial = 0;
+
+ /**
+ * The wl_surface last used with this pointing device
+ * (events with this pointing device will be sent here).
+ */
+ struct wl_surface *wl_surface = nullptr;
+
GHOST_Buttons buttons = GHOST_Buttons();
- struct cursor_t cursor;
+};
+
+/**
+ * State of the keyboard (in #GWL_Seat).
+ */
+struct GWL_SeatStateKeyboard {
+ /** The serial of the last used pointer or tablet. */
+ uint32_t serial = 0;
+
+ /**
+ * The wl_surface last used with this pointing device
+ * (events with this pointing device will be sent here).
+ */
+ struct wl_surface *wl_surface = nullptr;
+};
+
+struct GWL_Seat {
+ GHOST_SystemWayland *system = nullptr;
+
+ std::string name;
+ struct wl_seat *wl_seat = nullptr;
+ struct wl_pointer *wl_pointer = nullptr;
+ struct wl_keyboard *wl_keyboard = nullptr;
+ struct zwp_tablet_seat_v2 *tablet_seat = nullptr;
+
+ /** All currently active tablet tools (needed for changing the cursor). */
+ std::unordered_set<zwp_tablet_tool_v2 *> tablet_tools;
+
+ /** Use to check if the last cursor input was tablet or pointer. */
+ uint32_t cursor_source_serial = 0;
+
+ GWL_SeatStatePointer pointer;
+
+ /** Mostly this can be interchanged with `pointer` however it can't be locked/confined. */
+ GWL_SeatStatePointer tablet;
+
+ GWL_SeatStateKeyboard keyboard;
+
+#ifdef USE_GNOME_CONFINE_HACK
+ bool use_pointer_software_confine = false;
+#endif
+ /** The cursor location (in pixel-space) when hidden grab started (#GHOST_kGrabHide). */
+ wl_fixed_t grab_lock_xy[2] = {0, 0};
+
+ struct GWL_Cursor cursor;
struct zwp_relative_pointer_v1 *relative_pointer = nullptr;
struct zwp_locked_pointer_v1 *locked_pointer = nullptr;
struct zwp_confined_pointer_v1 *confined_pointer = nullptr;
struct xkb_context *xkb_context = nullptr;
+
struct xkb_state *xkb_state = nullptr;
+ /**
+ * Keep a state with no modifiers active, use for symbol lookups.
+ */
+ struct xkb_state *xkb_state_empty = nullptr;
+ /**
+ * Keep a state with number-lock enabled, use to access predictable key-pad symbols.
+ * If number-lock is not supported by the key-map, this is set to NULL.
+ */
+ struct xkb_state *xkb_state_empty_with_numlock = nullptr;
+
+ /**
+ * Cache result of `xkb_keymap_mod_get_index`
+ * so every time a modifier is accessed a string lookup isn't required.
+ * Be sure to check for #XKB_MOD_INVALID before using.
+ */
+ struct {
+ xkb_mod_index_t shift; /* #XKB_MOD_NAME_SHIFT */
+ xkb_mod_index_t caps; /* #XKB_MOD_NAME_CAPS */
+ xkb_mod_index_t ctrl; /* #XKB_MOD_NAME_CTRL */
+ xkb_mod_index_t alt; /* #XKB_MOD_NAME_ALT */
+ xkb_mod_index_t num; /* #XKB_MOD_NAME_NUM */
+ xkb_mod_index_t logo; /* #XKB_MOD_NAME_LOGO */
+ } xkb_keymap_mod_index;
+
struct {
/** Key repetition in character per second. */
int32_t rate = 0;
@@ -174,51 +337,50 @@ struct input_t {
GHOST_ITimerTask *timer = nullptr;
} key_repeat;
- struct wl_surface *focus_tablet = nullptr;
- struct wl_surface *focus_pointer = nullptr;
- struct wl_surface *focus_keyboard = nullptr;
- struct wl_surface *focus_dnd = nullptr;
+ struct wl_surface *wl_surface_focus_dnd = nullptr;
struct wl_data_device *data_device = nullptr;
/** Drag & Drop. */
- struct data_offer_t *data_offer_dnd = nullptr;
+ struct GWL_DataOffer *data_offer_dnd = nullptr;
std::mutex data_offer_dnd_mutex;
/** Copy & Paste. */
- struct data_offer_t *data_offer_copy_paste = nullptr;
+ struct GWL_DataOffer *data_offer_copy_paste = nullptr;
std::mutex data_offer_copy_paste_mutex;
- struct data_source_t *data_source = nullptr;
+ struct GWL_DataSource *data_source = nullptr;
std::mutex data_source_mutex;
/** Last device that was active. */
uint32_t data_source_serial = 0;
};
-struct display_t {
+struct GWL_Display {
GHOST_SystemWayland *system = nullptr;
struct wl_display *display = nullptr;
struct wl_compositor *compositor = nullptr;
+
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+ struct libdecor *decor_context = nullptr;
+#else
struct xdg_wm_base *xdg_shell = nullptr;
struct zxdg_decoration_manager_v1 *xdg_decoration_manager = nullptr;
+#endif
+
struct zxdg_output_manager_v1 *xdg_output_manager = nullptr;
struct wl_shm *shm = nullptr;
- std::vector<output_t *> outputs;
- std::vector<input_t *> inputs;
- struct {
- std::string theme;
- int size = 0;
- } cursor;
+ std::vector<GWL_Output *> outputs;
+ std::vector<GWL_Seat *> seats;
+
struct wl_data_device_manager *data_device_manager = nullptr;
struct zwp_tablet_manager_v2 *tablet_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;
};
+#undef LOG
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -247,7 +409,31 @@ static void ghost_wayland_log_handler(const char *msg, va_list arg)
}
}
-static void display_destroy(display_t *d)
+static GWL_SeatStatePointer *seat_state_pointer_active(GWL_Seat *seat)
+{
+ if (seat->pointer.serial == seat->cursor_source_serial) {
+ return &seat->pointer;
+ }
+ if (seat->tablet.serial == seat->cursor_source_serial) {
+ return &seat->tablet;
+ }
+ return nullptr;
+}
+
+static GWL_SeatStatePointer *seat_state_pointer_from_cursor_surface(GWL_Seat *seat,
+ const wl_surface *wl_surface)
+{
+ if (ghost_wl_surface_own_cursor_pointer(wl_surface)) {
+ return &seat->pointer;
+ }
+ if (ghost_wl_surface_own_cursor_tablet(wl_surface)) {
+ return &seat->tablet;
+ }
+ GHOST_ASSERT(0, "Surface found without pointer/tablet tag");
+ return nullptr;
+}
+
+static void display_destroy(GWL_Display *d)
{
if (d->data_device_manager) {
wl_data_device_manager_destroy(d->data_device_manager);
@@ -257,76 +443,77 @@ static void display_destroy(display_t *d)
zwp_tablet_manager_v2_destroy(d->tablet_manager);
}
- for (output_t *output : d->outputs) {
+ for (GWL_Output *output : d->outputs) {
wl_output_destroy(output->wl_output);
delete output;
}
- for (input_t *input : d->inputs) {
+ for (GWL_Seat *seat : d->seats) {
/* First handle members that require locking.
* While highly unlikely, it's possible they are being used while this function runs. */
{
- std::lock_guard lock{input->data_source_mutex};
- 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);
+ std::lock_guard lock{seat->data_source_mutex};
+ if (seat->data_source) {
+ free(seat->data_source->buffer_out);
+ if (seat->data_source->data_source) {
+ wl_data_source_destroy(seat->data_source->data_source);
}
- delete input->data_source;
+ delete seat->data_source;
}
}
{
- std::lock_guard lock{input->data_offer_dnd_mutex};
- if (input->data_offer_dnd) {
- wl_data_offer_destroy(input->data_offer_dnd->id);
- delete input->data_offer_dnd;
+ std::lock_guard lock{seat->data_offer_dnd_mutex};
+ if (seat->data_offer_dnd) {
+ wl_data_offer_destroy(seat->data_offer_dnd->id);
+ delete seat->data_offer_dnd;
}
}
{
- std::lock_guard lock{input->data_offer_copy_paste_mutex};
- if (input->data_offer_copy_paste) {
- wl_data_offer_destroy(input->data_offer_copy_paste->id);
- delete input->data_offer_copy_paste;
+ std::lock_guard lock{seat->data_offer_copy_paste_mutex};
+ if (seat->data_offer_copy_paste) {
+ wl_data_offer_destroy(seat->data_offer_copy_paste->id);
+ delete seat->data_offer_copy_paste;
}
}
- if (input->data_device) {
- wl_data_device_release(input->data_device);
+ if (seat->data_device) {
+ wl_data_device_release(seat->data_device);
}
- if (input->wl_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.wl_surface) {
- wl_surface_destroy(input->cursor.wl_surface);
+
+ if (seat->cursor.custom_data) {
+ munmap(seat->cursor.custom_data, seat->cursor.custom_data_size);
+ }
+
+ if (seat->wl_pointer) {
+ if (seat->cursor.wl_surface) {
+ wl_surface_destroy(seat->cursor.wl_surface);
}
- if (input->cursor.wl_theme) {
- wl_cursor_theme_destroy(input->cursor.wl_theme);
+ if (seat->cursor.wl_theme) {
+ wl_cursor_theme_destroy(seat->cursor.wl_theme);
}
- if (input->wl_pointer) {
- wl_pointer_destroy(input->wl_pointer);
+ if (seat->wl_pointer) {
+ wl_pointer_destroy(seat->wl_pointer);
}
}
- if (input->wl_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;
+ if (seat->wl_keyboard) {
+ if (seat->key_repeat.timer) {
+ keyboard_handle_key_repeat_cancel(seat);
}
- wl_keyboard_destroy(input->wl_keyboard);
- }
- if (input->xkb_state) {
- xkb_state_unref(input->xkb_state);
+ wl_keyboard_destroy(seat->wl_keyboard);
}
- if (input->xkb_context) {
- xkb_context_unref(input->xkb_context);
- }
- wl_seat_destroy(input->wl_seat);
- delete input;
+
+ /* Un-referencing checks for NULL case. */
+ xkb_state_unref(seat->xkb_state);
+ xkb_state_unref(seat->xkb_state_empty);
+ xkb_state_unref(seat->xkb_state_empty_with_numlock);
+
+ xkb_context_unref(seat->xkb_context);
+
+ wl_seat_destroy(seat->wl_seat);
+ delete seat;
}
if (d->shm) {
@@ -341,18 +528,15 @@ static void display_destroy(display_t *d)
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);
}
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+ if (d->decor_context) {
+ libdecor_unref(d->decor_context);
+ }
+#else
if (d->xdg_decoration_manager) {
zxdg_decoration_manager_v1_destroy(d->xdg_decoration_manager);
}
@@ -360,6 +544,7 @@ static void display_destroy(display_t *d)
if (d->xdg_shell) {
xdg_wm_base_destroy(d->xdg_shell);
}
+#endif /* !WITH_GHOST_WAYLAND_LIBDECOR */
if (eglGetDisplay) {
::eglTerminate(eglGetDisplay(EGLNativeDisplayType(d->display)));
@@ -372,7 +557,7 @@ static void display_destroy(display_t *d)
delete d;
}
-static GHOST_TKey xkb_map_gkey(const xkb_keysym_t &sym)
+static GHOST_TKey xkb_map_gkey(const xkb_keysym_t sym)
{
GHOST_TKey gkey;
@@ -463,8 +648,7 @@ static GHOST_TKey xkb_map_gkey(const xkb_keysym_t &sym)
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);
+ /* Rely on #xkb_map_gkey_or_scan_code to report when no key can be found. */
gkey = GHOST_kKeyUnknown;
}
#undef GXMAP
@@ -473,6 +657,41 @@ static GHOST_TKey xkb_map_gkey(const xkb_keysym_t &sym)
return gkey;
}
+/**
+ * Map the keys using the users keyboard layout, if that fails fall back to physical locations.
+ * This is needed so users with keyboard layouts that don't expose #GHOST_kKeyAccentGrave
+ * (typically the key under escape) in the layout can still use this key in keyboard shortcuts.
+ *
+ * \param key: The key's scan-code, compatible with values in `linux/input-event-codes.h`.
+ */
+static GHOST_TKey xkb_map_gkey_or_scan_code(const xkb_keysym_t sym, const uint32_t key)
+{
+ GHOST_TKey gkey = xkb_map_gkey(sym);
+
+ if (UNLIKELY(gkey == GHOST_kKeyUnknown)) {
+ /* Fall back to physical location for keys that would otherwise do nothing. */
+ switch (key) {
+ case KEY_GRAVE: {
+ gkey = GHOST_kKeyAccentGrave;
+ break;
+ }
+ default: {
+ GHOST_PRINT(
+ /* Key-code. */
+ "unhandled key: " << std::hex << std::showbase << sym << /* Hex. */
+ std::dec << " (" << sym << "), " << /* Decimal. */
+ /* Scan-code. */
+ "scan-code: " << std::hex << std::showbase << key << /* Hex. */
+ std::dec << " (" << key << ")" << /* Decimal. */
+ std::endl);
+ break;
+ }
+ }
+ }
+
+ return gkey;
+}
+
static GHOST_TTabletMode tablet_tool_map_type(enum zwp_tablet_tool_v2_type wl_tablet_tool_type)
{
switch (wl_tablet_tool_type) {
@@ -496,12 +715,12 @@ static GHOST_TTabletMode tablet_tool_map_type(enum zwp_tablet_tool_v2_type wl_ta
static const int default_cursor_size = 24;
-static const std::unordered_map<GHOST_TStandardCursor, std::string> cursors = {
+static const std::unordered_map<GHOST_TStandardCursor, const char *> cursors = {
{GHOST_kStandardCursorDefault, "left_ptr"},
{GHOST_kStandardCursorRightArrow, "right_ptr"},
{GHOST_kStandardCursorLeftArrow, "left_ptr"},
{GHOST_kStandardCursorInfo, ""},
- {GHOST_kStandardCursorDestroy, ""},
+ {GHOST_kStandardCursorDestroy, "pirate"},
{GHOST_kStandardCursorHelp, "question_arrow"},
{GHOST_kStandardCursorWait, "watch"},
{GHOST_kStandardCursorText, "xterm"},
@@ -509,21 +728,21 @@ static const std::unordered_map<GHOST_TStandardCursor, std::string> cursors = {
{GHOST_kStandardCursorCrosshairA, ""},
{GHOST_kStandardCursorCrosshairB, ""},
{GHOST_kStandardCursorCrosshairC, ""},
- {GHOST_kStandardCursorPencil, ""},
+ {GHOST_kStandardCursorPencil, "pencil"},
{GHOST_kStandardCursorUpArrow, "sb_up_arrow"},
{GHOST_kStandardCursorDownArrow, "sb_down_arrow"},
- {GHOST_kStandardCursorVerticalSplit, ""},
- {GHOST_kStandardCursorHorizontalSplit, ""},
+ {GHOST_kStandardCursorVerticalSplit, "split_v"},
+ {GHOST_kStandardCursorHorizontalSplit, "split_h"},
{GHOST_kStandardCursorEraser, ""},
{GHOST_kStandardCursorKnife, ""},
- {GHOST_kStandardCursorEyedropper, ""},
- {GHOST_kStandardCursorZoomIn, ""},
- {GHOST_kStandardCursorZoomOut, ""},
+ {GHOST_kStandardCursorEyedropper, "color-picker"},
+ {GHOST_kStandardCursorZoomIn, "zoom-in"},
+ {GHOST_kStandardCursorZoomOut, "zoom-out"},
{GHOST_kStandardCursorMove, "move"},
- {GHOST_kStandardCursorNSEWScroll, ""},
- {GHOST_kStandardCursorNSScroll, ""},
- {GHOST_kStandardCursorEWScroll, ""},
- {GHOST_kStandardCursorStop, ""},
+ {GHOST_kStandardCursorNSEWScroll, "size_all"}, /* Not an exact match. */
+ {GHOST_kStandardCursorNSScroll, "size_ver"}, /* Not an exact match. */
+ {GHOST_kStandardCursorEWScroll, "size_hor"}, /* Not an exact match. */
+ {GHOST_kStandardCursorStop, "not-allowed"},
{GHOST_kStandardCursorUpDown, "sb_v_double_arrow"},
{GHOST_kStandardCursorLeftRight, "sb_h_double_arrow"},
{GHOST_kStandardCursorTopSide, "top_side"},
@@ -562,6 +781,84 @@ static const std::vector<std::string> mime_send = {
"text/plain",
};
+static int memfd_create_sealed(const char *name)
+{
+#ifdef HAVE_MEMFD_CREATE
+ const int fd = memfd_create(name, MFD_CLOEXEC | MFD_ALLOW_SEALING);
+ if (fd >= 0) {
+ fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
+ }
+ return fd;
+#else /* HAVE_MEMFD_CREATE */
+ char *path = getenv("XDG_RUNTIME_DIR");
+ if (!path) {
+ errno = ENOENT;
+ return -1;
+ }
+ char *tmpname;
+ asprintf(&tmpname, "%s/%s-XXXXXX", path, name);
+ const int fd = mkostemp(tmpname, O_CLOEXEC);
+ if (fd >= 0) {
+ unlink(tmpname);
+ }
+ free(tmpname);
+ return fd;
+#endif /* !HAVE_MEMFD_CREATE */
+}
+
+static size_t ghost_wl_shm_format_as_size(enum wl_shm_format format)
+{
+ switch (format) {
+ case WL_SHM_FORMAT_ARGB8888: {
+ return 4;
+ }
+ default: {
+ /* Support other formats as needed. */
+ GHOST_ASSERT(0, "Unexpected format passed in!");
+ return 4;
+ }
+ }
+}
+
+/**
+ * Return a #wl_buffer, ready to have it's data filled in or NULL in case of failure.
+ * The caller is responsible for calling `unmap(buffer_data, buffer_size)`.
+ *
+ * \param r_buffer_data: The buffer to be filled.
+ * \param r_buffer_data_size: The size of `r_buffer_data` in bytes.
+ */
+static wl_buffer *ghost_wl_buffer_create_for_image(struct wl_shm *shm,
+ const int32_t size_xy[2],
+ enum wl_shm_format format,
+ void **r_buffer_data,
+ size_t *r_buffer_data_size)
+{
+ const int fd = memfd_create_sealed("ghost-wl-buffer");
+ wl_buffer *buffer = nullptr;
+ if (fd >= 0) {
+ const int32_t buffer_stride = size_xy[0] * ghost_wl_shm_format_as_size(format);
+ const int32_t buffer_size = buffer_stride * size_xy[1];
+ if (posix_fallocate(fd, 0, buffer_size) == 0) {
+ void *buffer_data = mmap(nullptr, buffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (buffer_data != MAP_FAILED) {
+ struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, buffer_size);
+ buffer = wl_shm_pool_create_buffer(pool, 0, UNPACK2(size_xy), buffer_stride, format);
+ wl_shm_pool_destroy(pool);
+ if (buffer) {
+ *r_buffer_data = buffer_data;
+ *r_buffer_data_size = (size_t)buffer_size;
+ }
+ else {
+ /* Highly unlikely. */
+ munmap(buffer_data, buffer_size);
+ }
+ }
+ }
+ close(fd);
+ }
+ return buffer;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -571,70 +868,110 @@ static const std::vector<std::string> mime_send = {
* an event is received from the compositor.
* \{ */
+static CLG_LogRef LOG_WL_RELATIVE_POINTER = {"ghost.wl.handle.relative_pointer"};
+#define LOG (&LOG_WL_RELATIVE_POINTER)
+
+/**
+ * The caller is responsible for setting the value of `seat->xy`.
+ */
+static void relative_pointer_handle_relative_motion_impl(GWL_Seat *seat,
+ GHOST_WindowWayland *win,
+ const wl_fixed_t xy[2])
+{
+ const wl_fixed_t scale = win->scale();
+
+ seat->pointer.xy[0] = xy[0];
+ seat->pointer.xy[1] = xy[1];
+
+#ifdef USE_GNOME_CONFINE_HACK
+ if (seat->use_pointer_software_confine) {
+ GHOST_Rect bounds;
+ win->getClientBounds(bounds);
+ /* Needed or the cursor is considered outside the window and doesn't restore the location. */
+ bounds.m_r -= 1;
+ bounds.m_b -= 1;
+
+ bounds.m_l = wl_fixed_from_int(bounds.m_l) / scale;
+ bounds.m_t = wl_fixed_from_int(bounds.m_t) / scale;
+ bounds.m_r = wl_fixed_from_int(bounds.m_r) / scale;
+ bounds.m_b = wl_fixed_from_int(bounds.m_b) / scale;
+ bounds.clampPoint(UNPACK2(seat->pointer.xy));
+ }
+#endif
+ seat->system->pushEvent(new GHOST_EventCursor(seat->system->getMilliSeconds(),
+ GHOST_kEventCursorMove,
+ win,
+ wl_fixed_to_int(scale * seat->pointer.xy[0]),
+ wl_fixed_to_int(scale * seat->pointer.xy[1]),
+ GHOST_TABLET_DATA_NONE));
+}
+
static void relative_pointer_handle_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);
- GHOST_WindowWayland *win = window_from_surface(input->focus_pointer);
- if (!win) {
- return;
+ const uint32_t /*utime_hi*/,
+ const uint32_t /*utime_lo*/,
+ const wl_fixed_t dx,
+ const wl_fixed_t dy,
+ const wl_fixed_t /*dx_unaccel*/,
+ const wl_fixed_t /*dy_unaccel*/)
+{
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+ if (wl_surface *wl_surface_focus = seat->pointer.wl_surface) {
+ CLOG_INFO(LOG, 2, "relative_motion");
+ GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
+ const wl_fixed_t scale = win->scale();
+ const wl_fixed_t xy_next[2] = {
+ seat->pointer.xy[0] + (dx / scale),
+ seat->pointer.xy[1] + (dy / scale),
+ };
+ relative_pointer_handle_relative_motion_impl(seat, win, xy_next);
+ }
+ else {
+ CLOG_INFO(LOG, 2, "relative_motion (skipped)");
}
- const wl_fixed_t scale = win->scale();
- input->xy[0] += dx / scale;
- input->xy[1] += dy / scale;
-
- input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
- GHOST_kEventCursorMove,
- win,
- wl_fixed_to_int(scale * input->xy[0]),
- wl_fixed_to_int(scale * input->xy[1]),
- GHOST_TABLET_DATA_NONE));
}
static const zwp_relative_pointer_v1_listener relative_pointer_listener = {
relative_pointer_handle_relative_motion,
};
+#undef LOG
+
/** \} */
/* -------------------------------------------------------------------- */
/** \name Listener (Data Source), #wl_data_source_listener
* \{ */
-static void dnd_events(const input_t *const input, const GHOST_TEventType event)
+static CLG_LogRef LOG_WL_DATA_SOURCE = {"ghost.wl.handle.data_source"};
+#define LOG (&LOG_WL_DATA_SOURCE)
+
+static void dnd_events(const GWL_Seat *const seat, const GHOST_TEventType event)
{
- /* NOTE: `input->data_offer_dnd_mutex` must already be locked. */
- const uint64_t time = input->system->getMilliSeconds();
- GHOST_WindowWayland *const win = static_cast<GHOST_WindowWayland *>(
- wl_surface_get_user_data(input->focus_dnd));
- if (!win) {
- return;
- }
- const wl_fixed_t scale = win->scale();
- const int event_xy[2] = {
- wl_fixed_to_int(scale * input->data_offer_dnd->dnd.xy[0]),
- wl_fixed_to_int(scale * input->data_offer_dnd->dnd.xy[1]),
- };
+ /* NOTE: `seat->data_offer_dnd_mutex` must already be locked. */
+ if (wl_surface *wl_surface_focus = seat->wl_surface_focus_dnd) {
+ GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
+ const wl_fixed_t scale = win->scale();
+ const int event_xy[2] = {
+ wl_fixed_to_int(scale * seat->data_offer_dnd->dnd.xy[0]),
+ wl_fixed_to_int(scale * seat->data_offer_dnd->dnd.xy[1]),
+ };
- for (const std::string &type : mime_preference_order) {
- input->system->pushEvent(new GHOST_EventDragnDrop(
- time, event, mime_dnd.at(type), win, event_xy[0], event_xy[1], nullptr));
+ const uint64_t time = seat->system->getMilliSeconds();
+ for (const std::string &type : mime_preference_order) {
+ seat->system->pushEvent(new GHOST_EventDragnDrop(
+ time, event, mime_dnd.at(type), win, UNPACK2(event_xy), nullptr));
+ }
}
}
-static std::string read_pipe(data_offer_t *data_offer,
+static std::string read_pipe(GWL_DataOffer *data_offer,
const std::string mime_receive,
std::mutex *mutex)
{
int pipefd[2];
- if (pipe(pipefd) != 0) {
+ if (UNLIKELY(pipe(pipefd) != 0)) {
return {};
}
wl_data_offer_receive(data_offer->id, mime_receive.c_str(), pipefd[1]);
@@ -668,18 +1005,20 @@ static void data_source_handle_target(void * /*data*/,
struct wl_data_source * /*wl_data_source*/,
const char * /*mime_type*/)
{
- /* pass */
+ CLOG_INFO(LOG, 2, "target");
}
static void data_source_handle_send(void *data,
struct wl_data_source * /*wl_data_source*/,
const char * /*mime_type*/,
- int32_t fd)
+ const int32_t fd)
{
- input_t *input = static_cast<input_t *>(data);
- std::lock_guard lock{input->data_source_mutex};
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+ std::lock_guard lock{seat->data_source_mutex};
+
+ CLOG_INFO(LOG, 2, "send");
- const char *const buffer = input->data_source->buffer_out;
+ const char *const buffer = seat->data_source->buffer_out;
if (write(fd, buffer, strlen(buffer)) < 0) {
GHOST_PRINT("error writing to clipboard: " << std::strerror(errno) << std::endl);
}
@@ -688,6 +1027,7 @@ static void data_source_handle_send(void *data,
static void data_source_handle_cancelled(void * /*data*/, struct wl_data_source *wl_data_source)
{
+ CLOG_INFO(LOG, 2, "cancelled");
wl_data_source_destroy(wl_data_source);
}
@@ -701,7 +1041,7 @@ static void data_source_handle_cancelled(void * /*data*/, struct wl_data_source
static void data_source_handle_dnd_drop_performed(void * /*data*/,
struct wl_data_source * /*wl_data_source*/)
{
- /* pass */
+ CLOG_INFO(LOG, 2, "dnd_drop_performed");
}
/**
@@ -714,7 +1054,7 @@ static void data_source_handle_dnd_drop_performed(void * /*data*/,
static void data_source_handle_dnd_finished(void * /*data*/,
struct wl_data_source * /*wl_data_source*/)
{
- /* pass */
+ CLOG_INFO(LOG, 2, "dnd_finished");
}
/**
@@ -726,9 +1066,9 @@ static void data_source_handle_dnd_finished(void * /*data*/,
*/
static void data_source_handle_action(void * /*data*/,
struct wl_data_source * /*wl_data_source*/,
- uint32_t /*dnd_action*/)
+ const uint32_t dnd_action)
{
- /* pass */
+ CLOG_INFO(LOG, 2, "handle_action (dnd_action=%u)", dnd_action);
}
static const struct wl_data_source_listener data_source_listener = {
@@ -740,31 +1080,39 @@ static const struct wl_data_source_listener data_source_listener = {
data_source_handle_action,
};
+#undef LOG
+
/** \} */
/* -------------------------------------------------------------------- */
/** \name Listener (Data Offer), #wl_data_offer_listener
* \{ */
+static CLG_LogRef LOG_WL_DATA_OFFER = {"ghost.wl.handle.data_offer"};
+#define LOG (&LOG_WL_DATA_OFFER)
+
static void data_offer_handle_offer(void *data,
struct wl_data_offer * /*wl_data_offer*/,
const char *mime_type)
{
- static_cast<data_offer_t *>(data)->types.insert(mime_type);
+ CLOG_INFO(LOG, 2, "offer (mime_type=%s)", mime_type);
+ static_cast<GWL_DataOffer *>(data)->types.insert(mime_type);
}
static void data_offer_handle_source_actions(void *data,
struct wl_data_offer * /*wl_data_offer*/,
- uint32_t source_actions)
+ const uint32_t source_actions)
{
- static_cast<data_offer_t *>(data)->source_actions = source_actions;
+ CLOG_INFO(LOG, 2, "source_actions (%u)", source_actions);
+ static_cast<GWL_DataOffer *>(data)->source_actions = source_actions;
}
static void data_offer_handle_action(void *data,
struct wl_data_offer * /*wl_data_offer*/,
- uint32_t dnd_action)
+ const uint32_t dnd_action)
{
- static_cast<data_offer_t *>(data)->dnd_action = dnd_action;
+ CLOG_INFO(LOG, 2, "actions (%u)", dnd_action);
+ static_cast<GWL_DataOffer *>(data)->dnd_action = dnd_action;
}
static const struct wl_data_offer_listener data_offer_listener = {
@@ -773,34 +1121,48 @@ static const struct wl_data_offer_listener data_offer_listener = {
data_offer_handle_action,
};
+#undef LOG
+
/** \} */
/* -------------------------------------------------------------------- */
/** \name Listener (Data Device), #wl_data_device_listener
* \{ */
+static CLG_LogRef LOG_WL_DATA_DEVICE = {"ghost.wl.handle.data_device"};
+#define LOG (&LOG_WL_DATA_DEVICE)
+
static void data_device_handle_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;
+ CLOG_INFO(LOG, 2, "data_offer");
+
+ GWL_DataOffer *data_offer = new GWL_DataOffer;
data_offer->id = id;
wl_data_offer_add_listener(id, &data_offer_listener, data_offer);
}
static void data_device_handle_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,
+ const uint32_t serial,
+ struct wl_surface *wl_surface,
+ const wl_fixed_t x,
+ const wl_fixed_t y,
struct wl_data_offer *id)
{
- input_t *input = static_cast<input_t *>(data);
- std::lock_guard lock{input->data_offer_dnd_mutex};
+ if (!ghost_wl_surface_own(wl_surface)) {
+ CLOG_INFO(LOG, 2, "enter (skipped)");
+ return;
+ }
+ CLOG_INFO(LOG, 2, "enter");
- 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;
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+ std::lock_guard lock{seat->data_offer_dnd_mutex};
+
+ delete seat->data_offer_dnd;
+ seat->data_offer_dnd = static_cast<GWL_DataOffer *>(wl_data_offer_get_user_data(id));
+ GWL_DataOffer *data_offer = seat->data_offer_dnd;
data_offer->in_use.store(true);
data_offer->dnd.xy[0] = x;
@@ -815,88 +1177,105 @@ static void data_device_handle_enter(void *data,
wl_data_offer_accept(id, serial, type.c_str());
}
- input->focus_dnd = surface;
- dnd_events(input, GHOST_kEventDraggingEntered);
+ seat->wl_surface_focus_dnd = wl_surface;
+ dnd_events(seat, GHOST_kEventDraggingEntered);
}
static void data_device_handle_leave(void *data, struct wl_data_device * /*wl_data_device*/)
{
- input_t *input = static_cast<input_t *>(data);
- std::lock_guard lock{input->data_offer_dnd_mutex};
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+ std::lock_guard lock{seat->data_offer_dnd_mutex};
+
+ CLOG_INFO(LOG, 2, "leave");
- dnd_events(input, GHOST_kEventDraggingExited);
- input->focus_dnd = nullptr;
+ dnd_events(seat, GHOST_kEventDraggingExited);
+ seat->wl_surface_focus_dnd = nullptr;
- 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;
+ if (seat->data_offer_dnd && !seat->data_offer_dnd->in_use.load()) {
+ wl_data_offer_destroy(seat->data_offer_dnd->id);
+ delete seat->data_offer_dnd;
+ seat->data_offer_dnd = nullptr;
}
}
static void data_device_handle_motion(void *data,
struct wl_data_device * /*wl_data_device*/,
- uint32_t /*time*/,
- wl_fixed_t x,
- wl_fixed_t y)
+ const uint32_t /*time*/,
+ const wl_fixed_t x,
+ const wl_fixed_t y)
{
- input_t *input = static_cast<input_t *>(data);
- std::lock_guard lock{input->data_offer_dnd_mutex};
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+ std::lock_guard lock{seat->data_offer_dnd_mutex};
+
+ CLOG_INFO(LOG, 2, "motion");
- input->data_offer_dnd->dnd.xy[0] = x;
- input->data_offer_dnd->dnd.xy[1] = y;
+ seat->data_offer_dnd->dnd.xy[0] = x;
+ seat->data_offer_dnd->dnd.xy[1] = y;
- dnd_events(input, GHOST_kEventDraggingUpdated);
+ dnd_events(seat, GHOST_kEventDraggingUpdated);
}
static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_data_device*/)
{
- input_t *input = static_cast<input_t *>(data);
- std::lock_guard lock{input->data_offer_dnd_mutex};
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+ std::lock_guard lock{seat->data_offer_dnd_mutex};
- data_offer_t *data_offer = input->data_offer_dnd;
+ GWL_DataOffer *data_offer = seat->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_fn = [](input_t *const input,
- data_offer_t *data_offer,
- wl_surface *surface,
+ CLOG_INFO(LOG, 2, "drop mime_recieve=%s", mime_receive.c_str());
+
+ auto read_uris_fn = [](GWL_Seat *const seat,
+ GWL_DataOffer *data_offer,
+ wl_surface *wl_surface,
const std::string mime_receive) {
- const wl_fixed_t xy[2] = {data_offer->dnd.xy[0], data_offer->dnd.xy[1]};
+ const wl_fixed_t xy[2] = {UNPACK2(data_offer->dnd.xy)};
const std::string data = read_pipe(data_offer, mime_receive, nullptr);
+ CLOG_INFO(
+ LOG, 2, "drop_read_uris mime_receive=%s, data=%s", mime_receive.c_str(), data.c_str());
+
wl_data_offer_finish(data_offer->id);
wl_data_offer_destroy(data_offer->id);
+ if (seat->data_offer_dnd == data_offer) {
+ seat->data_offer_dnd = nullptr;
+ }
delete data_offer;
data_offer = nullptr;
- GHOST_SystemWayland *const system = input->system;
+ GHOST_SystemWayland *const system = seat->system;
if (mime_receive == mime_text_uri) {
static constexpr const char *file_proto = "file://";
- static constexpr const char *crlf = "\r\n";
-
- GHOST_WindowWayland *win = window_from_surface(surface);
- GHOST_ASSERT(win != nullptr, "Unable to find window for drop event from surface");
+ /* NOTE: some applications CRLF (`\r\n`) GTK3 for e.g. & others don't `pcmanfm-qt`.
+ * So support both, once `\n` is found, strip the preceding `\r` if found. */
+ static constexpr const char *lf = "\n";
+ GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface);
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;
+ pos = data.find(lf, pos);
if (pos == std::string::npos) {
break;
}
+ /* Account for 'CRLF' case. */
+ size_t end = pos;
+ if (data[end - 1] == '\r') {
+ end -= 1;
+ }
uris.push_back(data.substr(start, end - start));
+ CLOG_INFO(LOG, 2, "drop_read_uris pos=%zu, text_uri=\"%s\"", start, uris.back().c_str());
}
GHOST_TStringArray *flist = static_cast<GHOST_TStringArray *>(
@@ -904,10 +1283,10 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat
flist->count = int(uris.size());
flist->strings = static_cast<uint8_t **>(malloc(uris.size() * sizeof(uint8_t *)));
for (size_t i = 0; i < uris.size(); i++) {
- flist->strings[i] = static_cast<uint8_t *>(malloc((uris[i].size() + 1) * sizeof(uint8_t)));
- memcpy(flist->strings[i], uris[i].data(), uris[i].size() + 1);
+ flist->strings[i] = (uint8_t *)GHOST_URL_decode_alloc(uris[i].c_str());
}
+ CLOG_INFO(LOG, 2, "drop_read_uris_fn file_count=%d", flist->count);
const wl_fixed_t scale = win->scale();
system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(),
GHOST_kEventDraggingDropDone,
@@ -917,16 +1296,18 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat
wl_fixed_to_int(scale * xy[1]),
flist));
}
- else if (mime_receive == mime_text_plain || mime_receive == mime_text_utf8) {
+ else if (ELEM(mime_receive, mime_text_plain, mime_text_utf8)) {
/* TODO: enable use of internal functions 'txt_insert_buf' and
* 'text_update_edited' to behave like dropped text was pasted. */
+ CLOG_INFO(LOG, 2, "drop_read_uris_fn (text_plain, text_utf8), unhandled!");
}
wl_display_roundtrip(system->display());
};
- /* Pass in `input->focus_dnd` instead of accessing it from `input` since the leave callback
- * (#data_device_leave) will clear the value once this function starts. */
- std::thread read_thread(read_uris_fn, input, data_offer, input->focus_dnd, mime_receive);
+ /* Pass in `seat->wl_surface_focus_dnd` instead of accessing it from `seat` since the leave
+ * callback (#data_device_handle_leave) will clear the value once this function starts. */
+ std::thread read_thread(
+ read_uris_fn, seat, data_offer, seat->wl_surface_focus_dnd, mime_receive);
read_thread.detach();
}
@@ -934,32 +1315,35 @@ static void data_device_handle_selection(void *data,
struct wl_data_device * /*wl_data_device*/,
struct wl_data_offer *id)
{
- input_t *input = static_cast<input_t *>(data);
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
- std::lock_guard lock{input->data_offer_copy_paste_mutex};
+ std::lock_guard lock{seat->data_offer_copy_paste_mutex};
- data_offer_t *data_offer = input->data_offer_copy_paste;
+ GWL_DataOffer *data_offer = seat->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;
+ seat->data_offer_copy_paste = nullptr;
}
if (id == nullptr) {
+ CLOG_INFO(LOG, 2, "selection: (skipped)");
return;
}
+ CLOG_INFO(LOG, 2, "selection");
/* 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;
+ data_offer = static_cast<GWL_DataOffer *>(wl_data_offer_get_user_data(id));
+ seat->data_offer_copy_paste = data_offer;
- auto read_selection_fn = [](input_t *input) {
- GHOST_SystemWayland *const system = input->system;
- input->data_offer_copy_paste_mutex.lock();
+ auto read_selection_fn = [](GWL_Seat *seat) {
+ GHOST_SystemWayland *const system = seat->system;
+ seat->data_offer_copy_paste_mutex.lock();
- data_offer_t *data_offer = input->data_offer_copy_paste;
+ GWL_DataOffer *data_offer = seat->data_offer_copy_paste;
std::string mime_receive;
for (const std::string type : {mime_text_utf8, mime_text_plain}) {
if (data_offer->types.count(type)) {
@@ -968,15 +1352,15 @@ static void data_device_handle_selection(void *data,
}
}
const std::string data = read_pipe(
- data_offer, mime_receive, &input->data_offer_copy_paste_mutex);
+ data_offer, mime_receive, &seat->data_offer_copy_paste_mutex);
{
std::lock_guard lock{system_selection_mutex};
- system->setSelection(data);
+ system->selection_set(data);
}
};
- std::thread read_thread(read_selection_fn, input);
+ std::thread read_thread(read_selection_fn, seat);
read_thread.detach();
}
@@ -989,59 +1373,62 @@ static const struct wl_data_device_listener data_device_listener = {
data_device_handle_selection,
};
+#undef LOG
+
/** \} */
/* -------------------------------------------------------------------- */
/** \name Listener (Buffer), #wl_buffer_listener
* \{ */
+static CLG_LogRef LOG_WL_CURSOR_BUFFER = {"ghost.wl.handle.cursor_buffer"};
+#define LOG (&LOG_WL_CURSOR_BUFFER)
+
static void cursor_buffer_handle_release(void *data, struct wl_buffer *wl_buffer)
{
- cursor_t *cursor = static_cast<cursor_t *>(data);
+ CLOG_INFO(LOG, 2, "release");
+ GWL_Cursor *cursor = static_cast<GWL_Cursor *>(data);
wl_buffer_destroy(wl_buffer);
if (wl_buffer == cursor->wl_buffer) {
- /* the mapped buffer was from a custom cursor */
+ /* The mapped buffer was from a custom cursor. */
cursor->wl_buffer = nullptr;
}
}
-const struct wl_buffer_listener cursor_buffer_listener = {
+static const struct wl_buffer_listener cursor_buffer_listener = {
cursor_buffer_handle_release,
};
+#undef LOG
+
/** \} */
/* -------------------------------------------------------------------- */
/** \name Listener (Surface), #wl_surface_listener
* \{ */
-static GHOST_WindowWayland *window_from_surface(struct wl_surface *surface)
-{
- if (surface) {
- for (GHOST_IWindow *iwin : window_manager->getWindows()) {
- GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(iwin);
- if (surface == win->surface()) {
- return win;
- }
- }
- }
- return nullptr;
-}
+static CLG_LogRef LOG_WL_CURSOR_SURFACE = {"ghost.wl.handle.cursor_surface"};
+#define LOG (&LOG_WL_CURSOR_SURFACE)
-static bool update_cursor_scale(cursor_t &cursor, wl_shm *shm)
+static bool update_cursor_scale(GWL_Cursor &cursor,
+ wl_shm *shm,
+ GWL_SeatStatePointer *seat_state_pointer,
+ wl_surface *wl_cursor_surface)
{
int scale = 0;
- for (const output_t *output : cursor.outputs) {
+ for (const GWL_Output *output : seat_state_pointer->outputs) {
if (output->scale > scale) {
scale = output->scale;
}
}
- if (scale > 0 && cursor.scale != scale) {
- cursor.scale = scale;
- wl_surface_set_buffer_scale(cursor.wl_surface, scale);
+ if (scale > 0 && seat_state_pointer->theme_scale != scale) {
+ seat_state_pointer->theme_scale = scale;
+ if (!cursor.is_custom) {
+ wl_surface_set_buffer_scale(wl_cursor_surface, scale);
+ }
wl_cursor_theme_destroy(cursor.wl_theme);
cursor.wl_theme = wl_cursor_theme_load(cursor.theme_name.c_str(), scale * cursor.size, shm);
return true;
@@ -1050,125 +1437,145 @@ static bool update_cursor_scale(cursor_t &cursor, wl_shm *shm)
}
static void cursor_surface_handle_enter(void *data,
- struct wl_surface * /*wl_surface*/,
- struct wl_output *output)
+ struct wl_surface *wl_surface,
+ struct wl_output *wl_output)
{
- input_t *input = static_cast<input_t *>(data);
- for (const output_t *reg_output : input->system->outputs()) {
- if (reg_output->wl_output == output) {
- input->cursor.outputs.insert(reg_output);
- }
+ if (!ghost_wl_output_own(wl_output)) {
+ CLOG_INFO(LOG, 2, "handle_enter (skipped)");
+ return;
}
- update_cursor_scale(input->cursor, input->system->shm());
+ CLOG_INFO(LOG, 2, "handle_enter");
+
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+ GWL_SeatStatePointer *seat_state_pointer = seat_state_pointer_from_cursor_surface(seat,
+ wl_surface);
+ const GWL_Output *reg_output = ghost_wl_output_user_data(wl_output);
+ seat_state_pointer->outputs.insert(reg_output);
+ update_cursor_scale(seat->cursor, seat->system->shm(), seat_state_pointer, wl_surface);
}
static void cursor_surface_handle_leave(void *data,
- struct wl_surface * /*wl_surface*/,
- struct wl_output *output)
+ struct wl_surface *wl_surface,
+ struct wl_output *wl_output)
{
- input_t *input = static_cast<input_t *>(data);
- for (const output_t *reg_output : input->system->outputs()) {
- if (reg_output->wl_output == output) {
- input->cursor.outputs.erase(reg_output);
- }
+ if (!(wl_output && ghost_wl_output_own(wl_output))) {
+ CLOG_INFO(LOG, 2, "handle_leave (skipped)");
+ return;
}
- update_cursor_scale(input->cursor, input->system->shm());
+ CLOG_INFO(LOG, 2, "handle_leave");
+
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+ GWL_SeatStatePointer *seat_state_pointer = seat_state_pointer_from_cursor_surface(seat,
+ wl_surface);
+ const GWL_Output *reg_output = ghost_wl_output_user_data(wl_output);
+ seat_state_pointer->outputs.erase(reg_output);
+ update_cursor_scale(seat->cursor, seat->system->shm(), seat_state_pointer, wl_surface);
}
-struct wl_surface_listener cursor_surface_listener = {
+static const struct wl_surface_listener cursor_surface_listener = {
cursor_surface_handle_enter,
cursor_surface_handle_leave,
};
+#undef LOG
+
/** \} */
/* -------------------------------------------------------------------- */
/** \name Listener (Pointer), #wl_pointer_listener
* \{ */
+static CLG_LogRef LOG_WL_POINTER = {"ghost.wl.handle.pointer"};
+#define LOG (&LOG_WL_POINTER)
+
static void pointer_handle_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)
+ const uint32_t serial,
+ struct wl_surface *wl_surface,
+ const wl_fixed_t surface_x,
+ const wl_fixed_t surface_y)
{
- GHOST_WindowWayland *win = window_from_surface(surface);
- if (!win) {
+ if (!ghost_wl_surface_own(wl_surface)) {
+ CLOG_INFO(LOG, 2, "enter (skipped)");
return;
}
+ CLOG_INFO(LOG, 2, "enter");
+
+ GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface);
win->activate();
- input_t *input = static_cast<input_t *>(data);
- input->pointer_serial = serial;
- input->cursor_serial = serial;
- input->xy[0] = surface_x;
- input->xy[1] = surface_y;
- input->focus_pointer = surface;
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+ seat->cursor_source_serial = serial;
+ seat->pointer.serial = serial;
+ seat->pointer.xy[0] = surface_x;
+ seat->pointer.xy[1] = surface_y;
+ seat->pointer.wl_surface = wl_surface;
win->setCursorShape(win->getCursorShape());
const wl_fixed_t scale = win->scale();
- input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
- GHOST_kEventCursorMove,
- static_cast<GHOST_WindowWayland *>(win),
- wl_fixed_to_int(scale * input->xy[0]),
- wl_fixed_to_int(scale * input->xy[1]),
- GHOST_TABLET_DATA_NONE));
+ seat->system->pushEvent(new GHOST_EventCursor(seat->system->getMilliSeconds(),
+ GHOST_kEventCursorMove,
+ win,
+ wl_fixed_to_int(scale * seat->pointer.xy[0]),
+ wl_fixed_to_int(scale * seat->pointer.xy[1]),
+ GHOST_TABLET_DATA_NONE));
}
static void pointer_handle_leave(void *data,
struct wl_pointer * /*wl_pointer*/,
- uint32_t /*serial*/,
- struct wl_surface *surface)
+ const uint32_t /*serial*/,
+ struct wl_surface *wl_surface)
{
- GHOST_IWindow *win = window_from_surface(surface);
- if (!win) {
- return;
+ /* First clear the `pointer.wl_surface`, since the window won't exist when closing the window. */
+ static_cast<GWL_Seat *>(data)->pointer.wl_surface = nullptr;
+ if (wl_surface && ghost_wl_surface_own(wl_surface)) {
+ CLOG_INFO(LOG, 2, "leave");
+ GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface);
+ win->deactivate();
+ }
+ else {
+ CLOG_INFO(LOG, 2, "leave (skipped)");
}
-
- static_cast<input_t *>(data)->focus_pointer = nullptr;
- static_cast<GHOST_WindowWayland *>(win)->deactivate();
}
static void pointer_handle_motion(void *data,
struct wl_pointer * /*wl_pointer*/,
- uint32_t /*time*/,
- wl_fixed_t surface_x,
- wl_fixed_t surface_y)
+ const uint32_t /*time*/,
+ const wl_fixed_t surface_x,
+ const wl_fixed_t surface_y)
{
- input_t *input = static_cast<input_t *>(data);
- GHOST_WindowWayland *win = window_from_surface(input->focus_pointer);
- if (!win) {
- return;
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+ seat->pointer.xy[0] = surface_x;
+ seat->pointer.xy[1] = surface_y;
+
+ if (wl_surface *wl_surface_focus = seat->pointer.wl_surface) {
+ CLOG_INFO(LOG, 2, "motion");
+ GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
+ const wl_fixed_t scale = win->scale();
+ seat->system->pushEvent(new GHOST_EventCursor(seat->system->getMilliSeconds(),
+ GHOST_kEventCursorMove,
+ win,
+ wl_fixed_to_int(scale * seat->pointer.xy[0]),
+ wl_fixed_to_int(scale * seat->pointer.xy[1]),
+ GHOST_TABLET_DATA_NONE));
+ }
+ else {
+ CLOG_INFO(LOG, 2, "motion (skipped)");
}
-
- input->xy[0] = surface_x;
- input->xy[1] = surface_y;
-
- const wl_fixed_t scale = win->scale();
- input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
- GHOST_kEventCursorMove,
- win,
- wl_fixed_to_int(scale * input->xy[0]),
- wl_fixed_to_int(scale * input->xy[1]),
- GHOST_TABLET_DATA_NONE));
}
static void pointer_handle_button(void *data,
struct wl_pointer * /*wl_pointer*/,
- uint32_t serial,
- uint32_t /*time*/,
- uint32_t button,
- uint32_t state)
+ const uint32_t serial,
+ const uint32_t /*time*/,
+ const uint32_t button,
+ const uint32_t state)
{
- input_t *input = static_cast<input_t *>(data);
- GHOST_IWindow *win = window_from_surface(input->focus_pointer);
- if (!win) {
- return;
- }
+ CLOG_INFO(LOG, 2, "button (button=%u, state=%u)", button, state);
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
GHOST_TEventType etype = GHOST_kEventUnknown;
switch (state) {
case WL_POINTER_BUTTON_STATE_RELEASED:
@@ -1179,7 +1586,7 @@ static void pointer_handle_button(void *data,
break;
}
- GHOST_TButtonMask ebutton = GHOST_kButtonMaskLeft;
+ GHOST_TButton ebutton = GHOST_kButtonMaskLeft;
switch (button) {
case BTN_LEFT:
ebutton = GHOST_kButtonMaskLeft;
@@ -1204,30 +1611,59 @@ static void pointer_handle_button(void *data,
break;
}
- input->data_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));
+ seat->data_source_serial = serial;
+ seat->pointer.buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED);
+
+ if (wl_surface *wl_surface_focus = seat->pointer.wl_surface) {
+ GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
+ seat->system->pushEvent(new GHOST_EventButton(
+ seat->system->getMilliSeconds(), etype, win, ebutton, GHOST_TABLET_DATA_NONE));
+ }
}
-static void pointer_handle_axis(void *data,
+static void pointer_handle_axis(void * /*data*/,
struct wl_pointer * /*wl_pointer*/,
- uint32_t /*time*/,
- uint32_t axis,
- wl_fixed_t value)
+ const uint32_t /*time*/,
+ const uint32_t axis,
+ const wl_fixed_t value)
{
- input_t *input = static_cast<input_t *>(data);
- GHOST_IWindow *win = window_from_surface(input->focus_pointer);
- if (!win) {
- return;
- }
+ CLOG_INFO(LOG, 2, "axis (axis=%u, value=%d)", axis, value);
+}
+static void pointer_handle_frame(void * /*data*/, struct wl_pointer * /*wl_pointer*/)
+{
+ CLOG_INFO(LOG, 2, "frame");
+}
+static void pointer_handle_axis_source(void * /*data*/,
+ struct wl_pointer * /*wl_pointer*/,
+ uint32_t axis_source)
+{
+ CLOG_INFO(LOG, 2, "axis_source (axis_source=%u)", axis_source);
+}
+static void pointer_handle_axis_stop(void * /*data*/,
+ struct wl_pointer * /*wl_pointer*/,
+ uint32_t /*time*/,
+ uint32_t axis)
+{
+ CLOG_INFO(LOG, 2, "axis_stop (axis=%u)", axis);
+}
+static void pointer_handle_axis_discrete(void *data,
+ struct wl_pointer * /*wl_pointer*/,
+ uint32_t axis,
+ int32_t discrete)
+{
+ CLOG_INFO(LOG, 2, "axis_discrete (axis=%u, discrete=%d)", axis, discrete);
+
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) {
return;
}
- input->system->pushEvent(
- new GHOST_EventWheel(input->system->getMilliSeconds(), win, std::signbit(value) ? +1 : -1));
+ if (wl_surface *wl_surface_focus = seat->pointer.wl_surface) {
+ GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
+ seat->system->pushEvent(new GHOST_EventWheel(
+ seat->system->getMilliSeconds(), win, std::signbit(discrete) ? +1 : -1));
+ }
}
static const struct wl_pointer_listener pointer_listener = {
@@ -1236,87 +1672,116 @@ static const struct wl_pointer_listener pointer_listener = {
pointer_handle_motion,
pointer_handle_button,
pointer_handle_axis,
+ pointer_handle_frame,
+ pointer_handle_axis_source,
+ pointer_handle_axis_stop,
+ pointer_handle_axis_discrete,
};
+#undef LOG
+
/** \} */
/* -------------------------------------------------------------------- */
/** \name Listener (Tablet Tool), #zwp_tablet_tool_v2_listener
* \{ */
+static CLG_LogRef LOG_WL_TABLET_TOOL = {"ghost.wl.handle.tablet_tool"};
+#define LOG (&LOG_WL_TABLET_TOOL)
+
static void tablet_tool_handle_type(void *data,
struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
- uint32_t tool_type)
+ const uint32_t tool_type)
{
- tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
+ CLOG_INFO(LOG, 2, "type (type=%u)", tool_type);
- tool_input->data.Active = tablet_tool_map_type((enum zwp_tablet_tool_v2_type)tool_type);
+ GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data);
+
+ tablet_tool->data.Active = tablet_tool_map_type((enum zwp_tablet_tool_v2_type)tool_type);
}
static void tablet_tool_handle_hardware_serial(void * /*data*/,
struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
- uint32_t /*hardware_serial_hi*/,
- uint32_t /*hardware_serial_lo*/)
+ const uint32_t /*hardware_serial_hi*/,
+ const uint32_t /*hardware_serial_lo*/)
{
+ CLOG_INFO(LOG, 2, "hardware_serial");
}
static void tablet_tool_handle_hardware_id_wacom(
void * /*data*/,
struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
- uint32_t /*hardware_id_hi*/,
- uint32_t /*hardware_id_lo*/)
+ const uint32_t /*hardware_id_hi*/,
+ const uint32_t /*hardware_id_lo*/)
{
+ CLOG_INFO(LOG, 2, "hardware_id_wacom");
}
static void tablet_tool_handle_capability(void * /*data*/,
struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
- uint32_t /*capability*/)
+ const uint32_t capability)
{
+ CLOG_INFO(LOG,
+ 2,
+ "capability (tilt=%d, distance=%d, rotation=%d, slider=%d, wheel=%d)",
+ (capability & ZWP_TABLET_TOOL_V2_CAPABILITY_TILT) != 0,
+ (capability & ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE) != 0,
+ (capability & ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION) != 0,
+ (capability & ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER) != 0,
+ (capability & ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL) != 0);
}
static void tablet_tool_handle_done(void * /*data*/,
struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/)
{
+ CLOG_INFO(LOG, 2, "done");
}
static void tablet_tool_handle_removed(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2)
{
- tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
- input_t *input = tool_input->input;
+ CLOG_INFO(LOG, 2, "removed");
+
+ GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data);
+ GWL_Seat *seat = tablet_tool->seat;
- if (tool_input->cursor_surface) {
- wl_surface_destroy(tool_input->cursor_surface);
+ if (tablet_tool->wl_surface_cursor) {
+ wl_surface_destroy(tablet_tool->wl_surface_cursor);
}
- input->tablet_tools.erase(zwp_tablet_tool_v2);
+ seat->tablet_tools.erase(zwp_tablet_tool_v2);
- delete tool_input;
+ delete tablet_tool;
}
static void tablet_tool_handle_proximity_in(void *data,
struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
- uint32_t serial,
+ const uint32_t serial,
struct zwp_tablet_v2 * /*tablet*/,
- struct wl_surface *surface)
+ struct wl_surface *wl_surface)
{
- tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
- input_t *input = tool_input->input;
+ if (!ghost_wl_surface_own(wl_surface)) {
+ CLOG_INFO(LOG, 2, "proximity_in (skipped)");
+ return;
+ }
+ CLOG_INFO(LOG, 2, "proximity_in");
+
+ GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data);
+ tablet_tool->proximity = true;
- input->focus_tablet = surface;
- input->tablet_serial = serial;
- input->cursor_serial = serial;
+ GWL_Seat *seat = tablet_tool->seat;
+ seat->cursor_source_serial = serial;
+ seat->tablet.wl_surface = wl_surface;
+ seat->tablet.serial = serial;
- input->data_source_serial = serial;
+ seat->data_source_serial = serial;
/* Update #GHOST_TabletData. */
- GHOST_TabletData &td = tool_input->data;
+ GHOST_TabletData &td = tablet_tool->data;
/* Reset, to avoid using stale tilt/pressure. */
td.Xtilt = 0.0f;
td.Ytilt = 0.0f;
/* In case pressure isn't supported. */
td.Pressure = 1.0f;
- GHOST_WindowWayland *win = window_from_surface(input->focus_tablet);
- if (!win) {
- return;
- }
+ GHOST_WindowWayland *win = ghost_wl_surface_user_data(seat->tablet.wl_surface);
+
win->activate();
win->setCursorShape(win->getCursorShape());
@@ -1324,158 +1789,145 @@ static void tablet_tool_handle_proximity_in(void *data,
static void tablet_tool_handle_proximity_out(void *data,
struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/)
{
- tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
- input_t *input = tool_input->input;
- input->focus_tablet = nullptr;
-
- GHOST_WindowWayland *win = window_from_surface(input->focus_tablet);
- if (!win) {
- return;
- }
- win->setCursorShape(win->getCursorShape());
+ CLOG_INFO(LOG, 2, "proximity_out");
+ GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data);
+ /* Defer clearing the wl_surface until the frame is handled.
+ * Without this, the frame can not access the wl_surface. */
+ tablet_tool->proximity = false;
}
static void tablet_tool_handle_down(void *data,
struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
- uint32_t serial)
+ const uint32_t serial)
{
- tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
- input_t *input = tool_input->input;
- GHOST_WindowWayland *win = window_from_surface(input->focus_tablet);
- if (!win) {
- return;
- }
+ CLOG_INFO(LOG, 2, "down");
+ GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data);
+ GWL_Seat *seat = tablet_tool->seat;
+ const GHOST_TButton ebutton = GHOST_kButtonMaskLeft;
const GHOST_TEventType etype = GHOST_kEventButtonDown;
- const GHOST_TButtonMask ebutton = GHOST_kButtonMaskLeft;
- input->data_source_serial = serial;
- input->buttons.set(ebutton, true);
- input->system->pushEvent(new GHOST_EventButton(
- input->system->getMilliSeconds(), etype, win, ebutton, tool_input->data));
+
+ seat->data_source_serial = serial;
+ seat->tablet.buttons.set(ebutton, true);
+
+ if (wl_surface *wl_surface_focus = seat->tablet.wl_surface) {
+ GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
+ seat->system->pushEvent(new GHOST_EventButton(
+ seat->system->getMilliSeconds(), etype, win, ebutton, tablet_tool->data));
+ }
}
static void tablet_tool_handle_up(void *data, struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/)
{
- tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
- input_t *input = tool_input->input;
- GHOST_WindowWayland *win = window_from_surface(input->focus_tablet);
- if (!win) {
- return;
- }
+ CLOG_INFO(LOG, 2, "up");
+ GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data);
+ GWL_Seat *seat = tablet_tool->seat;
+ const GHOST_TButton ebutton = GHOST_kButtonMaskLeft;
const GHOST_TEventType etype = GHOST_kEventButtonUp;
- const GHOST_TButtonMask ebutton = GHOST_kButtonMaskLeft;
- input->buttons.set(ebutton, false);
- input->system->pushEvent(new GHOST_EventButton(
- input->system->getMilliSeconds(), etype, win, ebutton, tool_input->data));
+
+ seat->tablet.buttons.set(ebutton, false);
+
+ if (wl_surface *wl_surface_focus = seat->tablet.wl_surface) {
+ GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
+ seat->system->pushEvent(new GHOST_EventButton(
+ seat->system->getMilliSeconds(), etype, win, ebutton, tablet_tool->data));
+ }
}
static void tablet_tool_handle_motion(void *data,
struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
- wl_fixed_t x,
- wl_fixed_t y)
+ const wl_fixed_t x,
+ const wl_fixed_t y)
{
- tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
- input_t *input = tool_input->input;
- GHOST_WindowWayland *win = window_from_surface(input->focus_tablet);
- if (!win) {
- return;
- }
+ CLOG_INFO(LOG, 2, "motion");
- input->xy[0] = x;
- input->xy[1] = y;
+ GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data);
+ GWL_Seat *seat = tablet_tool->seat;
- const wl_fixed_t scale = win->scale();
- input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
- GHOST_kEventCursorMove,
- win,
- wl_fixed_to_int(scale * input->xy[0]),
- wl_fixed_to_int(scale * input->xy[1]),
- tool_input->data));
+ seat->tablet.xy[0] = x;
+ seat->tablet.xy[1] = y;
+
+ /* NOTE: #tablet_tool_handle_frame generates the event (with updated pressure, tilt... etc). */
}
static void tablet_tool_handle_pressure(void *data,
struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
- uint32_t pressure)
+ const uint32_t pressure)
{
- tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
- input_t *input = tool_input->input;
- GHOST_WindowWayland *win = window_from_surface(input->focus_tablet);
- if (!win) {
- return;
- }
+ const float pressure_unit = (float)pressure / 65535;
+ CLOG_INFO(LOG, 2, "pressure (%.4f)", pressure_unit);
- GHOST_TabletData &td = tool_input->data;
- td.Pressure = (float)pressure / 65535;
+ GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data);
+ GHOST_TabletData &td = tablet_tool->data;
+ td.Pressure = pressure_unit;
}
static void tablet_tool_handle_distance(void * /*data*/,
struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
- uint32_t /*distance*/)
+ const uint32_t distance)
{
+ CLOG_INFO(LOG, 2, "distance (distance=%u)", distance);
}
+
static void tablet_tool_handle_tilt(void *data,
struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
- wl_fixed_t tilt_x,
- wl_fixed_t tilt_y)
+ const wl_fixed_t tilt_x,
+ const wl_fixed_t tilt_y)
{
- tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
- input_t *input = tool_input->input;
- GHOST_WindowWayland *win = window_from_surface(input->focus_tablet);
- if (!win) {
- return;
- }
-
- GHOST_TabletData &td = tool_input->data;
/* Map degrees to `-1.0..1.0`. */
- td.Xtilt = wl_fixed_to_double(tilt_x) / 90.0f;
- td.Ytilt = wl_fixed_to_double(tilt_y) / 90.0f;
- td.Xtilt = td.Xtilt < -1.0f ? -1.0f : (td.Xtilt > 1.0f ? 1.0f : td.Xtilt);
- td.Ytilt = td.Ytilt < -1.0f ? -1.0f : (td.Ytilt > 1.0f ? 1.0f : td.Ytilt);
+ const float tilt_unit[2] = {
+ (float)(wl_fixed_to_double(tilt_x) / 90.0),
+ (float)(wl_fixed_to_double(tilt_y) / 90.0),
+ };
+ CLOG_INFO(LOG, 2, "tilt (x=%.4f, y=%.4f)", UNPACK2(tilt_unit));
+ GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data);
+ GHOST_TabletData &td = tablet_tool->data;
+ td.Xtilt = tilt_unit[0];
+ td.Ytilt = tilt_unit[1];
+ CLAMP(td.Xtilt, -1.0f, 1.0f);
+ CLAMP(td.Ytilt, -1.0f, 1.0f);
}
static void tablet_tool_handle_rotation(void * /*data*/,
struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
- wl_fixed_t /*degrees*/)
+ const wl_fixed_t degrees)
{
- /* Pass. */
+ CLOG_INFO(LOG, 2, "rotation (degrees=%.4f)", wl_fixed_to_double(degrees));
}
static void tablet_tool_handle_slider(void * /*data*/,
struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
- int32_t /*position*/)
+ const int32_t position)
{
- /* Pass. */
+ CLOG_INFO(LOG, 2, "slider (position=%d)", position);
}
static void tablet_tool_handle_wheel(void *data,
struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
- wl_fixed_t /*degrees*/,
- int32_t clicks)
+ const wl_fixed_t /*degrees*/,
+ const int32_t clicks)
{
if (clicks == 0) {
return;
}
+ CLOG_INFO(LOG, 2, "wheel (clicks=%d)", clicks);
- tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
- input_t *input = tool_input->input;
- GHOST_WindowWayland *win = window_from_surface(input->focus_tablet);
- if (!win) {
- return;
+ GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data);
+ GWL_Seat *seat = tablet_tool->seat;
+ if (wl_surface *wl_surface_focus = seat->tablet.wl_surface) {
+ GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
+ seat->system->pushEvent(new GHOST_EventWheel(seat->system->getMilliSeconds(), win, clicks));
}
-
- input->system->pushEvent(new GHOST_EventWheel(input->system->getMilliSeconds(), win, clicks));
}
static void tablet_tool_handle_button(void *data,
struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
- uint32_t serial,
- uint32_t button,
- uint32_t state)
+ const uint32_t serial,
+ const uint32_t button,
+ const uint32_t state)
{
- tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(data);
- input_t *input = tool_input->input;
- GHOST_WindowWayland *win = window_from_surface(input->focus_tablet);
- if (!win) {
- return;
- }
+ CLOG_INFO(LOG, 2, "button (button=%u, state=%u)", button, state);
+
+ GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data);
+ GWL_Seat *seat = tablet_tool->seat;
GHOST_TEventType etype = GHOST_kEventUnknown;
switch (state) {
@@ -1487,7 +1939,7 @@ static void tablet_tool_handle_button(void *data,
break;
}
- GHOST_TButtonMask ebutton = GHOST_kButtonMaskLeft;
+ GHOST_TButton ebutton = GHOST_kButtonMaskLeft;
switch (button) {
case BTN_STYLUS:
ebutton = GHOST_kButtonMaskRight;
@@ -1500,15 +1952,42 @@ static void tablet_tool_handle_button(void *data,
break;
}
- input->data_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, tool_input->data));
+ seat->data_source_serial = serial;
+ seat->tablet.buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED);
+
+ if (wl_surface *wl_surface_focus = seat->tablet.wl_surface) {
+ GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
+ seat->system->pushEvent(new GHOST_EventButton(
+ seat->system->getMilliSeconds(), etype, win, ebutton, tablet_tool->data));
+ }
}
-static void tablet_tool_handle_frame(void * /*data*/,
+static void tablet_tool_handle_frame(void *data,
struct zwp_tablet_tool_v2 * /*zwp_tablet_tool_v2*/,
- uint32_t /*time*/)
+ const uint32_t /*time*/)
{
+ CLOG_INFO(LOG, 2, "frame");
+
+ GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data);
+ GWL_Seat *seat = tablet_tool->seat;
+
+ /* No need to check the surfaces origin, it's already known to be owned by GHOST. */
+ if (wl_surface *wl_surface_focus = seat->tablet.wl_surface) {
+ GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
+ const wl_fixed_t scale = win->scale();
+ seat->system->pushEvent(new GHOST_EventCursor(seat->system->getMilliSeconds(),
+ GHOST_kEventCursorMove,
+ win,
+ wl_fixed_to_int(scale * seat->tablet.xy[0]),
+ wl_fixed_to_int(scale * seat->tablet.xy[1]),
+ tablet_tool->data));
+ if (tablet_tool->proximity == false) {
+ win->setCursorShape(win->getCursorShape());
+ }
+ }
+
+ if (tablet_tool->proximity == false) {
+ seat->tablet.wl_surface = nullptr;
+ }
}
static const struct zwp_tablet_tool_v2_listener tablet_tool_listner = {
@@ -1533,61 +2012,79 @@ static const struct zwp_tablet_tool_v2_listener tablet_tool_listner = {
tablet_tool_handle_frame,
};
+#undef LOG
+
/** \} */
/* -------------------------------------------------------------------- */
/** \name Listener (Table Seat), #zwp_tablet_seat_v2_listener
* \{ */
+static CLG_LogRef LOG_WL_TABLET_SEAT = {"ghost.wl.handle.tablet_seat"};
+#define LOG (&LOG_WL_TABLET_SEAT)
+
static void tablet_seat_handle_tablet_added(void * /*data*/,
struct zwp_tablet_seat_v2 * /*zwp_tablet_seat_v2*/,
- struct zwp_tablet_v2 * /*id*/)
+ struct zwp_tablet_v2 *id)
{
- /* Pass. */
+ CLOG_INFO(LOG, 2, "tablet_added (id=%p)", id);
}
static void tablet_seat_handle_tool_added(void *data,
struct zwp_tablet_seat_v2 * /*zwp_tablet_seat_v2*/,
struct zwp_tablet_tool_v2 *id)
{
- input_t *input = static_cast<input_t *>(data);
- tablet_tool_input_t *tool_input = new tablet_tool_input_t();
- tool_input->input = input;
+ CLOG_INFO(LOG, 2, "tool_added (id=%p)", id);
+
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+ GWL_TabletTool *tablet_tool = new GWL_TabletTool();
+ tablet_tool->seat = seat;
- /* Every tool has it's own cursor surface. */
- tool_input->cursor_surface = wl_compositor_create_surface(input->system->compositor());
- wl_surface_add_listener(tool_input->cursor_surface, &cursor_surface_listener, (void *)input);
+ /* Every tool has it's own cursor wl_surface. */
+ tablet_tool->wl_surface_cursor = wl_compositor_create_surface(seat->system->compositor());
+ ghost_wl_surface_tag_cursor_tablet(tablet_tool->wl_surface_cursor);
- zwp_tablet_tool_v2_add_listener(id, &tablet_tool_listner, tool_input);
+ wl_surface_add_listener(tablet_tool->wl_surface_cursor, &cursor_surface_listener, (void *)seat);
- input->tablet_tools.insert(id);
+ zwp_tablet_tool_v2_add_listener(id, &tablet_tool_listner, tablet_tool);
+
+ seat->tablet_tools.insert(id);
}
static void tablet_seat_handle_pad_added(void * /*data*/,
struct zwp_tablet_seat_v2 * /*zwp_tablet_seat_v2*/,
- struct zwp_tablet_pad_v2 * /*id*/)
+ struct zwp_tablet_pad_v2 *id)
{
- /* Pass. */
+ CLOG_INFO(LOG, 2, "pad_added (id=%p)", id);
}
-const struct zwp_tablet_seat_v2_listener tablet_seat_listener = {
+static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = {
tablet_seat_handle_tablet_added,
tablet_seat_handle_tool_added,
tablet_seat_handle_pad_added,
};
+#undef LOG
+
/** \} */
/* -------------------------------------------------------------------- */
/** \name Listener (Keyboard), #wl_keyboard_listener
* \{ */
-static void keyboard_handle_keymap(
- void *data, struct wl_keyboard * /*wl_keyboard*/, uint32_t format, int32_t fd, uint32_t size)
+static CLG_LogRef LOG_WL_KEYBOARD = {"ghost.wl.handle.keyboard"};
+#define LOG (&LOG_WL_KEYBOARD)
+
+static void keyboard_handle_keymap(void *data,
+ struct wl_keyboard * /*wl_keyboard*/,
+ const uint32_t format,
+ const int32_t fd,
+ const uint32_t size)
{
- input_t *input = static_cast<input_t *>(data);
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
if ((!data) || (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)) {
+ CLOG_INFO(LOG, 2, "keymap (no data or wrong version)");
close(fd);
return;
}
@@ -1599,54 +2096,134 @@ static void keyboard_handle_keymap(
}
struct xkb_keymap *keymap = xkb_keymap_new_from_string(
- input->xkb_context, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
+ seat->xkb_context, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
munmap(map_str, size);
close(fd);
if (!keymap) {
+ CLOG_INFO(LOG, 2, "keymap (not found)");
return;
}
- struct xkb_state *xkb_state_next = xkb_state_new(keymap);
- if (xkb_state_next) {
- if (input->xkb_state) {
- xkb_state_unref(input->xkb_state);
+ CLOG_INFO(LOG, 2, "keymap");
+
+ /* In practice we can assume `xkb_state_new` always succeeds. */
+ xkb_state_unref(seat->xkb_state);
+ seat->xkb_state = xkb_state_new(keymap);
+
+ xkb_state_unref(seat->xkb_state_empty);
+ seat->xkb_state_empty = xkb_state_new(keymap);
+
+ xkb_state_unref(seat->xkb_state_empty_with_numlock);
+ seat->xkb_state_empty_with_numlock = nullptr;
+
+ {
+ 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) {
+ seat->xkb_state_empty_with_numlock = xkb_state_new(keymap);
+ xkb_state_update_mask(
+ seat->xkb_state_empty_with_numlock, (1 << mod2), 0, (1 << num), 0, 0, 0);
}
- input->xkb_state = xkb_state_next;
}
+
+ seat->xkb_keymap_mod_index.shift = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_SHIFT);
+ seat->xkb_keymap_mod_index.caps = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CAPS);
+ seat->xkb_keymap_mod_index.ctrl = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CTRL);
+ seat->xkb_keymap_mod_index.alt = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_ALT);
+ seat->xkb_keymap_mod_index.num = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_NUM);
+ seat->xkb_keymap_mod_index.logo = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_LOGO);
+
xkb_keymap_unref(keymap);
}
/**
* Enter event.
*
- * Notification that this seat's keyboard focus is on a certain
- * surface.
+ * Notification that this seat's keyboard focus is on a certain wl_surface.
*/
static void keyboard_handle_enter(void *data,
struct wl_keyboard * /*wl_keyboard*/,
- uint32_t /*serial*/,
- struct wl_surface *surface,
- struct wl_array * /*keys*/)
+ const uint32_t serial,
+ struct wl_surface *wl_surface,
+ struct wl_array *keys)
{
- if (surface != nullptr) {
- static_cast<input_t *>(data)->focus_keyboard = surface;
+ if (!ghost_wl_surface_own(wl_surface)) {
+ CLOG_INFO(LOG, 2, "enter (skipped)");
+ return;
+ }
+ CLOG_INFO(LOG, 2, "enter");
+
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+ seat->keyboard.serial = serial;
+ seat->keyboard.wl_surface = wl_surface;
+
+ if (keys->size != 0) {
+ /* If there are any keys held when activating the window,
+ * modifiers will be compared against the seat state,
+ * only enabling modifiers that were previously disabled. */
+
+ const xkb_mod_mask_t state = xkb_state_serialize_mods(seat->xkb_state, XKB_STATE_MODS_ALL);
+ uint32_t *key;
+ WL_ARRAY_FOR_EACH (key, keys) {
+ const xkb_keycode_t key_code = *key + EVDEV_OFFSET;
+ const xkb_keysym_t sym = xkb_state_key_get_one_sym(seat->xkb_state, key_code);
+ GHOST_TKey gkey = GHOST_kKeyUnknown;
+
+#define MOD_TEST(state, mod) ((mod != XKB_MOD_INVALID) && (state & (1 << mod)))
+#define MOD_TEST_CASE(xkb_key, ghost_key, mod_index) \
+ case xkb_key: \
+ if (!MOD_TEST(state, seat->xkb_keymap_mod_index.mod_index)) { \
+ gkey = ghost_key; \
+ } \
+ break
+
+ switch (sym) {
+ MOD_TEST_CASE(XKB_KEY_Shift_L, GHOST_kKeyLeftShift, shift);
+ MOD_TEST_CASE(XKB_KEY_Shift_R, GHOST_kKeyRightShift, shift);
+ MOD_TEST_CASE(XKB_KEY_Control_L, GHOST_kKeyLeftControl, ctrl);
+ MOD_TEST_CASE(XKB_KEY_Control_R, GHOST_kKeyRightControl, ctrl);
+ MOD_TEST_CASE(XKB_KEY_Alt_L, GHOST_kKeyLeftAlt, alt);
+ MOD_TEST_CASE(XKB_KEY_Alt_R, GHOST_kKeyRightAlt, alt);
+ MOD_TEST_CASE(XKB_KEY_Super_L, GHOST_kKeyOS, logo);
+ MOD_TEST_CASE(XKB_KEY_Super_R, GHOST_kKeyOS, logo);
+ }
+
+#undef MOD_TEST
+#undef MOD_TEST_CASE
+
+ if (gkey != GHOST_kKeyUnknown) {
+ GHOST_IWindow *win = ghost_wl_surface_user_data(wl_surface);
+ GHOST_SystemWayland *system = seat->system;
+ seat->system->pushEvent(
+ new GHOST_EventKey(system->getMilliSeconds(), GHOST_kEventKeyDown, win, gkey, false));
+ }
+ }
}
}
/**
* Leave event.
*
- * Notification that this seat's keyboard focus is no longer on a
- * certain surface.
+ * Notification that this seat's keyboard focus is no longer on a certain wl_surface.
*/
static void keyboard_handle_leave(void *data,
struct wl_keyboard * /*wl_keyboard*/,
- uint32_t /*serial*/,
- struct wl_surface *surface)
+ const uint32_t /*serial*/,
+ struct wl_surface *wl_surface)
{
- if (surface != nullptr) {
- static_cast<input_t *>(data)->focus_keyboard = nullptr;
+ if (!(wl_surface && ghost_wl_surface_own(wl_surface))) {
+ CLOG_INFO(LOG, 2, "leave (skipped)");
+ return;
+ }
+ CLOG_INFO(LOG, 2, "leave");
+
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+ seat->keyboard.wl_surface = nullptr;
+
+ /* Losing focus must stop repeating text. */
+ if (seat->key_repeat.timer) {
+ keyboard_handle_key_repeat_cancel(seat);
}
}
@@ -1654,36 +2231,75 @@ static void keyboard_handle_leave(void *data,
* 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)
+static xkb_keysym_t xkb_state_key_get_one_sym_without_modifiers(
+ struct xkb_state *xkb_state_empty,
+ struct xkb_state *xkb_state_empty_with_numlock,
+ const 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);
+ xkb_keysym_t sym = xkb_state_key_get_one_sym(xkb_state_empty, key);
+
+ /* NOTE(@campbellbarton): Only perform the number-locked lookup as a fallback
+ * when a number-pad key has been pressed. This is important as some key-maps use number lock
+ * for switching other layers (in particular `de(neo_qwertz)` turns on layer-4), see: T96170.
+ * Alternative solutions could be to inspect the layout however this could get involved
+ * and turning on the number-lock is only needed for a limited set of keys. */
+
+ /* Accounts for key-pad keys typically swapped for numbers when number-lock is enabled:
+ * `Home Left Up Right Down Prior Page_Up Next Page_Dow End Begin Insert Delete`. */
+ if (xkb_state_empty_with_numlock && (sym >= XKB_KEY_KP_Home && sym <= XKB_KEY_KP_Delete)) {
+ const xkb_keysym_t sym_test = xkb_state_key_get_one_sym(xkb_state_empty_with_numlock, key);
+ if (sym_test != XKB_KEY_NoSymbol) {
+ sym = sym_test;
}
}
- 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_handle_key_repeat_cancel(GWL_Seat *seat)
+{
+ GHOST_ASSERT(seat->key_repeat.timer != nullptr, "Caller much check for timer");
+ delete static_cast<GWL_KeyRepeatPlayload *>(seat->key_repeat.timer->getUserData());
+ seat->system->removeTimer(seat->key_repeat.timer);
+ seat->key_repeat.timer = nullptr;
+}
+
+/**
+ * Restart the key-repeat timer.
+ * \param use_delay: When false, use the interval
+ * (prevents pause when the setting changes while the key is held).
+ */
+static void keyboard_handle_key_repeat_reset(GWL_Seat *seat, const bool use_delay)
+{
+ GHOST_ASSERT(seat->key_repeat.timer != nullptr, "Caller much check for timer");
+ GHOST_SystemWayland *system = seat->system;
+ GHOST_ITimerTask *timer = seat->key_repeat.timer;
+ GHOST_TimerProcPtr key_repeat_fn = timer->getTimerProc();
+ GHOST_TUserDataPtr payload = seat->key_repeat.timer->getUserData();
+ seat->system->removeTimer(seat->key_repeat.timer);
+ const uint64_t time_step = 1000 / seat->key_repeat.rate;
+ const uint64_t time_start = use_delay ? seat->key_repeat.delay : time_step;
+ seat->key_repeat.timer = system->installTimer(time_start, time_step, key_repeat_fn, payload);
+}
+
static void keyboard_handle_key(void *data,
struct wl_keyboard * /*wl_keyboard*/,
- uint32_t serial,
- uint32_t /*time*/,
- uint32_t key,
- uint32_t state)
+ const uint32_t serial,
+ const uint32_t /*time*/,
+ const uint32_t key,
+ const uint32_t state)
{
- input_t *input = static_cast<input_t *>(data);
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+ const xkb_keycode_t key_code = key + EVDEV_OFFSET;
+
+ const xkb_keysym_t sym = xkb_state_key_get_one_sym_without_modifiers(
+ seat->xkb_state_empty, seat->xkb_state_empty_with_numlock, key_code);
+ if (sym == XKB_KEY_NoSymbol) {
+ CLOG_INFO(LOG, 2, "key (no symbol, skipped)");
+ return;
+ }
+ CLOG_INFO(LOG, 2, "key");
GHOST_TEventType etype = GHOST_kEventUnknown;
switch (state) {
@@ -1695,92 +2311,153 @@ static void keyboard_handle_key(void *data,
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;
- }
+ struct GWL_KeyRepeatPlayload *key_repeat_payload = nullptr;
/* 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;
- }
+ if (seat->key_repeat.timer) {
+ enum { NOP = 1, RESET, CANCEL } timer_action = NOP;
+ key_repeat_payload = static_cast<GWL_KeyRepeatPlayload *>(
+ seat->key_repeat.timer->getUserData());
+
+ if (seat->key_repeat.rate == 0) {
+ /* Repeat was disabled (unlikely but possible). */
+ timer_action = CANCEL;
+ }
+ else if (key_code == key_repeat_payload->key_code) {
+ /* Releasing the key that was held always cancels. */
+ timer_action = CANCEL;
+ }
+ else if (xkb_keymap_key_repeats(xkb_state_get_keymap(seat->xkb_state), key_code)) {
+ if (etype == GHOST_kEventKeyDown) {
+ /* Any other key-down always cancels (and may start it's own repeat timer). */
+ timer_action = CANCEL;
+ }
+ else {
+ /* Key-up from keys that were not repeating cause the repeat timer to pause.
+ *
+ * NOTE(@campbellbarton): This behavior isn't universal, some text input systems will
+ * stop the repeat entirely. Choose to pause repeat instead as this is what GTK/WIN32 do,
+ * and it fits better for keyboard input that isn't related to text entry. */
+ timer_action = RESET;
+ }
+ }
- GHOST_TEventKeyData key_data = {
- .key = xkb_map_gkey(sym),
- };
+ switch (timer_action) {
+ case NOP: {
+ /* Don't add a new timer, leave the existing timer owning this `key_repeat_payload`. */
+ key_repeat_payload = nullptr;
+ break;
+ }
+ case RESET: {
+ /* The payload will be added again. */
+ seat->system->removeTimer(seat->key_repeat.timer);
+ seat->key_repeat.timer = nullptr;
+ break;
+ }
+ case CANCEL: {
+ delete key_repeat_payload;
+ key_repeat_payload = nullptr;
- 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';
+ seat->system->removeTimer(seat->key_repeat.timer);
+ seat->key_repeat.timer = nullptr;
+ break;
+ }
+ }
}
- input->data_source_serial = serial;
+ const GHOST_TKey gkey = xkb_map_gkey_or_scan_code(sym, key);
+ char utf8_buf[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'};
+ if (etype == GHOST_kEventKeyDown) {
+ xkb_state_key_get_utf8(seat->xkb_state, key_code, utf8_buf, sizeof(utf8_buf));
+ }
- 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, key_data.key, '\0', key_data.utf8_buf, false));
+ seat->data_source_serial = serial;
- /* 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) {
+ if (wl_surface *wl_surface_focus = seat->keyboard.wl_surface) {
+ GHOST_IWindow *win = ghost_wl_surface_user_data(wl_surface_focus);
+ seat->system->pushEvent(
+ new GHOST_EventKey(seat->system->getMilliSeconds(), etype, win, gkey, false, utf8_buf));
+ }
- key_repeat_payload_t *payload = new key_repeat_payload_t({
- .system = input->system,
- .window = win,
- .key_data = key_data,
- });
+ /* An existing payload means the key repeat timer is reset and will be added again. */
+ if (key_repeat_payload == nullptr) {
+ /* Start timer for repeating key, if applicable. */
+ if ((seat->key_repeat.rate > 0) && (etype == GHOST_kEventKeyDown) &&
+ xkb_keymap_key_repeats(xkb_state_get_keymap(seat->xkb_state), key_code)) {
+ key_repeat_payload = new GWL_KeyRepeatPlayload({
+ .seat = seat,
+ .key_code = key_code,
+ .key_data = {.gkey = gkey},
+ });
+ }
+ }
+ if (key_repeat_payload) {
auto key_repeat_fn = [](GHOST_ITimerTask *task, uint64_t /*time*/) {
- struct key_repeat_payload_t *payload = static_cast<key_repeat_payload_t *>(
+ struct GWL_KeyRepeatPlayload *payload = static_cast<GWL_KeyRepeatPlayload *>(
task->getUserData());
- payload->system->pushEvent(new GHOST_EventKey(payload->system->getMilliSeconds(),
- GHOST_kEventKeyDown,
- payload->window,
- payload->key_data.key,
- '\0',
- payload->key_data.utf8_buf,
- true));
+
+ GWL_Seat *seat = payload->seat;
+ if (wl_surface *wl_surface_focus = seat->keyboard.wl_surface) {
+ GHOST_IWindow *win = ghost_wl_surface_user_data(wl_surface_focus);
+ GHOST_SystemWayland *system = seat->system;
+ /* Calculate this value every time in case modifier keys are pressed. */
+ char utf8_buf[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'};
+ xkb_state_key_get_utf8(seat->xkb_state, payload->key_code, utf8_buf, sizeof(utf8_buf));
+ system->pushEvent(new GHOST_EventKey(system->getMilliSeconds(),
+ GHOST_kEventKeyDown,
+ win,
+ payload->key_data.gkey,
+ true,
+ utf8_buf));
+ }
};
- input->key_repeat.timer = input->system->installTimer(
- input->key_repeat.delay, 1000 / input->key_repeat.rate, key_repeat_fn, payload);
+ seat->key_repeat.timer = seat->system->installTimer(
+ seat->key_repeat.delay, 1000 / seat->key_repeat.rate, key_repeat_fn, key_repeat_payload);
}
}
static void keyboard_handle_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)
+ const uint32_t /*serial*/,
+ const uint32_t mods_depressed,
+ const uint32_t mods_latched,
+ const uint32_t mods_locked,
+ const uint32_t group)
{
- xkb_state_update_mask(static_cast<input_t *>(data)->xkb_state,
- mods_depressed,
- mods_latched,
- mods_locked,
- 0,
- 0,
- group);
+ CLOG_INFO(LOG,
+ 2,
+ "modifiers (depressed=%u, latched=%u, locked=%u, group=%u)",
+ mods_depressed,
+ mods_latched,
+ mods_locked,
+ group);
+
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+ xkb_state_update_mask(seat->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
+
+ /* A modifier changed so reset the timer,
+ * see comment in #keyboard_handle_key regarding this behavior. */
+ if (seat->key_repeat.timer) {
+ keyboard_handle_key_repeat_reset(seat, true);
+ }
}
static void keyboard_repeat_handle_info(void *data,
struct wl_keyboard * /*wl_keyboard*/,
- int32_t rate,
- int32_t delay)
+ const int32_t rate,
+ const int32_t delay)
{
- input_t *input = static_cast<input_t *>(data);
+ CLOG_INFO(LOG, 2, "info (rate=%d, delay=%d)", rate, delay);
- input->key_repeat.rate = rate;
- input->key_repeat.delay = delay;
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+ seat->key_repeat.rate = rate;
+ seat->key_repeat.delay = delay;
+
+ /* Unlikely possible this setting changes while repeating. */
+ if (seat->key_repeat.timer) {
+ keyboard_handle_key_repeat_reset(seat, false);
+ }
}
static const struct wl_keyboard_listener keyboard_listener = {
@@ -1792,41 +2469,57 @@ static const struct wl_keyboard_listener keyboard_listener = {
keyboard_repeat_handle_info,
};
+#undef LOG
+
/** \} */
/* -------------------------------------------------------------------- */
/** \name Listener (Seat), #wl_seat_listener
* \{ */
-static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities)
+static CLG_LogRef LOG_WL_SEAT = {"ghost.wl.handle.seat"};
+#define LOG (&LOG_WL_SEAT)
+
+static void seat_handle_capabilities(void *data,
+ struct wl_seat *wl_seat,
+ const uint32_t capabilities)
{
- input_t *input = static_cast<input_t *>(data);
- input->wl_pointer = nullptr;
- input->wl_keyboard = nullptr;
+ CLOG_INFO(LOG,
+ 2,
+ "capabilities (pointer=%d, keyboard=%d, touch=%d)",
+ (capabilities & WL_SEAT_CAPABILITY_POINTER) != 0,
+ (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) != 0,
+ (capabilities & WL_SEAT_CAPABILITY_TOUCH) != 0);
+
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+ seat->wl_pointer = nullptr;
+ seat->wl_keyboard = nullptr;
if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
- input->wl_pointer = wl_seat_get_pointer(wl_seat);
- input->cursor.wl_surface = wl_compositor_create_surface(input->system->compositor());
- input->cursor.visible = true;
- input->cursor.wl_buffer = nullptr;
- input->cursor.file_buffer = new buffer_t;
- if (!get_cursor_settings(input->cursor.theme_name, input->cursor.size)) {
- input->cursor.theme_name = std::string();
- input->cursor.size = default_cursor_size;
+ seat->wl_pointer = wl_seat_get_pointer(wl_seat);
+ seat->cursor.wl_surface = wl_compositor_create_surface(seat->system->compositor());
+ seat->cursor.visible = true;
+ seat->cursor.wl_buffer = nullptr;
+ if (!get_cursor_settings(seat->cursor.theme_name, seat->cursor.size)) {
+ seat->cursor.theme_name = std::string();
+ seat->cursor.size = default_cursor_size;
}
- wl_pointer_add_listener(input->wl_pointer, &pointer_listener, data);
- wl_surface_add_listener(input->cursor.wl_surface, &cursor_surface_listener, data);
+ wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, data);
+
+ wl_surface_add_listener(seat->cursor.wl_surface, &cursor_surface_listener, data);
+ ghost_wl_surface_tag_cursor_pointer(seat->cursor.wl_surface);
}
if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
- input->wl_keyboard = wl_seat_get_keyboard(wl_seat);
- wl_keyboard_add_listener(input->wl_keyboard, &keyboard_listener, data);
+ seat->wl_keyboard = wl_seat_get_keyboard(wl_seat);
+ wl_keyboard_add_listener(seat->wl_keyboard, &keyboard_listener, data);
}
}
static void seat_handle_name(void *data, struct wl_seat * /*wl_seat*/, const char *name)
{
- static_cast<input_t *>(data)->name = std::string(name);
+ CLOG_INFO(LOG, 2, "name (name=\"%s\")", name);
+ static_cast<GWL_Seat *>(data)->name = std::string(name);
}
static const struct wl_seat_listener seat_listener = {
@@ -1834,18 +2527,25 @@ static const struct wl_seat_listener seat_listener = {
seat_handle_name,
};
+#undef LOG
+
/** \} */
/* -------------------------------------------------------------------- */
/** \name Listener (XDG Output), #zxdg_output_v1_listener
* \{ */
+static CLG_LogRef LOG_WL_XDG_OUTPUT = {"ghost.wl.handle.xdg_output"};
+#define LOG (&LOG_WL_XDG_OUTPUT)
+
static void xdg_output_handle_logical_position(void *data,
struct zxdg_output_v1 * /*xdg_output*/,
- int32_t x,
- int32_t y)
+ const int32_t x,
+ const int32_t y)
{
- output_t *output = static_cast<output_t *>(data);
+ CLOG_INFO(LOG, 2, "logical_position [%d, %d]", x, y);
+
+ GWL_Output *output = static_cast<GWL_Output *>(data);
output->position_logical[0] = x;
output->position_logical[1] = y;
output->has_position_logical = true;
@@ -1853,11 +2553,12 @@ static void xdg_output_handle_logical_position(void *data,
static void xdg_output_handle_logical_size(void *data,
struct zxdg_output_v1 * /*xdg_output*/,
- int32_t width,
- int32_t height)
+ const int32_t width,
+ const int32_t height)
{
- output_t *output = static_cast<output_t *>(data);
+ CLOG_INFO(LOG, 2, "logical_size [%d, %d]", width, height);
+ GWL_Output *output = static_cast<GWL_Output *>(data);
if (output->size_logical[0] != 0 && output->size_logical[1] != 0) {
/* Original comment from SDL. */
/* FIXME(@flibit): GNOME has a bug where the logical size does not account for
@@ -1867,7 +2568,14 @@ static void xdg_output_handle_logical_size(void *data,
* done (we can't match exactly because fractional scaling can't be
* detected otherwise), then override if necessary. */
if ((output->size_logical[0] == width) && (output->scale_fractional == wl_fixed_from_int(1))) {
- GHOST_PRINT("xdg_output scale did not match, overriding with wl_output scale");
+ GHOST_PRINT("xdg_output scale did not match, overriding with wl_output scale\n");
+
+#ifdef USE_GNOME_CONFINE_HACK
+ /* Use a bug in GNOME to check GNOME is in use. If the bug is fixed this won't cause an issue
+ * as T98793 has been fixed up-stream too, but not in a release at time of writing. */
+ use_gnome_confine_hack = true;
+#endif
+
return;
}
}
@@ -1877,24 +2585,29 @@ static void xdg_output_handle_logical_size(void *data,
output->has_size_logical = true;
}
-static void xdg_output_handle_done(void * /*data*/, struct zxdg_output_v1 * /*xdg_output*/)
+static void xdg_output_handle_done(void *data, struct zxdg_output_v1 * /*xdg_output*/)
{
+ CLOG_INFO(LOG, 2, "done");
/* NOTE: `xdg-output.done` events are deprecated and only apply below version 3 of the protocol.
* `wl-output.done` event will be emitted in version 3 or higher. */
+ GWL_Output *output = static_cast<GWL_Output *>(data);
+ if (zxdg_output_v1_get_version(output->xdg_output) < 3) {
+ output_handle_done(data, output->wl_output);
+ }
}
static void xdg_output_handle_name(void * /*data*/,
struct zxdg_output_v1 * /*xdg_output*/,
- const char * /*name*/)
+ const char *name)
{
- /* Pass. */
+ CLOG_INFO(LOG, 2, "name (name=\"%s\")", name);
}
static void xdg_output_handle_description(void * /*data*/,
struct zxdg_output_v1 * /*xdg_output*/,
- const char * /*description*/)
+ const char *description)
{
- /* Pass. */
+ CLOG_INFO(LOG, 2, "description (description=\"%s\")", description);
}
static const struct zxdg_output_v1_listener xdg_output_listener = {
@@ -1905,24 +2618,38 @@ static const struct zxdg_output_v1_listener xdg_output_listener = {
xdg_output_handle_description,
};
+#undef LOG
+
/** \} */
/* -------------------------------------------------------------------- */
/** \name Listener (Output), #wl_output_listener
* \{ */
+static CLG_LogRef LOG_WL_OUTPUT = {"ghost.wl.handle.output"};
+#define LOG (&LOG_WL_OUTPUT)
+
static void output_handle_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 int32_t /*x*/,
+ const int32_t /*y*/,
+ const int32_t physical_width,
+ const int32_t physical_height,
+ const int32_t /*subpixel*/,
const char *make,
const char *model,
- int32_t transform)
+ const int32_t transform)
{
- output_t *output = static_cast<output_t *>(data);
+ CLOG_INFO(LOG,
+ 2,
+ "geometry (make=\"%s\", model=\"%s\", transform=%d, size=[%d, %d])",
+ make,
+ model,
+ transform,
+ physical_width,
+ physical_height);
+
+ GWL_Output *output = static_cast<GWL_Output *>(data);
output->transform = transform;
output->make = std::string(make);
output->model = std::string(model);
@@ -1932,23 +2659,26 @@ static void output_handle_geometry(void *data,
static void output_handle_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);
-
- if (flags & WL_OUTPUT_MODE_CURRENT) {
- output->size_native[0] = width;
- output->size_native[1] = height;
-
- /* Don't rotate this yet, `wl-output` coordinates are transformed in
- * handle_done and `xdg-output` coordinates are pre-transformed. */
- if (!output->has_size_logical) {
- output->size_logical[0] = width;
- output->size_logical[1] = height;
- }
+ const uint32_t flags,
+ const int32_t width,
+ const int32_t height,
+ const int32_t /*refresh*/)
+{
+ if ((flags & WL_OUTPUT_MODE_CURRENT) == 0) {
+ CLOG_INFO(LOG, 2, "mode (skipped)");
+ return;
+ }
+ CLOG_INFO(LOG, 2, "mode (size=[%d, %d], flags=%u)", width, height, flags);
+
+ GWL_Output *output = static_cast<GWL_Output *>(data);
+ output->size_native[0] = width;
+ output->size_native[1] = height;
+
+ /* Don't rotate this yet, `wl-output` coordinates are transformed in
+ * handle_done and `xdg-output` coordinates are pre-transformed. */
+ if (!output->has_size_logical) {
+ output->size_logical[0] = width;
+ output->size_logical[1] = height;
}
}
@@ -1962,11 +2692,13 @@ static void output_handle_mode(void *data,
*/
static void output_handle_done(void *data, struct wl_output * /*wl_output*/)
{
- output_t *output = static_cast<output_t *>(data);
+ CLOG_INFO(LOG, 2, "done");
+
+ GWL_Output *output = static_cast<GWL_Output *>(data);
int32_t size_native[2];
if (output->transform & WL_OUTPUT_TRANSFORM_90) {
size_native[0] = output->size_native[1];
- size_native[1] = output->size_native[1];
+ size_native[1] = output->size_native[0];
}
else {
size_native[0] = output->size_native[0];
@@ -1987,9 +2719,17 @@ static void output_handle_done(void *data, struct wl_output * /*wl_output*/)
}
}
-static void output_handle_scale(void *data, struct wl_output * /*wl_output*/, int32_t factor)
+static void output_handle_scale(void *data, struct wl_output * /*wl_output*/, const int32_t factor)
{
- static_cast<output_t *>(data)->scale = factor;
+ CLOG_INFO(LOG, 2, "scale");
+ static_cast<GWL_Output *>(data)->scale = factor;
+
+ if (window_manager) {
+ for (GHOST_IWindow *iwin : window_manager->getWindows()) {
+ GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(iwin);
+ win->outputs_changed_update_scale();
+ }
+ }
}
static const struct wl_output_listener output_listener = {
@@ -1999,14 +2739,24 @@ static const struct wl_output_listener output_listener = {
output_handle_scale,
};
+#undef LOG
+
/** \} */
/* -------------------------------------------------------------------- */
/** \name Listener (XDG WM Base), #xdg_wm_base_listener
* \{ */
-static void shell_handle_ping(void * /*data*/, struct xdg_wm_base *xdg_wm_base, uint32_t serial)
+#ifndef WITH_GHOST_WAYLAND_LIBDECOR
+
+static CLG_LogRef LOG_WL_XDG_WM_BASE = {"ghost.wl.handle.xdg_wm_base"};
+# define LOG (&LOG_WL_XDG_WM_BASE)
+
+static void shell_handle_ping(void * /*data*/,
+ struct xdg_wm_base *xdg_wm_base,
+ const uint32_t serial)
{
+ CLOG_INFO(LOG, 2, "ping");
xdg_wm_base_pong(xdg_wm_base, serial);
}
@@ -2014,23 +2764,67 @@ static const struct xdg_wm_base_listener shell_listener = {
shell_handle_ping,
};
+# undef LOG
+
+#endif /* !WITH_GHOST_WAYLAND_LIBDECOR. */
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (LibDecor), #libdecor_interface
+ * \{ */
+
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+
+static CLG_LogRef LOG_WL_LIBDECOR = {"ghost.wl.handle.libdecor"};
+# define LOG (&LOG_WL_LIBDECOR)
+
+static void decor_handle_error(struct libdecor * /*context*/,
+ enum libdecor_error error,
+ const char *message)
+{
+ CLOG_INFO(LOG, 2, "error (id=%d, message=%s)", error, message);
+
+ (void)(error);
+ (void)(message);
+ GHOST_PRINT("decoration error (" << error << "): " << message << std::endl);
+ exit(EXIT_FAILURE);
+}
+
+static struct libdecor_interface libdecor_interface = {
+ decor_handle_error,
+};
+
+# undef LOG
+
+#endif /* WITH_GHOST_WAYLAND_LIBDECOR. */
+
/** \} */
/* -------------------------------------------------------------------- */
/** \name Listener (Registry), #wl_registry_listener
* \{ */
+static CLG_LogRef LOG_WL_REGISTRY = {"ghost.wl.handle.registry"};
+#define LOG (&LOG_WL_REGISTRY)
+
static void global_handle_add(void *data,
struct wl_registry *wl_registry,
- uint32_t name,
+ const uint32_t name,
const char *interface,
- uint32_t /*version*/)
+ const uint32_t version)
{
- struct display_t *display = static_cast<struct display_t *>(data);
+ /* Log last since it can be noted if the interface was handled or not. */
+ bool found = true;
+
+ struct GWL_Display *display = static_cast<struct GWL_Display *>(data);
if (!strcmp(interface, wl_compositor_interface.name)) {
display->compositor = static_cast<wl_compositor *>(
wl_registry_bind(wl_registry, name, &wl_compositor_interface, 3));
}
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+ /* Pass. */
+#else
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));
@@ -2040,19 +2834,23 @@ static void global_handle_add(void *data,
display->xdg_decoration_manager = static_cast<zxdg_decoration_manager_v1 *>(
wl_registry_bind(wl_registry, name, &zxdg_decoration_manager_v1_interface, 1));
}
+#endif /* !WITH_GHOST_WAYLAND_LIBDECOR. */
else if (!strcmp(interface, zxdg_output_manager_v1_interface.name)) {
display->xdg_output_manager = static_cast<zxdg_output_manager_v1 *>(
- wl_registry_bind(wl_registry, name, &zxdg_output_manager_v1_interface, 3));
- for (output_t *output : display->outputs) {
+ wl_registry_bind(wl_registry, name, &zxdg_output_manager_v1_interface, 2));
+ for (GWL_Output *output : display->outputs) {
output->xdg_output = zxdg_output_manager_v1_get_xdg_output(display->xdg_output_manager,
output->wl_output);
zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output);
}
}
else if (!strcmp(interface, wl_output_interface.name)) {
- output_t *output = new output_t;
+ GWL_Output *output = new GWL_Output;
output->wl_output = static_cast<wl_output *>(
wl_registry_bind(wl_registry, name, &wl_output_interface, 2));
+ ghost_wl_output_tag(output->wl_output);
+ wl_output_set_user_data(output->wl_output, output);
+
display->outputs.push_back(output);
wl_output_add_listener(output->wl_output, &output_listener, output);
@@ -2063,14 +2861,14 @@ static void global_handle_add(void *data,
}
}
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->data_source = new data_source_t;
- input->wl_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->wl_seat, &seat_listener, input);
+ GWL_Seat *seat = new GWL_Seat;
+ seat->system = display->system;
+ seat->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+ seat->data_source = new GWL_DataSource;
+ seat->wl_seat = static_cast<wl_seat *>(
+ wl_registry_bind(wl_registry, name, &wl_seat_interface, 5));
+ display->seats.push_back(seat);
+ wl_seat_add_listener(seat->wl_seat, &seat_listener, seat);
}
else if (!strcmp(interface, wl_shm_interface.name)) {
display->shm = static_cast<wl_shm *>(
@@ -2092,6 +2890,17 @@ static void global_handle_add(void *data,
display->pointer_constraints = static_cast<zwp_pointer_constraints_v1 *>(
wl_registry_bind(wl_registry, name, &zwp_pointer_constraints_v1_interface, 1));
}
+ else {
+ found = false;
+ }
+
+ CLOG_INFO(LOG,
+ 2,
+ "add %s(interface=%s, version=%u, name=%u)",
+ found ? "" : "(skipped), ",
+ interface,
+ version,
+ name);
}
/**
@@ -2105,8 +2914,9 @@ static void global_handle_add(void *data,
*/
static void global_handle_remove(void * /*data*/,
struct wl_registry * /*wl_registry*/,
- uint32_t /*name*/)
+ const uint32_t name)
{
+ CLOG_INFO(LOG, 2, "remove (name=%u)", name);
}
static const struct wl_registry_listener registry_listener = {
@@ -2114,15 +2924,17 @@ static const struct wl_registry_listener registry_listener = {
global_handle_remove,
};
+#undef LOG
+
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Ghost Implementation
+/** \name GHOST Implementation
*
- * Wayland specific implementation of the GHOST_System interface.
+ * WAYLAND specific implementation of the #GHOST_System interface.
* \{ */
-GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), d(new display_t)
+GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), d(new GWL_Display)
{
wl_log_set_handler_client(ghost_wayland_log_handler);
@@ -2143,25 +2955,32 @@ GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), d(new display_t)
wl_display_roundtrip(d->display);
wl_registry_destroy(registry);
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+ d->decor_context = libdecor_new(d->display, &libdecor_interface);
+ if (!d->decor_context) {
+ display_destroy(d);
+ throw std::runtime_error("Wayland: unable to create window decorations!");
+ }
+#else
if (!d->xdg_shell) {
display_destroy(d);
throw std::runtime_error("Wayland: unable to access xdg_shell!");
}
+#endif
/* 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->wl_seat);
- wl_data_device_add_listener(input->data_device, &data_device_listener, input);
+ for (GWL_Seat *seat : d->seats) {
+ seat->data_device = wl_data_device_manager_get_data_device(d->data_device_manager,
+ seat->wl_seat);
+ wl_data_device_add_listener(seat->data_device, &data_device_listener, seat);
}
}
if (d->tablet_manager) {
- for (input_t *input : d->inputs) {
- input->tablet_seat = zwp_tablet_manager_v2_get_tablet_seat(d->tablet_manager,
- input->wl_seat);
- zwp_tablet_seat_v2_add_listener(input->tablet_seat, &tablet_seat_listener, input);
+ for (GWL_Seat *seat : d->seats) {
+ seat->tablet_seat = zwp_tablet_manager_v2_get_tablet_seat(d->tablet_manager, seat->wl_seat);
+ zwp_tablet_seat_v2_add_listener(seat->tablet_seat, &tablet_seat_listener, seat);
}
}
}
@@ -2171,9 +2990,36 @@ GHOST_SystemWayland::~GHOST_SystemWayland()
display_destroy(d);
}
+GHOST_TSuccess GHOST_SystemWayland::init()
+{
+ GHOST_TSuccess success = GHOST_System::init();
+
+ if (success) {
+#ifdef WITH_INPUT_NDOF
+ m_ndofManager = new GHOST_NDOFManagerUnix(*this);
+#endif
+ return GHOST_kSuccess;
+ }
+
+ return GHOST_kFailure;
+}
+
bool GHOST_SystemWayland::processEvents(bool waitForEvent)
{
- const bool fired = getTimerManager()->fireTimers(getMilliSeconds());
+ bool any_processed = false;
+
+ if (getTimerManager()->fireTimers(getMilliSeconds())) {
+ any_processed = true;
+ }
+
+#ifdef WITH_INPUT_NDOF
+ if (static_cast<GHOST_NDOFManagerUnix *>(m_ndofManager)->processEvents()) {
+ /* As NDOF bypasses WAYLAND event handling,
+ * never wait for an event when an NDOF event was found. */
+ waitForEvent = false;
+ any_processed = true;
+ }
+#endif /* WITH_INPUT_NDOF */
if (waitForEvent) {
wl_display_dispatch(d->display);
@@ -2182,56 +3028,69 @@ bool GHOST_SystemWayland::processEvents(bool waitForEvent)
wl_display_roundtrip(d->display);
}
- return fired || (getEventManager()->getNumEvents() > 0);
+ if ((getEventManager()->getNumEvents() > 0)) {
+ any_processed = true;
+ }
+
+ return any_processed;
}
-int GHOST_SystemWayland::setConsoleWindowState(GHOST_TConsoleWindowState /*action*/)
+bool GHOST_SystemWayland::setConsoleWindowState(GHOST_TConsoleWindowState /*action*/)
{
- return 0;
+ return false;
}
GHOST_TSuccess GHOST_SystemWayland::getModifierKeys(GHOST_ModifierKeys &keys) const
{
- if (d->inputs.empty()) {
+ if (UNLIKELY(d->seats.empty())) {
return GHOST_kFailure;
}
- 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);
+ GWL_Seat *seat = d->seats[0];
bool val;
- /* NOTE: XKB doesn't seem to differentiate between left/right modifiers. */
+ /* NOTE: XKB doesn't differentiate between left/right modifiers
+ * for it's internal modifier state storage. */
+ const xkb_mod_mask_t state = xkb_state_serialize_mods(seat->xkb_state, XKB_STATE_MODS_ALL);
+
+#define MOD_TEST(state, mod) ((mod != XKB_MOD_INVALID) && (state & (1 << mod)))
- val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_SHIFT, mods_all) == 1;
+ val = MOD_TEST(state, seat->xkb_keymap_mod_index.shift);
keys.set(GHOST_kModifierKeyLeftShift, val);
keys.set(GHOST_kModifierKeyRightShift, val);
- val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_ALT, mods_all) == 1;
+ val = MOD_TEST(state, seat->xkb_keymap_mod_index.alt);
keys.set(GHOST_kModifierKeyLeftAlt, val);
keys.set(GHOST_kModifierKeyRightAlt, val);
- val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_CTRL, mods_all) == 1;
+ val = MOD_TEST(state, seat->xkb_keymap_mod_index.ctrl);
keys.set(GHOST_kModifierKeyLeftControl, val);
keys.set(GHOST_kModifierKeyRightControl, val);
- val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_LOGO, mods_all) == 1;
+ val = MOD_TEST(state, seat->xkb_keymap_mod_index.logo);
keys.set(GHOST_kModifierKeyOS, val);
- val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_NUM, mods_all) == 1;
- keys.set(GHOST_kModifierKeyNumMasks, val);
+ val = MOD_TEST(state, seat->xkb_keymap_mod_index.num);
+ keys.set(GHOST_kModifierKeyNum, val);
+
+#undef MOD_TEST
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_SystemWayland::getButtons(GHOST_Buttons &buttons) const
{
- if (d->inputs.empty()) {
+ if (UNLIKELY(d->seats.empty())) {
+ return GHOST_kFailure;
+ }
+ GWL_Seat *seat = d->seats[0];
+ GWL_SeatStatePointer *seat_state_pointer = seat_state_pointer_active(seat);
+ if (!seat_state_pointer) {
return GHOST_kFailure;
}
- buttons = d->inputs[0]->buttons;
+ buttons = seat_state_pointer->buttons;
return GHOST_kSuccess;
}
@@ -2244,15 +3103,15 @@ char *GHOST_SystemWayland::getClipboard(bool /*selection*/) const
void GHOST_SystemWayland::putClipboard(const char *buffer, bool /*selection*/) const
{
- if (!d->data_device_manager || d->inputs.empty()) {
+ if (UNLIKELY(!d->data_device_manager || d->seats.empty())) {
return;
}
- input_t *input = d->inputs[0];
+ GWL_Seat *seat = d->seats[0];
- std::lock_guard lock{input->data_source_mutex};
+ std::lock_guard lock{seat->data_source_mutex};
- data_source_t *data_source = input->data_source;
+ GWL_DataSource *data_source = seat->data_source;
/* Copy buffer. */
free(data_source->buffer_out);
@@ -2262,15 +3121,15 @@ void GHOST_SystemWayland::putClipboard(const char *buffer, bool /*selection*/) c
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, input);
+ wl_data_source_add_listener(data_source->data_source, &data_source_listener, seat);
for (const std::string &type : mime_send) {
wl_data_source_offer(data_source->data_source, type.c_str());
}
- if (input->data_device) {
+ if (seat->data_device) {
wl_data_device_set_selection(
- input->data_device, data_source->data_source, input->data_source_serial);
+ seat->data_device, data_source->data_source, seat->data_source_serial);
}
}
@@ -2279,70 +3138,145 @@ uint8_t GHOST_SystemWayland::getNumDisplays() const
return d ? uint8_t(d->outputs.size()) : 0;
}
-GHOST_TSuccess GHOST_SystemWayland::getCursorPosition(int32_t &x, int32_t &y) const
+static GHOST_TSuccess getCursorPositionClientRelative_impl(
+ const GWL_SeatStatePointer *seat_state_pointer,
+ const GHOST_WindowWayland *win,
+ int32_t &x,
+ int32_t &y)
{
- if (d->inputs.empty()) {
+ const wl_fixed_t scale = win->scale();
+ x = wl_fixed_to_int(scale * seat_state_pointer->xy[0]);
+ y = wl_fixed_to_int(scale * seat_state_pointer->xy[1]);
+ return GHOST_kSuccess;
+}
+
+static GHOST_TSuccess setCursorPositionClientRelative_impl(GWL_Seat *seat,
+ GHOST_WindowWayland *win,
+ const int32_t x,
+ const int32_t y)
+{
+ /* NOTE: WAYLAND doesn't support warping the cursor.
+ * However when grab is enabled, we already simulate a cursor location
+ * so that can be set to a new location. */
+ if (!seat->relative_pointer) {
return GHOST_kFailure;
}
+ const wl_fixed_t scale = win->scale();
+ const wl_fixed_t xy_next[2] = {
+ wl_fixed_from_int(x) / scale,
+ wl_fixed_from_int(y) / scale,
+ };
- input_t *input = d->inputs[0];
- struct wl_surface *surface = nullptr;
- if (input->pointer_serial == input->cursor_serial) {
- surface = input->focus_pointer;
+ /* As the cursor was "warped" generate an event at the new location. */
+ relative_pointer_handle_relative_motion_impl(seat, win, xy_next);
+
+ return GHOST_kSuccess;
+}
+
+GHOST_TSuccess GHOST_SystemWayland::getCursorPositionClientRelative(const GHOST_IWindow *window,
+ int32_t &x,
+ int32_t &y) const
+{
+ if (UNLIKELY(d->seats.empty())) {
+ return GHOST_kFailure;
}
- else if (input->tablet_serial == input->cursor_serial) {
- surface = input->focus_tablet;
+ GWL_Seat *seat = d->seats[0];
+ GWL_SeatStatePointer *seat_state_pointer = seat_state_pointer_active(seat);
+ if (!seat_state_pointer || !seat_state_pointer->wl_surface) {
+ return GHOST_kFailure;
}
- if (!surface) {
+ const GHOST_WindowWayland *win = static_cast<const GHOST_WindowWayland *>(window);
+ return getCursorPositionClientRelative_impl(seat_state_pointer, win, x, y);
+}
+
+GHOST_TSuccess GHOST_SystemWayland::setCursorPositionClientRelative(GHOST_IWindow *window,
+ const int32_t x,
+ const int32_t y)
+{
+ if (UNLIKELY(d->seats.empty())) {
return GHOST_kFailure;
}
+ GWL_Seat *seat = d->seats[0];
+ GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(window);
+ return setCursorPositionClientRelative_impl(seat, win, x, y);
+}
- GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(wl_surface_get_user_data(surface));
- if (!win) {
+GHOST_TSuccess GHOST_SystemWayland::getCursorPosition(int32_t &x, int32_t &y) const
+{
+ if (UNLIKELY(d->seats.empty())) {
+ return GHOST_kFailure;
+ }
+ GWL_Seat *seat = d->seats[0];
+ GWL_SeatStatePointer *seat_state_pointer = seat_state_pointer_active(seat);
+ if (!seat_state_pointer) {
return GHOST_kFailure;
}
- const wl_fixed_t scale = win->scale();
- x = wl_fixed_to_int(scale * input->xy[0]);
- y = wl_fixed_to_int(scale * input->xy[1]);
- return GHOST_kSuccess;
+ if (wl_surface *wl_surface_focus = seat_state_pointer->wl_surface) {
+ GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
+ return getCursorPositionClientRelative_impl(seat_state_pointer, win, x, y);
+ }
+ return GHOST_kFailure;
}
-GHOST_TSuccess GHOST_SystemWayland::setCursorPosition(int32_t /*x*/, int32_t /*y*/)
+GHOST_TSuccess GHOST_SystemWayland::setCursorPosition(const int32_t x, const int32_t y)
{
+ if (UNLIKELY(d->seats.empty())) {
+ return GHOST_kFailure;
+ }
+ GWL_Seat *seat = d->seats[0];
+
+ /* Intentionally different from `getCursorPosition` which supports both tablet & pointer.
+ * In the case of setting the cursor location, tablets don't support this. */
+ if (wl_surface *wl_surface_focus = seat->pointer.wl_surface) {
+ GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
+ return setCursorPositionClientRelative_impl(seat, win, x, y);
+ }
return GHOST_kFailure;
}
void GHOST_SystemWayland::getMainDisplayDimensions(uint32_t &width, uint32_t &height) const
{
- if (getNumDisplays() > 0) {
- /* We assume first output as main. */
- width = uint32_t(d->outputs[0]->size_native[0]) / d->outputs[0]->scale;
- height = uint32_t(d->outputs[0]->size_native[1]) / d->outputs[0]->scale;
+ if (getNumDisplays() == 0) {
+ return;
}
+ /* We assume first output as main. */
+ width = uint32_t(d->outputs[0]->size_native[0]);
+ height = uint32_t(d->outputs[0]->size_native[1]);
}
void GHOST_SystemWayland::getAllDisplayDimensions(uint32_t &width, uint32_t &height) const
{
- getMainDisplayDimensions(width, height);
+ int32_t xy_min[2] = {INT32_MAX, INT32_MAX};
+ int32_t xy_max[2] = {INT32_MIN, INT32_MIN};
+
+ for (const GWL_Output *output : d->outputs) {
+ int32_t xy[2] = {0, 0};
+ if (output->has_position_logical) {
+ xy[0] = output->position_logical[0];
+ xy[1] = output->position_logical[1];
+ }
+ xy_min[0] = std::min(xy_min[0], xy[0]);
+ xy_min[1] = std::min(xy_min[1], xy[1]);
+ xy_max[0] = std::max(xy_max[0], xy[0] + output->size_native[0]);
+ xy_max[1] = std::max(xy_max[1], xy[1] + output->size_native[1]);
+ }
+
+ width = xy_max[0] - xy_min[0];
+ height = xy_max[1] - xy_min[1];
}
-GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings /*glSettings*/)
+static GHOST_Context *createOffscreenContext_impl(GHOST_SystemWayland *system,
+ struct wl_display *wl_display,
+ wl_egl_window *egl_window)
{
- /* 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;
for (int minor = 6; minor >= 0; --minor) {
- context = new GHOST_ContextEGL(this,
+ context = new GHOST_ContextEGL(system,
false,
- EGLNativeWindowType(os_egl_window),
- EGLNativeDisplayType(d->display),
+ EGLNativeWindowType(egl_window),
+ EGLNativeDisplayType(wl_display),
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
4,
minor,
@@ -2356,10 +3290,10 @@ GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings /*g
delete context;
}
- context = new GHOST_ContextEGL(this,
+ context = new GHOST_ContextEGL(system,
false,
- EGLNativeWindowType(os_egl_window),
- EGLNativeDisplayType(d->display),
+ EGLNativeWindowType(egl_window),
+ EGLNativeDisplayType(wl_display),
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
3,
3,
@@ -2371,31 +3305,59 @@ GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings /*g
return context;
}
delete context;
+ return nullptr;
+}
- GHOST_PRINT("Cannot create off-screen EGL context" << std::endl);
+GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings /*glSettings*/)
+{
+ /* Create new off-screen window. */
+ wl_surface *wl_surface = wl_compositor_create_surface(compositor());
+ wl_egl_window *egl_window = wl_surface ? wl_egl_window_create(wl_surface, 1, 1) : nullptr;
- return nullptr;
+ GHOST_Context *context = createOffscreenContext_impl(this, d->display, egl_window);
+
+ if (!context) {
+ GHOST_PRINT("Cannot create off-screen EGL context" << std::endl);
+ if (wl_surface) {
+ wl_surface_destroy(wl_surface);
+ }
+ if (egl_window) {
+ wl_egl_window_destroy(egl_window);
+ }
+ return nullptr;
+ }
+
+ wl_surface_set_user_data(wl_surface, egl_window);
+ context->setUserData(wl_surface);
+
+ return context;
}
GHOST_TSuccess GHOST_SystemWayland::disposeContext(GHOST_IContext *context)
{
+ struct wl_surface *wl_surface = (struct wl_surface *)((GHOST_Context *)context)->getUserData();
+ wl_egl_window *egl_window = (wl_egl_window *)wl_surface_get_user_data(wl_surface);
+ wl_egl_window_destroy(egl_window);
+ wl_surface_destroy(wl_surface);
+
delete context;
+
return GHOST_kSuccess;
}
GHOST_IWindow *GHOST_SystemWayland::createWindow(const char *title,
- int32_t left,
- int32_t top,
- uint32_t width,
- uint32_t height,
- GHOST_TWindowState state,
- GHOST_TDrawingContextType type,
- GHOST_GLSettings glSettings,
+ const int32_t left,
+ const int32_t top,
+ const uint32_t width,
+ const uint32_t height,
+ const GHOST_TWindowState state,
+ const GHOST_TDrawingContextType type,
+ const GHOST_GLSettings glSettings,
const bool exclusive,
const bool is_dialog,
const GHOST_IWindow *parentWindow)
{
- /* globally store pointer to window manager */
+ /* Globally store pointer to window manager. */
if (!window_manager) {
window_manager = getWindowManager();
}
@@ -2429,106 +3391,209 @@ GHOST_IWindow *GHOST_SystemWayland::createWindow(const char *title,
return window;
}
-wl_display *GHOST_SystemWayland::display()
+/**
+ * Show the buffer defined by #cursor_buffer_set without changing anything else,
+ * so #cursor_buffer_hide can be used to display it again.
+ *
+ * The caller is responsible for setting `seat->cursor.visible`.
+ */
+static void cursor_buffer_show(const GWL_Seat *seat)
{
- return d->display;
-}
+ const GWL_Cursor *c = &seat->cursor;
+
+ if (seat->wl_pointer) {
+ const int scale = c->is_custom ? c->custom_scale : seat->pointer.theme_scale;
+ const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / scale;
+ const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / scale;
+ if (seat->wl_pointer) {
+ wl_pointer_set_cursor(
+ seat->wl_pointer, seat->pointer.serial, c->wl_surface, hotspot_x, hotspot_y);
+ }
+ }
-wl_compositor *GHOST_SystemWayland::compositor()
-{
- return d->compositor;
+ if (!seat->tablet_tools.empty()) {
+ const int scale = c->is_custom ? c->custom_scale : seat->tablet.theme_scale;
+ const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / scale;
+ const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / scale;
+ for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : seat->tablet_tools) {
+ GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(
+ zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2));
+ zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2,
+ seat->tablet.serial,
+ tablet_tool->wl_surface_cursor,
+ hotspot_x,
+ hotspot_y);
+ }
+ }
}
-xdg_wm_base *GHOST_SystemWayland::xdg_shell()
+/**
+ * Hide the buffer defined by #cursor_buffer_set without changing anything else,
+ * so #cursor_buffer_show can be used to display it again.
+ *
+ * The caller is responsible for setting `seat->cursor.visible`.
+ */
+static void cursor_buffer_hide(const GWL_Seat *seat)
{
- return d->xdg_shell;
+ wl_pointer_set_cursor(seat->wl_pointer, seat->pointer.serial, nullptr, 0, 0);
+ for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : seat->tablet_tools) {
+ zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2, seat->tablet.serial, nullptr, 0, 0);
+ }
}
-zxdg_decoration_manager_v1 *GHOST_SystemWayland::xdg_decoration_manager()
+/**
+ * Needed to ensure the cursor size is always a multiple of scale.
+ */
+static int cursor_buffer_compatible_scale_from_image(const struct wl_cursor_image *wl_image,
+ int scale)
{
- return d->xdg_decoration_manager;
+ const int32_t image_size_x = int32_t(wl_image->width);
+ const int32_t image_size_y = int32_t(wl_image->height);
+ while (scale > 1) {
+ if ((image_size_x % scale) == 0 && (image_size_y % scale) == 0) {
+ break;
+ }
+ scale -= 1;
+ }
+ return scale;
}
-const std::vector<output_t *> &GHOST_SystemWayland::outputs() const
+static void cursor_buffer_set_surface_impl(const GWL_Seat *seat,
+ wl_buffer *buffer,
+ struct wl_surface *wl_surface,
+ const int scale)
{
- return d->outputs;
+ const wl_cursor_image *wl_image = &seat->cursor.wl_image;
+ const int32_t image_size_x = int32_t(wl_image->width);
+ const int32_t image_size_y = int32_t(wl_image->height);
+ GHOST_ASSERT((image_size_x % scale) == 0 && (image_size_y % scale) == 0,
+ "The size must be a multiple of the scale!");
+
+ wl_surface_set_buffer_scale(wl_surface, scale);
+ wl_surface_attach(wl_surface, buffer, 0, 0);
+ wl_surface_damage(wl_surface, 0, 0, image_size_x, image_size_y);
+ wl_surface_commit(wl_surface);
}
-wl_shm *GHOST_SystemWayland::shm() const
+static void cursor_buffer_set(const GWL_Seat *seat, wl_buffer *buffer)
{
- return d->shm;
-}
+ const GWL_Cursor *c = &seat->cursor;
+ const wl_cursor_image *wl_image = &seat->cursor.wl_image;
+ const bool visible = (c->visible && c->is_hardware);
+
+ /* This is a requirement of WAYLAND, when this isn't the case,
+ * it causes Blender's window to close intermittently. */
+ if (seat->wl_pointer) {
+ const int scale = cursor_buffer_compatible_scale_from_image(
+ wl_image, c->is_custom ? c->custom_scale : seat->pointer.theme_scale);
+ const int32_t hotspot_x = int32_t(wl_image->hotspot_x) / scale;
+ const int32_t hotspot_y = int32_t(wl_image->hotspot_y) / scale;
+ cursor_buffer_set_surface_impl(seat, buffer, c->wl_surface, scale);
+ wl_pointer_set_cursor(seat->wl_pointer,
+ seat->pointer.serial,
+ visible ? c->wl_surface : nullptr,
+ hotspot_x,
+ hotspot_y);
+ }
-void GHOST_SystemWayland::setSelection(const std::string &selection)
-{
- this->selection = selection;
+ /* Set the cursor for all tablet tools as well. */
+ if (!seat->tablet_tools.empty()) {
+ const int scale = cursor_buffer_compatible_scale_from_image(
+ wl_image, c->is_custom ? c->custom_scale : seat->tablet.theme_scale);
+ const int32_t hotspot_x = int32_t(wl_image->hotspot_x) / scale;
+ const int32_t hotspot_y = int32_t(wl_image->hotspot_y) / scale;
+ for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : seat->tablet_tools) {
+ GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(
+ zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2));
+ cursor_buffer_set_surface_impl(seat, buffer, tablet_tool->wl_surface_cursor, scale);
+ zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2,
+ seat->tablet.serial,
+ visible ? tablet_tool->wl_surface_cursor : nullptr,
+ hotspot_x,
+ hotspot_y);
+ }
+ }
}
-static void set_cursor_buffer(input_t *input, wl_buffer *buffer)
-{
- cursor_t *c = &input->cursor;
-
- c->visible = (buffer != nullptr);
-
- const int32_t image_size_x = int32_t(c->wl_image.width);
- const int32_t image_size_y = int32_t(c->wl_image.height);
+enum eCursorSetMode {
+ CURSOR_VISIBLE_ALWAYS_SET = 1,
+ CURSOR_VISIBLE_ONLY_HIDE,
+ CURSOR_VISIBLE_ONLY_SHOW,
+};
- const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / c->scale;
- const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / c->scale;
+static void cursor_visible_set(GWL_Seat *seat,
+ const bool visible,
+ const bool is_hardware,
+ const enum eCursorSetMode set_mode)
+{
+ GWL_Cursor *cursor = &seat->cursor;
+ const bool was_visible = cursor->is_hardware && cursor->visible;
+ const bool use_visible = is_hardware && visible;
- if (buffer) {
- wl_surface_set_buffer_scale(c->wl_surface, c->scale);
- wl_surface_attach(c->wl_surface, buffer, 0, 0);
- wl_surface_damage(c->wl_surface, 0, 0, image_size_x, image_size_y);
- wl_surface_commit(c->wl_surface);
+ if (set_mode == CURSOR_VISIBLE_ALWAYS_SET) {
+ /* Pass. */
+ }
+ else if ((set_mode == CURSOR_VISIBLE_ONLY_SHOW)) {
+ if (!use_visible) {
+ return;
+ }
+ }
+ else if ((set_mode == CURSOR_VISIBLE_ONLY_HIDE)) {
+ if (use_visible) {
+ return;
+ }
}
- wl_pointer_set_cursor(input->wl_pointer,
- input->pointer_serial,
- c->visible ? c->wl_surface : nullptr,
- hotspot_x,
- hotspot_y);
-
- /* Set the cursor for all tablet tools as well. */
- for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : input->tablet_tools) {
- tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>(
- zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2));
-
- if (buffer) {
- /* FIXME: for some reason cursor scale is applied twice (when the scale isn't 1x),
- * this happens both in gnome-shell & KDE. Setting the surface scale here doesn't help. */
- wl_surface_set_buffer_scale(tool_input->cursor_surface, c->scale);
- wl_surface_attach(tool_input->cursor_surface, buffer, 0, 0);
- wl_surface_damage(tool_input->cursor_surface, 0, 0, image_size_x, image_size_y);
- wl_surface_commit(tool_input->cursor_surface);
+ if (use_visible) {
+ if (!was_visible) {
+ cursor_buffer_show(seat);
}
+ }
+ else {
+ if (was_visible) {
+ cursor_buffer_hide(seat);
+ }
+ }
+ cursor->visible = visible;
+ cursor->is_hardware = is_hardware;
+}
- zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2,
- input->tablet_serial,
- c->visible ? tool_input->cursor_surface : nullptr,
- hotspot_x,
- hotspot_y);
+static bool cursor_is_software(const GHOST_TGrabCursorMode mode, const bool use_software_confine)
+{
+ if (mode == GHOST_kGrabWrap) {
+ return true;
+ }
+#ifdef USE_GNOME_CONFINE_HACK
+ if (mode == GHOST_kGrabNormal) {
+ if (use_software_confine) {
+ return true;
+ }
}
+#else
+ (void)use_software_confine;
+#endif
+ return false;
}
-GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape)
+GHOST_TSuccess GHOST_SystemWayland::setCursorShape(const GHOST_TStandardCursor shape)
{
- if (d->inputs.empty()) {
+ if (UNLIKELY(d->seats.empty())) {
return GHOST_kFailure;
}
- const std::string cursor_name = cursors.count(shape) ? cursors.at(shape) :
- cursors.at(GHOST_kStandardCursorDefault);
+ auto cursor_find = cursors.find(shape);
+ const char *cursor_name = (cursor_find == cursors.end()) ?
+ cursors.at(GHOST_kStandardCursorDefault) :
+ (*cursor_find).second;
- input_t *input = d->inputs[0];
- cursor_t *c = &input->cursor;
+ GWL_Seat *seat = d->seats[0];
+ GWL_Cursor *c = &seat->cursor;
if (!c->wl_theme) {
- /* The cursor surface hasn't entered an output yet. Initialize theme with scale 1. */
- c->wl_theme = wl_cursor_theme_load(
- c->theme_name.c_str(), c->size, d->inputs[0]->system->shm());
+ /* The cursor wl_surface hasn't entered an output yet. Initialize theme with scale 1. */
+ c->wl_theme = wl_cursor_theme_load(c->theme_name.c_str(), c->size, d->seats[0]->system->shm());
}
- wl_cursor *cursor = wl_cursor_theme_get_cursor(c->wl_theme, cursor_name.c_str());
+ wl_cursor *cursor = wl_cursor_theme_get_cursor(c->wl_theme, cursor_name);
if (!cursor) {
GHOST_PRINT("cursor '" << cursor_name << "' does not exist" << std::endl);
@@ -2541,81 +3606,56 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape)
return GHOST_kFailure;
}
+ c->visible = true;
+ c->is_custom = false;
c->wl_buffer = buffer;
c->wl_image = *image;
- set_cursor_buffer(input, buffer);
+ cursor_buffer_set(seat, buffer);
return GHOST_kSuccess;
}
-GHOST_TSuccess GHOST_SystemWayland::hasCursorShape(GHOST_TStandardCursor cursorShape)
+GHOST_TSuccess GHOST_SystemWayland::hasCursorShape(const GHOST_TStandardCursor cursorShape)
{
- return GHOST_TSuccess(cursors.count(cursorShape) && !cursors.at(cursorShape).empty());
+ auto cursor_find = cursors.find(cursorShape);
+ if (cursor_find == cursors.end()) {
+ return GHOST_kFailure;
+ }
+ const char *value = (*cursor_find).second;
+ if (*value == '\0') {
+ return GHOST_kFailure;
+ }
+ return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap,
uint8_t *mask,
- int sizex,
- int sizey,
- int hotX,
- int hotY,
- bool /*canInvertColor*/)
+ const int sizex,
+ const int sizey,
+ const int hotX,
+ const int hotY,
+ const 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;
-
-#ifdef HAVE_MEMFD_CREATE
- const int fd = memfd_create("blender-cursor-custom", MFD_CLOEXEC | MFD_ALLOW_SEALING);
- if (fd >= 0) {
- fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
- }
-#else
- char *path = getenv("XDG_RUNTIME_DIR");
- if (!path) {
- errno = ENOENT;
+ if (UNLIKELY(d->seats.empty())) {
return GHOST_kFailure;
}
- char *tmpname;
- asprintf(&tmpname, "%s/%s", path, "blender-XXXXXX");
- const int fd = mkostemp(tmpname, O_CLOEXEC);
- if (fd >= 0) {
- unlink(tmpname);
- }
- free(tmpname);
-#endif
-
- if (fd < 0) {
- return GHOST_kFailure;
- }
+ GWL_Cursor *cursor = &d->seats[0]->cursor;
- if (posix_fallocate(fd, 0, int32_t(cursor->file_buffer->size)) != 0) {
- return GHOST_kFailure;
+ if (cursor->custom_data) {
+ munmap(cursor->custom_data, cursor->custom_data_size);
+ cursor->custom_data = nullptr;
+ cursor->custom_data_size = 0; /* Not needed, but the value is no longer meaningful. */
}
- cursor->file_buffer->data = mmap(
- nullptr, cursor->file_buffer->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
-
- if (cursor->file_buffer->data == MAP_FAILED) {
- close(fd);
+ const int32_t size_xy[2] = {sizex, sizey};
+ wl_buffer *buffer = ghost_wl_buffer_create_for_image(
+ d->shm, size_xy, WL_SHM_FORMAT_ARGB8888, &cursor->custom_data, &cursor->custom_data_size);
+ if (buffer == nullptr) {
return GHOST_kFailure;
}
- 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;
@@ -2626,7 +3666,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap,
uint32_t *pixel;
for (int y = 0; y < sizey; ++y) {
- pixel = &static_cast<uint32_t *>(cursor->file_buffer->data)[y * sizex];
+ pixel = &static_cast<uint32_t *>(cursor->custom_data)[y * sizex];
for (int x = 0; x < sizex; ++x) {
if ((x % 8) == 0) {
datab = *bitmap++;
@@ -2648,37 +3688,49 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap,
}
}
+ cursor->visible = true;
+ cursor->is_custom = true;
+ cursor->custom_scale = 1; /* TODO: support Hi-DPI custom cursors. */
cursor->wl_buffer = buffer;
cursor->wl_image.width = uint32_t(sizex);
cursor->wl_image.height = uint32_t(sizey);
cursor->wl_image.hotspot_x = uint32_t(hotX);
cursor->wl_image.hotspot_y = uint32_t(hotY);
- set_cursor_buffer(d->inputs[0], buffer);
+ cursor_buffer_set(d->seats[0], buffer);
return GHOST_kSuccess;
}
-GHOST_TSuccess GHOST_SystemWayland::setCursorVisibility(bool visible)
+GHOST_TSuccess GHOST_SystemWayland::getCursorBitmap(GHOST_CursorBitmapRef *bitmap)
{
- if (d->inputs.empty()) {
+ GWL_Cursor *cursor = &d->seats[0]->cursor;
+ if (cursor->custom_data == nullptr) {
+ return GHOST_kFailure;
+ }
+ if (!cursor->is_custom) {
return GHOST_kFailure;
}
- input_t *input = d->inputs[0];
+ bitmap->data_size[0] = cursor->wl_image.width;
+ bitmap->data_size[1] = cursor->wl_image.height;
- cursor_t *cursor = &input->cursor;
- if (visible) {
- if (!cursor->visible) {
- set_cursor_buffer(input, cursor->wl_buffer);
- }
- }
- else {
- if (cursor->visible) {
- set_cursor_buffer(input, nullptr);
- }
+ bitmap->hot_spot[0] = cursor->wl_image.hotspot_x;
+ bitmap->hot_spot[1] = cursor->wl_image.hotspot_y;
+
+ bitmap->data = (uint8_t *)static_cast<void *>(cursor->custom_data);
+
+ return GHOST_kSuccess;
+}
+
+GHOST_TSuccess GHOST_SystemWayland::setCursorVisibility(const bool visible)
+{
+ if (UNLIKELY(d->seats.empty())) {
+ return GHOST_kFailure;
}
+ GWL_Seat *seat = d->seats[0];
+ cursor_visible_set(seat, visible, seat->cursor.is_hardware, CURSOR_VISIBLE_ALWAYS_SET);
return GHOST_kSuccess;
}
@@ -2695,148 +3747,426 @@ bool GHOST_SystemWayland::supportsWindowPosition()
return false;
}
-GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mode,
- const GHOST_TGrabCursorMode mode_current,
- wl_surface *surface)
+bool GHOST_SystemWayland::getCursorGrabUseSoftwareDisplay(const GHOST_TGrabCursorMode mode)
{
- /* ignore, if the required protocols are not supported */
- if (!d->relative_pointer_manager || !d->pointer_constraints) {
- return GHOST_kFailure;
+ if (UNLIKELY(d->seats.empty())) {
+ return false;
+ }
+
+#ifdef USE_GNOME_CONFINE_HACK
+ GWL_Seat *seat = d->seats[0];
+ const bool use_software_confine = seat->use_pointer_software_confine;
+#else
+ const bool use_software_confine = false;
+#endif
+
+ return cursor_is_software(mode, use_software_confine);
+}
+
+#ifdef USE_GNOME_CONFINE_HACK
+static bool setCursorGrab_use_software_confine(const GHOST_TGrabCursorMode mode,
+ wl_surface *wl_surface)
+{
+# ifndef USE_GNOME_CONFINE_HACK_ALWAYS_ON
+ if (use_gnome_confine_hack == false) {
+ return false;
+ }
+# endif
+ if (mode != GHOST_kGrabNormal) {
+ return false;
+ }
+ const GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface);
+ if (!win) {
+ return false;
+ }
+
+# ifndef USE_GNOME_CONFINE_HACK_ALWAYS_ON
+ if (win->scale() <= 1) {
+ return false;
}
+# endif
+ return true;
+}
+#endif
+
+static GWL_SeatStateGrab seat_grab_state_from_mode(const GHOST_TGrabCursorMode mode,
+ const bool use_software_confine)
+{
+ /* Initialize all members. */
+ const struct GWL_SeatStateGrab grab_state = {
+ /* Warping happens to require software cursor which also hides. */
+ .use_lock = ELEM(mode, GHOST_kGrabWrap, GHOST_kGrabHide) || use_software_confine,
+ .use_confine = (mode == GHOST_kGrabNormal) && (use_software_confine == false),
+ };
+ return grab_state;
+}
- if (d->inputs.empty()) {
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Public WAYLAND Proxy Ownership API
+ * \{ */
+
+static const char *ghost_wl_output_tag_id = "GHOST-output";
+static const char *ghost_wl_surface_tag_id = "GHOST-window";
+static const char *ghost_wl_surface_cursor_pointer_tag_id = "GHOST-cursor-pointer";
+static const char *ghost_wl_surface_cursor_tablet_tag_id = "GHOST-cursor-tablet";
+
+bool ghost_wl_output_own(const struct wl_output *wl_output)
+{
+ return wl_proxy_get_tag((struct wl_proxy *)wl_output) == &ghost_wl_output_tag_id;
+}
+
+bool ghost_wl_surface_own(const struct wl_surface *wl_surface)
+{
+ return wl_proxy_get_tag((struct wl_proxy *)wl_surface) == &ghost_wl_surface_tag_id;
+}
+
+bool ghost_wl_surface_own_cursor_pointer(const struct wl_surface *wl_surface)
+{
+ return wl_proxy_get_tag((struct wl_proxy *)wl_surface) ==
+ &ghost_wl_surface_cursor_pointer_tag_id;
+}
+
+bool ghost_wl_surface_own_cursor_tablet(const struct wl_surface *wl_surface)
+{
+ return wl_proxy_get_tag((struct wl_proxy *)wl_surface) == &ghost_wl_surface_cursor_tablet_tag_id;
+}
+
+void ghost_wl_output_tag(struct wl_output *wl_output)
+{
+ wl_proxy_set_tag((struct wl_proxy *)wl_output, &ghost_wl_output_tag_id);
+}
+
+void ghost_wl_surface_tag(struct wl_surface *wl_surface)
+{
+ wl_proxy_set_tag((struct wl_proxy *)wl_surface, &ghost_wl_surface_tag_id);
+}
+
+void ghost_wl_surface_tag_cursor_pointer(struct wl_surface *wl_surface)
+{
+ wl_proxy_set_tag((struct wl_proxy *)wl_surface, &ghost_wl_surface_cursor_pointer_tag_id);
+}
+
+void ghost_wl_surface_tag_cursor_tablet(struct wl_surface *wl_surface)
+{
+ wl_proxy_set_tag((struct wl_proxy *)wl_surface, &ghost_wl_surface_cursor_tablet_tag_id);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Public WAYLAND Direct Data Access
+ *
+ * Expose some members via methods.
+ * \{ */
+
+wl_display *GHOST_SystemWayland::display()
+{
+ return d->display;
+}
+
+wl_compositor *GHOST_SystemWayland::compositor()
+{
+ return d->compositor;
+}
+
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+
+libdecor *GHOST_SystemWayland::decor_context()
+{
+ return d->decor_context;
+}
+
+#else /* WITH_GHOST_WAYLAND_LIBDECOR */
+
+xdg_wm_base *GHOST_SystemWayland::xdg_shell()
+{
+ return d->xdg_shell;
+}
+
+zxdg_decoration_manager_v1 *GHOST_SystemWayland::xdg_decoration_manager()
+{
+ return d->xdg_decoration_manager;
+}
+
+#endif /* !WITH_GHOST_WAYLAND_LIBDECOR */
+
+const std::vector<GWL_Output *> &GHOST_SystemWayland::outputs() const
+{
+ return d->outputs;
+}
+
+wl_shm *GHOST_SystemWayland::shm() const
+{
+ return d->shm;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Public WAYLAND Query Access
+ * \{ */
+
+struct GWL_Output *ghost_wl_output_user_data(struct wl_output *wl_output)
+{
+ GHOST_ASSERT(wl_output, "output must not be NULL");
+ GHOST_ASSERT(ghost_wl_output_own(wl_output), "output is not owned by GHOST");
+ GWL_Output *output = static_cast<GWL_Output *>(wl_output_get_user_data(wl_output));
+ return output;
+}
+
+GHOST_WindowWayland *ghost_wl_surface_user_data(struct wl_surface *wl_surface)
+{
+ GHOST_ASSERT(wl_surface, "wl_surface must not be NULL");
+ GHOST_ASSERT(ghost_wl_surface_own(wl_surface), "wl_surface is not owned by GHOST");
+ GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(
+ wl_surface_get_user_data(wl_surface));
+ return win;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Public WAYLAND Utility Functions
+ *
+ * Functionality only used for the WAYLAND implementation.
+ * \{ */
+
+void GHOST_SystemWayland::selection_set(const std::string &selection)
+{
+ this->selection = selection;
+}
+
+void GHOST_SystemWayland::window_surface_unref(const wl_surface *wl_surface)
+{
+#define SURFACE_CLEAR_PTR(surface_test) \
+ if (surface_test == wl_surface) { \
+ surface_test = nullptr; \
+ } \
+ ((void)0);
+
+ /* Only clear window surfaces (not cursors, off-screen surfaces etc). */
+ for (GWL_Seat *seat : d->seats) {
+ SURFACE_CLEAR_PTR(seat->pointer.wl_surface);
+ SURFACE_CLEAR_PTR(seat->tablet.wl_surface);
+ SURFACE_CLEAR_PTR(seat->keyboard.wl_surface);
+ SURFACE_CLEAR_PTR(seat->wl_surface_focus_dnd);
+ }
+#undef SURFACE_CLEAR_PTR
+}
+
+bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mode,
+ const GHOST_TGrabCursorMode mode_current,
+ int32_t init_grab_xy[2],
+ const GHOST_Rect *wrap_bounds,
+ const GHOST_TAxisFlag wrap_axis,
+ wl_surface *wl_surface,
+ const int scale)
+{
+ /* Ignore, if the required protocols are not supported. */
+ if (UNLIKELY(!d->relative_pointer_manager || !d->pointer_constraints)) {
return GHOST_kFailure;
}
+ if (UNLIKELY(d->seats.empty())) {
+ return GHOST_kFailure;
+ }
/* No change, success. */
if (mode == mode_current) {
return GHOST_kSuccess;
}
- input_t *input = d->inputs[0];
+ GWL_Seat *seat = d->seats[0];
-#define MODE_NEEDS_LOCK(m) ((m) == GHOST_kGrabWrap || (m) == GHOST_kGrabHide)
-#define MODE_NEEDS_HIDE(m) ((m) == GHOST_kGrabHide)
-#define MODE_NEEDS_CONFINE(m) ((m) == GHOST_kGrabNormal)
+#ifdef USE_GNOME_CONFINE_HACK
+ const bool was_software_confine = seat->use_pointer_software_confine;
+ const bool use_software_confine = setCursorGrab_use_software_confine(mode, wl_surface);
+#else
+ const bool was_software_confine = false;
+ const bool use_software_confine = false;
+#endif
- const bool was_lock = MODE_NEEDS_LOCK(mode_current);
- const bool use_lock = MODE_NEEDS_LOCK(mode);
+ const struct GWL_SeatStateGrab grab_state_prev = seat_grab_state_from_mode(mode_current,
+ was_software_confine);
+ const struct GWL_SeatStateGrab grab_state_next = seat_grab_state_from_mode(mode,
+ use_software_confine);
/* Check for wrap as #supportsCursorWarp isn't supported. */
- const bool was_hide = MODE_NEEDS_HIDE(mode_current) || (mode_current == GHOST_kGrabWrap);
- const bool use_hide = MODE_NEEDS_HIDE(mode) || (mode == GHOST_kGrabWrap);
-
- const bool was_confine = MODE_NEEDS_CONFINE(mode_current);
- const bool use_confine = MODE_NEEDS_CONFINE(mode);
+ const bool use_visible = !(ELEM(mode, GHOST_kGrabHide, GHOST_kGrabWrap) || use_software_confine);
+ const bool is_hardware_cursor = !cursor_is_software(mode, use_software_confine);
-#undef MODE_NEEDS_LOCK
-#undef MODE_NEEDS_HIDE
-#undef MODE_NEEDS_CONFINE
-
- if (!use_hide) {
- setCursorVisibility(true);
- }
+ /* Only hide so the cursor is not made visible before it's location is restored.
+ * This function is called again at the end of this function which only shows. */
+ cursor_visible_set(seat, use_visible, is_hardware_cursor, CURSOR_VISIBLE_ONLY_HIDE);
/* Switching from one grab mode to another,
* in this case disable the current locks as it makes logic confusing,
* postpone changing the cursor to avoid flickering. */
- if (!use_lock) {
- if (input->relative_pointer) {
- zwp_relative_pointer_v1_destroy(input->relative_pointer);
- input->relative_pointer = nullptr;
+ if (!grab_state_next.use_lock) {
+ if (seat->relative_pointer) {
+ zwp_relative_pointer_v1_destroy(seat->relative_pointer);
+ seat->relative_pointer = nullptr;
}
- if (input->locked_pointer) {
+ if (seat->locked_pointer) {
+ /* Potentially add a motion event so the application has updated X/Y coordinates. */
+ int32_t xy_motion[2] = {0, 0};
+ bool xy_motion_create_event = false;
+
/* Request location to restore to. */
if (mode_current == GHOST_kGrabWrap) {
- /* The chance this fails is _very_ low. */
- GHOST_WindowWayland *win = window_from_surface(surface);
- if (!win) {
- GHOST_PRINT("could not find window from surface when un-grabbing!" << std::endl);
+ /* Since this call is initiated by Blender, we can be sure the window wasn't closed
+ * by logic outside this function - as the window was needed to make this call. */
+ int32_t xy_next[2] = {UNPACK2(seat->pointer.xy)};
+
+ GHOST_Rect bounds_scale;
+
+ bounds_scale.m_l = wl_fixed_from_int(wrap_bounds->m_l) / scale;
+ bounds_scale.m_t = wl_fixed_from_int(wrap_bounds->m_t) / scale;
+ bounds_scale.m_r = wl_fixed_from_int(wrap_bounds->m_r) / scale;
+ bounds_scale.m_b = wl_fixed_from_int(wrap_bounds->m_b) / scale;
+
+ bounds_scale.wrapPoint(UNPACK2(xy_next), 0, wrap_axis);
+
+ /* Push an event so the new location is registered. */
+ if ((xy_next[0] != seat->pointer.xy[0]) || (xy_next[1] != seat->pointer.xy[1])) {
+ xy_motion[0] = xy_next[0];
+ xy_motion[1] = xy_next[1];
+ xy_motion_create_event = true;
}
- else {
- GHOST_Rect bounds;
- int32_t xy_new[2] = {input->xy[0], input->xy[1]};
-
- /* Fallback to window bounds. */
- if (win->getCursorGrabBounds(bounds) == GHOST_kFailure) {
- win->getClientBounds(bounds);
- }
-
- const int scale = win->scale();
-
- bounds.m_l = wl_fixed_from_int(bounds.m_l) / scale;
- bounds.m_t = wl_fixed_from_int(bounds.m_t) / scale;
- bounds.m_r = wl_fixed_from_int(bounds.m_r) / scale;
- bounds.m_b = wl_fixed_from_int(bounds.m_b) / scale;
-
- bounds.wrapPoint(xy_new[0], xy_new[1], 0, win->getCursorGrabAxis());
-
- /* Push an event so the new location is registered. */
- if ((xy_new[0] != input->xy[0]) || (xy_new[1] != input->xy[1])) {
- input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
- GHOST_kEventCursorMove,
- win,
- wl_fixed_to_int(scale * xy_new[0]),
- wl_fixed_to_int(scale * xy_new[1]),
- GHOST_TABLET_DATA_NONE));
- }
- input->xy[0] = xy_new[0];
- input->xy[1] = xy_new[1];
-
- zwp_locked_pointer_v1_set_cursor_position_hint(
- input->locked_pointer, xy_new[0], xy_new[1]);
- wl_surface_commit(surface);
+ seat->pointer.xy[0] = xy_next[0];
+ seat->pointer.xy[1] = xy_next[1];
+
+ zwp_locked_pointer_v1_set_cursor_position_hint(seat->locked_pointer, UNPACK2(xy_next));
+ wl_surface_commit(wl_surface);
+ }
+ else if (mode_current == GHOST_kGrabHide) {
+ if ((init_grab_xy[0] != seat->grab_lock_xy[0]) ||
+ (init_grab_xy[1] != seat->grab_lock_xy[1])) {
+ const wl_fixed_t xy_next[2] = {
+ wl_fixed_from_int(init_grab_xy[0]) / scale,
+ wl_fixed_from_int(init_grab_xy[1]) / scale,
+ };
+ zwp_locked_pointer_v1_set_cursor_position_hint(seat->locked_pointer, UNPACK2(xy_next));
+ wl_surface_commit(wl_surface);
+
+ /* NOTE(@campbellbarton): The new cursor position is a hint,
+ * it's possible the hint is ignored. It doesn't seem like there is a good way to
+ * know if the hint will be used or not, at least not immediately. */
+ xy_motion[0] = xy_next[0];
+ xy_motion[1] = xy_next[1];
+ xy_motion_create_event = true;
}
}
+#ifdef USE_GNOME_CONFINE_HACK
+ else if (mode_current == GHOST_kGrabNormal) {
+ if (was_software_confine) {
+ zwp_locked_pointer_v1_set_cursor_position_hint(seat->locked_pointer,
+ UNPACK2(seat->pointer.xy));
+ wl_surface_commit(wl_surface);
+ }
+ }
+#endif
- zwp_locked_pointer_v1_destroy(input->locked_pointer);
- input->locked_pointer = nullptr;
+ if (xy_motion_create_event) {
+ seat->system->pushEvent(new GHOST_EventCursor(seat->system->getMilliSeconds(),
+ GHOST_kEventCursorMove,
+ ghost_wl_surface_user_data(wl_surface),
+ wl_fixed_to_int(scale * xy_motion[0]),
+ wl_fixed_to_int(scale * xy_motion[1]),
+ GHOST_TABLET_DATA_NONE));
+ }
+
+ zwp_locked_pointer_v1_destroy(seat->locked_pointer);
+ seat->locked_pointer = nullptr;
}
}
- if (!use_confine) {
- if (input->confined_pointer) {
- zwp_confined_pointer_v1_destroy(input->confined_pointer);
- input->confined_pointer = nullptr;
+ if (!grab_state_next.use_confine) {
+ if (seat->confined_pointer) {
+ zwp_confined_pointer_v1_destroy(seat->confined_pointer);
+ seat->confined_pointer = nullptr;
}
}
if (mode != GHOST_kGrabDisable) {
- if (use_lock) {
- if (!was_lock) {
+ if (grab_state_next.use_lock) {
+ if (!grab_state_prev.use_lock) {
/* TODO(@campbellbarton): As WAYLAND does not support warping the pointer it may not be
* possible to support #GHOST_kGrabWrap by pragmatically settings it's coordinates.
* An alternative could be to draw the cursor in software (and hide the real cursor),
* or just accept a locked cursor on WAYLAND. */
- input->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(
- d->relative_pointer_manager, input->wl_pointer);
+ seat->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(
+ d->relative_pointer_manager, seat->wl_pointer);
zwp_relative_pointer_v1_add_listener(
- input->relative_pointer, &relative_pointer_listener, input);
- input->locked_pointer = zwp_pointer_constraints_v1_lock_pointer(
+ seat->relative_pointer, &relative_pointer_listener, seat);
+ seat->locked_pointer = zwp_pointer_constraints_v1_lock_pointer(
d->pointer_constraints,
- surface,
- input->wl_pointer,
+ wl_surface,
+ seat->wl_pointer,
nullptr,
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
}
+ if (mode == GHOST_kGrabHide) {
+ /* Set the initial position to detect any changes when un-grabbing,
+ * otherwise the unlocked cursor defaults to un-locking in-place. */
+ init_grab_xy[0] = wl_fixed_to_int(scale * seat->pointer.xy[0]);
+ init_grab_xy[1] = wl_fixed_to_int(scale * seat->pointer.xy[1]);
+ seat->grab_lock_xy[0] = init_grab_xy[0];
+ seat->grab_lock_xy[1] = init_grab_xy[1];
+ }
}
- else if (use_confine) {
- if (!was_confine) {
- input->confined_pointer = zwp_pointer_constraints_v1_confine_pointer(
+ else if (grab_state_next.use_confine) {
+ if (!grab_state_prev.use_confine) {
+ seat->confined_pointer = zwp_pointer_constraints_v1_confine_pointer(
d->pointer_constraints,
- surface,
- input->wl_pointer,
+ wl_surface,
+ seat->wl_pointer,
nullptr,
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
}
}
-
- if (use_hide && !was_hide) {
- setCursorVisibility(false);
- }
}
+ /* Only show so the cursor is made visible as the last step. */
+ cursor_visible_set(seat, use_visible, is_hardware_cursor, CURSOR_VISIBLE_ONLY_SHOW);
+
+#ifdef USE_GNOME_CONFINE_HACK
+ seat->use_pointer_software_confine = use_software_confine;
+#endif
+
return GHOST_kSuccess;
}
+#ifdef WITH_GHOST_WAYLAND_DYNLOAD
+bool ghost_wl_dynload_libraries()
+{
+ /* Only report when `libwayland-client` is not found when building without X11,
+ * which will be used as a fallback. */
+# ifdef WITH_GHOST_X11
+ bool verbose = false;
+# else
+ bool verbose = true;
+# endif
+
+ if (wayland_dynload_client_init(verbose) && /* `libwayland-client`. */
+ wayland_dynload_cursor_init(verbose) && /* `libwayland-cursor`. */
+ wayland_dynload_egl_init(verbose) && /* `libwayland-egl`. */
+# ifdef WITH_GHOST_WAYLAND_LIBDECOR
+ wayland_dynload_libdecor_init(verbose) && /* `libdecor-0`. */
+# endif
+ true) {
+ return true;
+ }
+# ifdef WITH_GHOST_WAYLAND_LIBDECOR
+ wayland_dynload_libdecor_exit();
+# endif
+ wayland_dynload_client_exit();
+ wayland_dynload_cursor_exit();
+ wayland_dynload_egl_exit();
+
+ return false;
+}
+#endif /* WITH_GHOST_WAYLAND_DYNLOAD */
+
/** \} */
diff --git a/intern/ghost/intern/GHOST_SystemWayland.h b/intern/ghost/intern/GHOST_SystemWayland.h
index 762ceb80e38..caea7b0d748 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.h
+++ b/intern/ghost/intern/GHOST_SystemWayland.h
@@ -11,17 +11,51 @@
#include "GHOST_System.h"
#include "GHOST_WindowWayland.h"
+#ifdef WITH_GHOST_WAYLAND_DYNLOAD
+# include <wayland_dynload_client.h>
+#endif
#include <wayland-client.h>
-#include <xdg-decoration-client-protocol.h>
-#include <xdg-shell-client-protocol.h>
+
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+# ifdef WITH_GHOST_WAYLAND_DYNLOAD
+# include <wayland_dynload_libdecor.h>
+# endif
+# include <libdecor.h>
+#else
+/* Generated by `wayland-scanner`. */
+# include <xdg-decoration-unstable-v1-client-protocol.h>
+# include <xdg-shell-client-protocol.h>
+#endif
#include <string>
class GHOST_WindowWayland;
-struct display_t;
+struct GWL_Display;
+
+bool ghost_wl_output_own(const struct wl_output *wl_output);
+void ghost_wl_output_tag(struct wl_output *wl_output);
+struct GWL_Output *ghost_wl_output_user_data(struct wl_output *wl_output);
+
+bool ghost_wl_surface_own(const struct wl_surface *surface);
+void ghost_wl_surface_tag(struct wl_surface *surface);
+GHOST_WindowWayland *ghost_wl_surface_user_data(struct wl_surface *surface);
+
+bool ghost_wl_surface_own_cursor_pointer(const struct wl_surface *surface);
+void ghost_wl_surface_tag_cursor_pointer(struct wl_surface *surface);
+
+bool ghost_wl_surface_own_cursor_tablet(const struct wl_surface *surface);
+void ghost_wl_surface_tag_cursor_tablet(struct wl_surface *surface);
-struct output_t {
+#ifdef WITH_GHOST_WAYLAND_DYNLOAD
+/**
+ * Return true when all required WAYLAND libraries are present,
+ * Performs dynamic loading when `WITH_GHOST_WAYLAND_DYNLOAD` is in use.
+ */
+bool ghost_wl_dynload_libraries();
+#endif
+
+struct GWL_Output {
struct wl_output *wl_output = nullptr;
struct zxdg_output_v1 *xdg_output = nullptr;
/** Dimensions in pixels. */
@@ -32,6 +66,7 @@ struct output_t {
int32_t size_logical[2] = {0, 0};
bool has_size_logical = false;
+ /** Monitor position in pixels. */
int32_t position_logical[2] = {0, 0};
bool has_position_logical = false;
@@ -58,9 +93,11 @@ class GHOST_SystemWayland : public GHOST_System {
~GHOST_SystemWayland() override;
+ GHOST_TSuccess init();
+
bool processEvents(bool waitForEvent) override;
- int setConsoleWindowState(GHOST_TConsoleWindowState action) override;
+ bool setConsoleWindowState(GHOST_TConsoleWindowState action) override;
GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const override;
@@ -72,8 +109,14 @@ class GHOST_SystemWayland : public GHOST_System {
uint8_t getNumDisplays() const override;
- GHOST_TSuccess getCursorPosition(int32_t &x, int32_t &y) const override;
+ GHOST_TSuccess getCursorPositionClientRelative(const GHOST_IWindow *window,
+ int32_t &x,
+ int32_t &y) const override;
+ GHOST_TSuccess setCursorPositionClientRelative(GHOST_IWindow *window,
+ int32_t x,
+ int32_t y) override;
+ GHOST_TSuccess getCursorPosition(int32_t &x, int32_t &y) const override;
GHOST_TSuccess setCursorPosition(int32_t x, int32_t y) override;
void getMainDisplayDimensions(uint32_t &width, uint32_t &height) const override;
@@ -96,20 +139,6 @@ class GHOST_SystemWayland : public GHOST_System {
const bool is_dialog,
const GHOST_IWindow *parentWindow) override;
- wl_display *display();
-
- wl_compositor *compositor();
-
- xdg_wm_base *xdg_shell();
-
- zxdg_decoration_manager_v1 *xdg_decoration_manager();
-
- const std::vector<output_t *> &outputs() const;
-
- wl_shm *shm() const;
-
- void setSelection(const std::string &selection);
-
GHOST_TSuccess setCursorShape(GHOST_TStandardCursor shape);
GHOST_TSuccess hasCursorShape(GHOST_TStandardCursor cursorShape);
@@ -122,16 +151,48 @@ class GHOST_SystemWayland : public GHOST_System {
int hotY,
bool canInvertColor);
+ GHOST_TSuccess getCursorBitmap(GHOST_CursorBitmapRef *bitmap);
+
GHOST_TSuccess setCursorVisibility(bool visible);
bool supportsCursorWarp();
bool supportsWindowPosition();
- GHOST_TSuccess setCursorGrab(const GHOST_TGrabCursorMode mode,
- const GHOST_TGrabCursorMode mode_current,
- wl_surface *surface);
+ bool getCursorGrabUseSoftwareDisplay(const GHOST_TGrabCursorMode mode);
+
+ /* WAYLAND direct-data access. */
+
+ wl_display *display();
+
+ wl_compositor *compositor();
+
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+ libdecor *decor_context();
+#else
+ xdg_wm_base *xdg_shell();
+ zxdg_decoration_manager_v1 *xdg_decoration_manager();
+#endif
+
+ const std::vector<GWL_Output *> &outputs() const;
+
+ wl_shm *shm() const;
+
+ /* WAYLAND utility functions. */
+
+ void selection_set(const std::string &selection);
+
+ /** Clear all references to this surface to prevent accessing NULL pointers. */
+ void window_surface_unref(const wl_surface *wl_surface);
+
+ bool window_cursor_grab_set(const GHOST_TGrabCursorMode mode,
+ const GHOST_TGrabCursorMode mode_current,
+ int32_t init_grab_xy[2],
+ const GHOST_Rect *wrap_bounds,
+ GHOST_TAxisFlag wrap_axis,
+ wl_surface *wl_surface,
+ int scale);
private:
- struct display_t *d;
+ struct GWL_Display *d;
std::string selection;
};
diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp
index 28c86db53e2..6cb36337b55 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -42,47 +42,48 @@
# include "GHOST_NDOFManagerWin32.h"
#endif
-// Key code values not found in winuser.h
+/* Key code values not found in `winuser.h`. */
#ifndef VK_MINUS
# define VK_MINUS 0xBD
-#endif // VK_MINUS
+#endif /* VK_MINUS */
#ifndef VK_SEMICOLON
# define VK_SEMICOLON 0xBA
-#endif // VK_SEMICOLON
+#endif /* VK_SEMICOLON */
#ifndef VK_PERIOD
# define VK_PERIOD 0xBE
-#endif // VK_PERIOD
+#endif /* VK_PERIOD */
#ifndef VK_COMMA
# define VK_COMMA 0xBC
-#endif // VK_COMMA
+#endif /* VK_COMMA */
#ifndef VK_BACK_QUOTE
# define VK_BACK_QUOTE 0xC0
-#endif // VK_BACK_QUOTE
+#endif /* VK_BACK_QUOTE */
#ifndef VK_SLASH
# define VK_SLASH 0xBF
-#endif // VK_SLASH
+#endif /* VK_SLASH */
#ifndef VK_BACK_SLASH
# define VK_BACK_SLASH 0xDC
-#endif // VK_BACK_SLASH
+#endif /* VK_BACK_SLASH */
#ifndef VK_EQUALS
# define VK_EQUALS 0xBB
-#endif // VK_EQUALS
+#endif /* VK_EQUALS */
#ifndef VK_OPEN_BRACKET
# define VK_OPEN_BRACKET 0xDB
-#endif // VK_OPEN_BRACKET
+#endif /* VK_OPEN_BRACKET */
#ifndef VK_CLOSE_BRACKET
# define VK_CLOSE_BRACKET 0xDD
-#endif // VK_CLOSE_BRACKET
+#endif /* VK_CLOSE_BRACKET */
#ifndef VK_GR_LESS
# define VK_GR_LESS 0xE2
-#endif // VK_GR_LESS
+#endif /* VK_GR_LESS */
-/* Workaround for some laptop touchpads, some of which seems to
+/**
+ * Workaround for some laptop touch-pads, some of which seems to
* have driver issues which makes it so window function receives
- * the message, but PeekMessage doesn't pick those messages for
+ * the message, but #PeekMessage doesn't pick those messages for
* some reason.
*
- * We send a dummy WM_USER message to force PeekMessage to receive
+ * We send a dummy WM_USER message to force #PeekMessage to receive
* something, making it so blender's window manager sees the new
* messages coming in.
*/
@@ -101,22 +102,23 @@ static void initRawInput()
RAWINPUTDEVICE devices[DEVICE_COUNT];
memset(devices, 0, DEVICE_COUNT * sizeof(RAWINPUTDEVICE));
- // Initiates WM_INPUT messages from keyboard
- // That way GHOST can retrieve true keys
+ /* Initiates WM_INPUT messages from keyboard
+ * That way GHOST can retrieve true keys. */
devices[0].usUsagePage = 0x01;
devices[0].usUsage = 0x06; /* http://msdn.microsoft.com/en-us/windows/hardware/gg487473.aspx */
#ifdef WITH_INPUT_NDOF
- // multi-axis mouse (SpaceNavigator, etc.)
+ /* multi-axis mouse (SpaceNavigator, etc.). */
devices[1].usUsagePage = 0x01;
devices[1].usUsage = 0x08;
#endif
- if (RegisterRawInputDevices(devices, DEVICE_COUNT, sizeof(RAWINPUTDEVICE)))
- ; // yay!
- else
+ if (RegisterRawInputDevices(devices, DEVICE_COUNT, sizeof(RAWINPUTDEVICE))) {
+ /* Success. */
+ }
+ else {
GHOST_PRINTF("could not register for RawInput: %d\n", (int)GetLastError());
-
+ }
#undef DEVICE_COUNT
}
@@ -129,17 +131,17 @@ GHOST_SystemWin32::GHOST_SystemWin32()
GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::GHOST_SystemWin32(): m_displayManager==0\n");
m_displayManager->initialize();
- m_consoleStatus = 1;
+ m_consoleStatus = true;
- // Tell Windows we are per monitor DPI aware. This disables the default
- // blurry scaling and enables WM_DPICHANGED to allow us to draw at proper DPI.
+ /* Tell Windows we are per monitor DPI aware. This disables the default
+ * blurry scaling and enables WM_DPICHANGED to allow us to draw at proper DPI. */
SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
- // Check if current keyboard layout uses AltGr and save keylayout ID for
- // specialized handling if keys like VK_OEM_*. I.e. french keylayout
- // generates VK_OEM_8 for their exclamation key (key left of right shift)
+ /* Check if current keyboard layout uses AltGr and save keylayout ID for
+ * specialized handling if keys like VK_OEM_*. I.e. french keylayout
+ * generates #VK_OEM_8 for their exclamation key (key left of right shift). */
this->handleKeyboardChange();
- // Require COM for GHOST_DropTargetWin32 created in GHOST_WindowWin32.
+ /* Require COM for GHOST_DropTargetWin32 created in GHOST_WindowWin32. */
OleInitialize(0);
#ifdef WITH_INPUT_NDOF
@@ -149,7 +151,7 @@ GHOST_SystemWin32::GHOST_SystemWin32()
GHOST_SystemWin32::~GHOST_SystemWin32()
{
- // Shutdown COM
+ /* Shutdown COM. */
OleUninitialize();
if (isStartedFromCommandPrompt()) {
@@ -159,7 +161,7 @@ GHOST_SystemWin32::~GHOST_SystemWin32()
uint64_t GHOST_SystemWin32::performanceCounterToMillis(__int64 perf_ticks) const
{
- // Calculate the time passed since system initialization.
+ /* Calculate the time passed since system initialization. */
__int64 delta = (perf_ticks - m_start) * 1000;
uint64_t t = (uint64_t)(delta / m_freq);
@@ -173,12 +175,12 @@ uint64_t GHOST_SystemWin32::tickCountToMillis(__int64 ticks) const
uint64_t GHOST_SystemWin32::getMilliSeconds() const
{
- // Hardware does not support high resolution timers. We will use GetTickCount instead then.
+ /* Hardware does not support high resolution timers. We will use GetTickCount instead then. */
if (!m_hasPerformanceCounter) {
return tickCountToMillis(::GetTickCount());
}
- // Retrieve current count
+ /* Retrieve current count */
__int64 count = 0;
::QueryPerformanceCounter((LARGE_INTEGER *)&count);
@@ -227,13 +229,13 @@ GHOST_IWindow *GHOST_SystemWin32::createWindow(const char *title,
state,
type,
((glSettings.flags & GHOST_glStereoVisual) != 0),
- ((glSettings.flags & GHOST_glAlphaBackground) != 0),
+ false,
(GHOST_WindowWin32 *)parentWindow,
((glSettings.flags & GHOST_glDebugContext) != 0),
is_dialog);
if (window->getValid()) {
- // Store the pointer to the window
+ /* Store the pointer to the window */
m_windowManager->addWindow(window);
m_windowManager->setActiveWindow(window);
}
@@ -272,7 +274,7 @@ GHOST_IContext *GHOST_SystemWin32::createOffscreenContext(GHOST_GLSettings glSet
HDC mHDC = GetDC(wnd);
HDC prev_hdc = wglGetCurrentDC();
HGLRC prev_context = wglGetCurrentContext();
-#if defined(WITH_GL_PROFILE_CORE)
+
for (int minor = 5; minor >= 0; --minor) {
context = new GHOST_ContextWGL(false,
true,
@@ -310,29 +312,6 @@ GHOST_IContext *GHOST_SystemWin32::createOffscreenContext(GHOST_GLSettings glSet
return NULL;
}
-#elif defined(WITH_GL_PROFILE_COMPAT)
- // ask for 2.1 context, driver gives any GL version >= 2.1
- // (hopefully the latest compatibility profile)
- // 2.1 ignores the profile bit & is incompatible with core profile
- context = new GHOST_ContextWGL(false,
- true,
- NULL,
- NULL,
- 0, // no profile bit
- 2,
- 1,
- (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
- GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
-
- if (context->initializeDrawingContext()) {
- return context;
- }
- else {
- delete context;
- }
-#else
-# error // must specify either core or compat at build time
-#endif
finished:
wglMakeCurrent(prev_hdc, prev_context);
return context;
@@ -374,6 +353,7 @@ GHOST_ContextD3D *GHOST_SystemWin32::createOffscreenContextD3D()
context = new GHOST_ContextD3D(false, wnd);
if (context->initializeDrawingContext() == GHOST_kFailure) {
delete context;
+ context = nullptr;
}
return context;
@@ -418,10 +398,10 @@ bool GHOST_SystemWin32::processEvents(bool waitForEvent)
driveTrackpad();
- // Process all the events waiting for us
+ /* Process all the events waiting for us. */
while (::PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) != 0) {
- // TranslateMessage doesn't alter the message, and doesn't change our raw keyboard data.
- // Needed for MapVirtualKey or if we ever need to get chars from wm_ime_char or similar.
+ /* #TranslateMessage doesn't alter the message, and doesn't change our raw keyboard data.
+ * Needed for #MapVirtualKey or if we ever need to get chars from wm_ime_char or similar. */
::TranslateMessage(&msg);
::DispatchMessageW(&msg);
hasEventHandled = true;
@@ -453,8 +433,9 @@ GHOST_TSuccess GHOST_SystemWin32::getCursorPosition(int32_t &x, int32_t &y) cons
GHOST_TSuccess GHOST_SystemWin32::setCursorPosition(int32_t x, int32_t y)
{
- if (!::GetActiveWindow())
+ if (!::GetActiveWindow()) {
return GHOST_kFailure;
+ }
return ::SetCursorPos(x, y) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
}
@@ -477,10 +458,12 @@ GHOST_TSuccess GHOST_SystemWin32::getModifierKeys(GHOST_ModifierKeys &keys) cons
bool lwindown = HIBYTE(::GetKeyState(VK_LWIN)) != 0;
bool rwindown = HIBYTE(::GetKeyState(VK_RWIN)) != 0;
- if (lwindown || rwindown)
+ if (lwindown || rwindown) {
keys.set(GHOST_kModifierKeyOS, true);
- else
+ }
+ else {
keys.set(GHOST_kModifierKeyOS, false);
+ }
return GHOST_kSuccess;
}
@@ -512,7 +495,7 @@ GHOST_TSuccess GHOST_SystemWin32::init()
initRawInput();
m_lfstart = ::GetTickCount();
- // Determine whether this system has a high frequency performance counter. */
+ /* Determine whether this system has a high frequency performance counter. */
m_hasPerformanceCounter = ::QueryPerformanceFrequency((LARGE_INTEGER *)&m_freq) == TRUE;
if (m_hasPerformanceCounter) {
GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer available\n");
@@ -543,7 +526,7 @@ GHOST_TSuccess GHOST_SystemWin32::init()
wc.lpszMenuName = 0;
wc.lpszClassName = L"GHOST_WindowClass";
- // Use RegisterClassEx for setting small icon
+ /* Use #RegisterClassEx for setting small icon. */
if (::RegisterClassW(&wc) == 0) {
success = GHOST_kFailure;
}
@@ -557,76 +540,15 @@ GHOST_TSuccess GHOST_SystemWin32::exit()
return GHOST_System::exit();
}
-GHOST_TKey GHOST_SystemWin32::hardKey(RAWINPUT const &raw,
- bool *r_keyDown,
- bool *r_is_repeated_modifier)
+GHOST_TKey GHOST_SystemWin32::hardKey(RAWINPUT const &raw, bool *r_key_down)
{
- bool is_repeated_modifier = false;
-
- GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
- GHOST_TKey key = GHOST_kKeyUnknown;
- GHOST_ModifierKeys modifiers;
- system->retrieveModifierKeys(modifiers);
-
- // RI_KEY_BREAK doesn't work for sticky keys release, so we also
- // check for the up message
+ /* #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;
- *r_keyDown = !(raw.data.keyboard.Flags & RI_KEY_BREAK) && msg != WM_KEYUP && msg != WM_SYSKEYUP;
-
- key = this->convertKey(raw.data.keyboard.VKey,
- raw.data.keyboard.MakeCode,
- (raw.data.keyboard.Flags & (RI_KEY_E1 | RI_KEY_E0)));
-
- // extra handling of modifier keys: don't send repeats out from GHOST
- if (key >= GHOST_kKeyLeftShift && key <= GHOST_kKeyRightAlt) {
- bool changed = false;
- GHOST_TModifierKeyMask modifier;
- switch (key) {
- case GHOST_kKeyLeftShift: {
- changed = (modifiers.get(GHOST_kModifierKeyLeftShift) != *r_keyDown);
- modifier = GHOST_kModifierKeyLeftShift;
- break;
- }
- case GHOST_kKeyRightShift: {
- changed = (modifiers.get(GHOST_kModifierKeyRightShift) != *r_keyDown);
- modifier = GHOST_kModifierKeyRightShift;
- break;
- }
- case GHOST_kKeyLeftControl: {
- changed = (modifiers.get(GHOST_kModifierKeyLeftControl) != *r_keyDown);
- modifier = GHOST_kModifierKeyLeftControl;
- break;
- }
- case GHOST_kKeyRightControl: {
- changed = (modifiers.get(GHOST_kModifierKeyRightControl) != *r_keyDown);
- modifier = GHOST_kModifierKeyRightControl;
- break;
- }
- case GHOST_kKeyLeftAlt: {
- changed = (modifiers.get(GHOST_kModifierKeyLeftAlt) != *r_keyDown);
- modifier = GHOST_kModifierKeyLeftAlt;
- break;
- }
- case GHOST_kKeyRightAlt: {
- changed = (modifiers.get(GHOST_kModifierKeyRightAlt) != *r_keyDown);
- modifier = GHOST_kModifierKeyRightAlt;
- break;
- }
- default:
- break;
- }
-
- if (changed) {
- modifiers.set(modifier, *r_keyDown);
- system->storeModifierKeys(modifiers);
- }
- else {
- is_repeated_modifier = true;
- }
- }
+ *r_key_down = !(raw.data.keyboard.Flags & RI_KEY_BREAK) && msg != WM_KEYUP && msg != WM_SYSKEYUP;
- *r_is_repeated_modifier = is_repeated_modifier;
- return key;
+ return this->convertKey(raw.data.keyboard.VKey,
+ raw.data.keyboard.MakeCode,
+ (raw.data.keyboard.Flags & (RI_KEY_E1 | RI_KEY_E0)));
}
/**
@@ -638,6 +560,11 @@ GHOST_TKey GHOST_SystemWin32::hardKey(RAWINPUT const &raw,
GHOST_TKey GHOST_SystemWin32::processSpecialKey(short vKey, short scanCode) const
{
GHOST_TKey key = GHOST_kKeyUnknown;
+ if (vKey == 0xFF) {
+ /* 0xFF is not a valid virtual key code. */
+ return key;
+ }
+
char ch = (char)MapVirtualKeyA(vKey, MAPVK_VK_TO_CHAR);
switch (ch) {
case u'\"':
@@ -675,11 +602,11 @@ GHOST_TKey GHOST_SystemWin32::convertKey(short vKey, short scanCode, short exten
GHOST_TKey key;
if ((vKey >= '0') && (vKey <= '9')) {
- // VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39)
+ /* VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39). */
key = (GHOST_TKey)(vKey - '0' + GHOST_kKey0);
}
else if ((vKey >= 'A') && (vKey <= 'Z')) {
- // VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A)
+ /* VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A). */
key = (GHOST_TKey)(vKey - 'A' + GHOST_kKeyA);
}
else if ((vKey >= VK_F1) && (vKey <= VK_F24)) {
@@ -864,7 +791,7 @@ GHOST_TKey GHOST_SystemWin32::convertKey(short vKey, short scanCode, short exten
GHOST_EventButton *GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type,
GHOST_WindowWin32 *window,
- GHOST_TButtonMask mask)
+ GHOST_TButton mask)
{
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
@@ -974,11 +901,11 @@ void GHOST_SystemWin32::processWintabEvent(GHOST_WindowWin32 *window)
new GHOST_EventButton(info.time, info.type, window, info.button, info.tabletData));
mouseMoveHandled = true;
- break;
}
else {
WINTAB_PRINTF(" ... but no system button\n");
}
+ break;
}
case GHOST_kEventButtonUp: {
WINTAB_PRINTF("HWND %p Wintab button up", window->getHWND());
@@ -1053,7 +980,7 @@ void GHOST_SystemWin32::processPointerEvent(
}
switch (type) {
- case WM_POINTERUPDATE:
+ case WM_POINTERUPDATE: {
/* Coalesced pointer events are reverse chronological order, reorder chronologically.
* Only contiguous move events are coalesced. */
for (uint32_t i = pointerInfo.size(); i-- > 0;) {
@@ -1068,7 +995,8 @@ void GHOST_SystemWin32::processPointerEvent(
/* Leave event unhandled so that system cursor is moved. */
break;
- case WM_POINTERDOWN:
+ }
+ 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,
@@ -1087,7 +1015,8 @@ void GHOST_SystemWin32::processPointerEvent(
eventHandled = true;
break;
- case WM_POINTERUP:
+ }
+ case WM_POINTERUP: {
system->pushEvent(new GHOST_EventButton(pointerInfo[0].time,
GHOST_kEventButtonUp,
window,
@@ -1099,8 +1028,10 @@ void GHOST_SystemWin32::processPointerEvent(
eventHandled = true;
break;
- default:
+ }
+ default: {
break;
+ }
}
}
@@ -1117,6 +1048,12 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
system->getCursorPosition(x_screen, y_screen);
if (window->getCursorGrabModeIsWarp()) {
+ /* WORKAROUND:
+ * Sometimes Windows ignores `SetCursorPos()` or `SendInput()` calls or the mouse event is
+ * outdated. Identify these cases by checking if the cursor is not yet within bounds. */
+ static bool is_warping_x = false;
+ static bool is_warping_y = false;
+
int32_t x_new = x_screen;
int32_t y_new = y_screen;
int32_t x_accum, y_accum;
@@ -1133,29 +1070,41 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
window->getCursorGrabAccum(x_accum, y_accum);
if (x_new != x_screen || y_new != y_screen) {
+ system->setCursorPosition(x_new, y_new); /* wrap */
+
+ /* Do not update the accum values if we are an outdated or failed pos-warp event. */
+ if (!is_warping_x) {
+ is_warping_x = x_new != x_screen;
+ if (is_warping_x) {
+ x_accum += (x_screen - x_new);
+ }
+ }
+
+ if (!is_warping_y) {
+ is_warping_y = y_new != y_screen;
+ if (is_warping_y) {
+ y_accum += (y_screen - y_new);
+ }
+ }
+ window->setCursorGrabAccum(x_accum, y_accum);
+
/* When wrapping we don't need to add an event because the setCursorPosition call will cause
* a new event after. */
- system->setCursorPosition(x_new, y_new); /* wrap */
- window->setCursorGrabAccum(x_accum + (x_screen - x_new), y_accum + (y_screen - y_new));
- }
- else {
- return new GHOST_EventCursor(system->getMilliSeconds(),
- GHOST_kEventCursorMove,
- window,
- x_screen + x_accum,
- y_screen + y_accum,
- GHOST_TABLET_DATA_NONE);
+ return NULL;
}
+
+ is_warping_x = false;
+ is_warping_y = false;
+ x_screen += x_accum;
+ y_screen += y_accum;
}
- else {
- return new GHOST_EventCursor(system->getMilliSeconds(),
- GHOST_kEventCursorMove,
- window,
- x_screen,
- y_screen,
- GHOST_TABLET_DATA_NONE);
- }
- return NULL;
+
+ return new GHOST_EventCursor(system->getMilliSeconds(),
+ GHOST_kEventCursorMove,
+ window,
+ x_screen,
+ y_screen,
+ GHOST_TABLET_DATA_NONE);
}
void GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam)
@@ -1166,7 +1115,7 @@ void GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window, WPARAM wPar
int delta = GET_WHEEL_DELTA_WPARAM(wParam);
if (acc * delta < 0) {
- // scroll direction reversed.
+ /* Scroll direction reversed. */
acc = 0;
}
acc += delta;
@@ -1182,48 +1131,49 @@ void GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window, WPARAM wPar
GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RAWINPUT const &raw)
{
- bool keyDown = false;
- bool is_repeated_modifier = false;
+ const char vk = raw.data.keyboard.VKey;
+ bool key_down = false;
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
- GHOST_TKey key = system->hardKey(raw, &keyDown, &is_repeated_modifier);
+ GHOST_TKey key = system->hardKey(raw, &key_down);
GHOST_EventKey *event;
+ bool is_repeat = false;
+ bool is_repeated_modifier = false;
+ if (key_down) {
+ if (system->m_keycode_last_repeat_key == vk) {
+ is_repeat = true;
+ is_repeated_modifier = (key >= GHOST_kKeyLeftShift && key <= GHOST_kKeyRightAlt);
+ }
+ system->m_keycode_last_repeat_key = vk;
+ }
+ else {
+ if (system->m_keycode_last_repeat_key == vk) {
+ system->m_keycode_last_repeat_key = 0;
+ }
+ }
+
/* 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;
+ BYTE state[256];
+ const BOOL has_state = GetKeyboardState((PBYTE)state);
+ const bool ctrl_pressed = has_state && state[VK_CONTROL] & 0x80;
+ const bool alt_pressed = has_state && state[VK_MENU] & 0x80;
- /* Unlike on Linux, not all keys can send repeat events. E.g. modifier keys don't. */
- if (keyDown) {
- if (system->m_keycode_last_repeat_key == vk) {
- is_repeat = true;
- }
- system->m_keycode_last_repeat_key = vk;
+ if (!key_down) {
+ /* Pass. */
}
- else {
- if (system->m_keycode_last_repeat_key == vk) {
- system->m_keycode_last_repeat_key = 0;
- }
- }
-
- wchar_t utf16[3] = {0};
- BYTE state[256] = {0};
- int r;
- GetKeyboardState((PBYTE)state);
- bool ctrl_pressed = state[VK_CONTROL] & 0x80;
- bool alt_pressed = state[VK_MENU] & 0x80;
-
/* No text with control key pressed (Alt can be used to insert special characters though!). */
- if (ctrl_pressed && !alt_pressed) {
- utf8_char[0] = '\0';
+ else if (ctrl_pressed && !alt_pressed) {
+ /* Pass. */
}
- // Don't call ToUnicodeEx on dead keys as it clears the buffer and so won't allow diacritical
- // composition.
+ /* Don't call #ToUnicodeEx on dead keys as it clears the buffer and so won't allow diacritical
+ * composition. */
else if (MapVirtualKeyW(vk, 2) != 0) {
+ wchar_t utf16[3] = {0};
+ int r;
/* TODO: #ToUnicodeEx can respond with up to 4 utf16 chars (only 2 here).
* Could be up to 24 utf8 bytes. */
if ((r = ToUnicodeEx(
@@ -1238,29 +1188,25 @@ GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RA
}
}
- if (!keyDown) {
- utf8_char[0] = '\0';
- ascii = '\0';
- }
- else {
- ascii = utf8_char[0] & 0x80 ? '?' : utf8_char[0];
- }
-
#ifdef WITH_INPUT_IME
- if (window->getImeInput()->IsImeKeyEvent(ascii, key)) {
- return NULL;
+ if (key_down && ((utf8_char[0] & 0x80) == 0)) {
+ const char ascii = utf8_char[0];
+ if (window->getImeInput()->IsImeKeyEvent(ascii, key)) {
+ return NULL;
+ }
}
#endif /* WITH_INPUT_IME */
event = new GHOST_EventKey(system->getMilliSeconds(),
- keyDown ? GHOST_kEventKeyDown : GHOST_kEventKeyUp,
+ key_down ? GHOST_kEventKeyDown : GHOST_kEventKeyUp,
window,
key,
- ascii,
- utf8_char,
- is_repeat);
+ is_repeat,
+ utf8_char);
- // GHOST_PRINTF("%c\n", ascii); // we already get this info via EventPrinter
+#if 0 /* we already get this info via EventPrinter. */
+ GHOST_PRINTF("%c\n", ascii);
+#endif
}
else {
event = NULL;
@@ -1281,9 +1227,7 @@ GHOST_Event *GHOST_SystemWin32::processWindowSizeEvent(GHOST_WindowWin32 *window
system->dispatchEvents();
return NULL;
}
- else {
- return sizeEvent;
- }
+ return sizeEvent;
}
GHOST_Event *GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type,
@@ -1364,54 +1308,53 @@ bool GHOST_SystemWin32::processNDOF(RAWINPUT const &raw)
uint64_t now = getMilliSeconds();
static bool firstEvent = true;
- if (firstEvent) { // determine exactly which device is plugged in
+ if (firstEvent) { /* Determine exactly which device is plugged in. */
RID_DEVICE_INFO info;
unsigned infoSize = sizeof(RID_DEVICE_INFO);
info.cbSize = infoSize;
GetRawInputDeviceInfo(raw.header.hDevice, RIDI_DEVICEINFO, &info, &infoSize);
- if (info.dwType == RIM_TYPEHID)
+ if (info.dwType == RIM_TYPEHID) {
m_ndofManager->setDevice(info.hid.dwVendorId, info.hid.dwProductId);
- else
+ }
+ else {
GHOST_PRINT("<!> not a HID device... mouse/kb perhaps?\n");
-
+ }
firstEvent = false;
}
- // The NDOF manager sends button changes immediately, and *pretends* to
- // send motion. Mark as 'sent' so motion will always get dispatched.
+ /* The NDOF manager sends button changes immediately, and *pretends* to
+ * send motion. Mark as 'sent' so motion will always get dispatched. */
eventSent = true;
BYTE const *data = raw.data.hid.bRawData;
BYTE packetType = data[0];
switch (packetType) {
- case 1: // translation
- {
+ case 1: { /* Translation. */
const short *axis = (short *)(data + 1);
- // massage into blender view coords (same goes for rotation)
+ /* Massage into blender view coords (same goes for rotation). */
const int t[3] = {axis[0], -axis[2], axis[1]};
m_ndofManager->updateTranslation(t, now);
if (raw.data.hid.dwSizeHid == 13) {
- // this report also includes rotation
+ /* This report also includes rotation. */
const int r[3] = {-axis[3], axis[5], -axis[4]};
m_ndofManager->updateRotation(r, now);
- // I've never gotten one of these, has anyone else?
+ /* I've never gotten one of these, has anyone else? */
GHOST_PRINT("ndof: combined T + R\n");
}
break;
}
- case 2: // rotation
- {
+ case 2: { /* Rotation. */
+
const short *axis = (short *)(data + 1);
const int r[3] = {-axis[0], axis[2], -axis[1]};
m_ndofManager->updateRotation(r, now);
break;
}
- case 3: // buttons
- {
+ case 3: { /* Buttons. */
int button_bits;
memcpy(&button_bits, data + 1, sizeof(button_bits));
m_ndofManager->updateButtons(button_bits, now);
@@ -1420,7 +1363,7 @@ bool GHOST_SystemWin32::processNDOF(RAWINPUT const &raw)
}
return eventSent;
}
-#endif // WITH_INPUT_NDOF
+#endif /* WITH_INPUT_NDOF */
void GHOST_SystemWin32::driveTrackpad()
{
@@ -1483,8 +1426,8 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
if (hwnd) {
if (msg == WM_NCCREATE) {
- // Tell Windows to automatically handle scaling of non-client areas
- // such as the caption bar. EnableNonClientDpiScaling was introduced in Windows 10
+ /* Tell Windows to automatically handle scaling of non-client areas
+ * such as the caption bar. #EnableNonClientDpiScaling was introduced in Windows 10. */
HMODULE m_user32 = ::LoadLibrary("User32.dll");
if (m_user32) {
GHOST_WIN32_EnableNonClientDpiScaling fpEnableNonClientDpiScaling =
@@ -1499,7 +1442,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
GHOST_WindowWin32 *window = (GHOST_WindowWin32 *)::GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (window) {
switch (msg) {
- // we need to check if new key layout has AltGr
+ /* We need to check if new key layout has AltGr. */
case WM_INPUTLANGCHANGE: {
system->handleKeyboardChange();
#ifdef WITH_INPUT_IME
@@ -1508,9 +1451,9 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
#endif
break;
}
- ////////////////////////////////////////////////////////////////////////
- // Keyboard events, processed
- ////////////////////////////////////////////////////////////////////////
+ /* ==========================
+ * Keyboard events, processed
+ * ========================== */
case WM_INPUT: {
RAWINPUT raw;
RAWINPUT *raw_ptr = &raw;
@@ -1519,7 +1462,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, raw_ptr, &rawSize, sizeof(RAWINPUTHEADER));
switch (raw.header.dwType) {
- case RIM_TYPEKEYBOARD:
+ case RIM_TYPEKEYBOARD: {
event = processKeyEvent(window, raw);
if (!event) {
GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ");
@@ -1527,20 +1470,22 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
GHOST_PRINT(" key ignored\n");
}
break;
+ }
#ifdef WITH_INPUT_NDOF
- case RIM_TYPEHID:
+ case RIM_TYPEHID: {
if (system->processNDOF(raw)) {
eventHandled = true;
}
break;
+ }
#endif
}
break;
}
#ifdef WITH_INPUT_IME
- ////////////////////////////////////////////////////////////////////////
- // IME events, processed, read more in GHOST_IME.h
- ////////////////////////////////////////////////////////////////////////
+ /* =================================================
+ * IME events, processed, read more in `GHOST_IME.h`
+ * ================================================= */
case WM_IME_NOTIFY: {
/* Update conversion status when IME is changed or input mode is changed. */
if (wParam == IMN_SETOPENSTATUS || wParam == IMN_SETCONVERSIONMODE) {
@@ -1588,56 +1533,53 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
break;
}
#endif /* WITH_INPUT_IME */
- ////////////////////////////////////////////////////////////////////////
- // Keyboard events, ignored
- ////////////////////////////////////////////////////////////////////////
+ /* ========================
+ * Keyboard events, ignored
+ * ======================== */
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_KEYUP:
case WM_SYSKEYUP:
- /* These functions were replaced by #WM_INPUT. */
+ /* These functions were replaced by #WM_INPUT. */
case WM_CHAR:
- /* The WM_CHAR message is posted to the window with the keyboard focus when
- * a WM_KEYDOWN message is translated by the TranslateMessage function. WM_CHAR
- * contains the character code of the key that was pressed.
- */
+ /* The #WM_CHAR message is posted to the window with the keyboard focus when
+ * a WM_KEYDOWN message is translated by the #TranslateMessage function.
+ * WM_CHAR contains the character code of the key that was pressed. */
case WM_DEADCHAR:
- /* The WM_DEADCHAR message is posted to the window with the keyboard focus when a
- * WM_KEYUP message is translated by the TranslateMessage function. WM_DEADCHAR
+ /* The #WM_DEADCHAR message is posted to the window with the keyboard focus when a
+ * WM_KEYUP message is translated by the #TranslateMessage function. WM_DEADCHAR
* specifies a character code generated by a dead key. A dead key is a key that
* generates a character, such as the umlaut (double-dot), that is combined with
* another character to form a composite character. For example, the umlaut-O
* character (Ö) is generated by typing the dead key for the umlaut character, and
- * then typing the O key.
- */
+ * then typing the O key. */
break;
case WM_SYSDEADCHAR:
- /* The WM_SYSDEADCHAR message is sent to the window with the keyboard focus when
- * a WM_SYSKEYDOWN message is translated by the TranslateMessage function.
- * WM_SYSDEADCHAR specifies the character code of a system dead key - that is,
- * a dead key that is pressed while holding down the alt key.
- */
- case WM_SYSCHAR:
- /* The WM_SYSCHAR message is sent to the window with the keyboard focus when
- * a WM_SYSCHAR message is translated by the TranslateMessage function.
+ /* The #WM_SYSDEADCHAR message is sent to the window with the keyboard focus when
+ * a WM_SYSKEYDOWN message is translated by the #TranslateMessage function.
+ * WM_SYSDEADCHAR specifies the character code of a system dead key - that is,
+ * a dead key that is pressed while holding down the alt key. */
+ case WM_SYSCHAR: {
+ /* #The WM_SYSCHAR message is sent to the window with the keyboard focus when
+ * a WM_SYSCHAR message is translated by the #TranslateMessage function.
* WM_SYSCHAR specifies the character code of a dead key - that is,
* a dead key that is pressed while holding down the alt key.
- * To prevent the sound, DefWindowProc must be avoided by return
- */
+ * To prevent the sound, #DefWindowProc must be avoided by return. */
break;
- case WM_SYSCOMMAND:
- /* The WM_SYSCOMMAND message is sent to the window when system commands such as
+ }
+ case WM_SYSCOMMAND: {
+ /* The #WM_SYSCOMMAND message is sent to the window when system commands such as
* maximize, minimize or close the window are triggered. Also it is sent when ALT
- * button is press for menu. To prevent this we must return preventing DefWindowProc.
+ * button is press for menu. To prevent this we must return preventing #DefWindowProc.
*
* Note that the four low-order bits of the wParam parameter are used internally by the
* OS. To obtain the correct result when testing the value of wParam, an application must
- * combine the value 0xFFF0 with the wParam value by using the bit-wise AND operator.
- */
+ * combine the value 0xFFF0 with the wParam value by using the bit-wise AND operator. */
switch (wParam & 0xFFF0) {
- case SC_KEYMENU:
+ case SC_KEYMENU: {
eventHandled = true;
break;
+ }
case SC_RESTORE: {
::ShowWindow(hwnd, SW_RESTORE);
window->setState(window->getState());
@@ -1668,9 +1610,10 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
}
}
break;
- ////////////////////////////////////////////////////////////////////////
- // Wintab events, processed
- ////////////////////////////////////////////////////////////////////////
+ }
+ /* ========================
+ * Wintab events, processed
+ * ======================== */
case WT_CSRCHANGE: {
WINTAB_PRINTF("HWND %p HCTX %p WT_CSRCHANGE\n", window->getHWND(), (void *)lParam);
GHOST_Wintab *wt = window->getWintab();
@@ -1722,44 +1665,53 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
eventHandled = true;
break;
}
- case WT_PACKET:
+ case WT_PACKET: {
processWintabEvent(window);
eventHandled = true;
break;
- ////////////////////////////////////////////////////////////////////////
- // Wintab events, debug
- ////////////////////////////////////////////////////////////////////////
- case WT_CTXOPEN:
+ }
+ /* ====================
+ * Wintab events, debug
+ * ==================== */
+ case WT_CTXOPEN: {
WINTAB_PRINTF("HWND %p HCTX %p WT_CTXOPEN\n", window->getHWND(), (void *)wParam);
break;
- case WT_CTXCLOSE:
+ }
+ case WT_CTXCLOSE: {
WINTAB_PRINTF("HWND %p HCTX %p WT_CTXCLOSE\n", window->getHWND(), (void *)wParam);
break;
- case WT_CTXUPDATE:
+ }
+ case WT_CTXUPDATE: {
WINTAB_PRINTF("HWND %p HCTX %p WT_CTXUPDATE\n", window->getHWND(), (void *)wParam);
break;
- case WT_CTXOVERLAP:
+ }
+ case WT_CTXOVERLAP: {
WINTAB_PRINTF("HWND %p HCTX %p WT_CTXOVERLAP", window->getHWND(), (void *)wParam);
switch (lParam) {
- case CXS_DISABLED:
+ case CXS_DISABLED: {
WINTAB_PRINTF(" CXS_DISABLED\n");
break;
- case CXS_OBSCURED:
+ }
+ case CXS_OBSCURED: {
WINTAB_PRINTF(" CXS_OBSCURED\n");
break;
- case CXS_ONTOP:
+ }
+ case CXS_ONTOP: {
WINTAB_PRINTF(" CXS_ONTOP\n");
break;
+ }
}
break;
- ////////////////////////////////////////////////////////////////////////
- // Pointer events, processed
- ////////////////////////////////////////////////////////////////////////
+ }
+ /* =========================
+ * Pointer events, processed
+ * ========================= */
case WM_POINTERUPDATE:
case WM_POINTERDOWN:
- case WM_POINTERUP:
+ case WM_POINTERUP: {
processPointerEvent(msg, window, wParam, lParam, eventHandled);
break;
+ }
case WM_POINTERLEAVE: {
uint32_t pointerId = GET_POINTERID_WPARAM(wParam);
POINTER_INFO pointerInfo;
@@ -1774,19 +1726,22 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
}
break;
}
- ////////////////////////////////////////////////////////////////////////
- // Mouse events, processed
- ////////////////////////////////////////////////////////////////////////
- case WM_LBUTTONDOWN:
+ /* =======================
+ * Mouse events, processed
+ * ======================= */
+ case WM_LBUTTONDOWN: {
event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft);
break;
- case WM_MBUTTONDOWN:
+ }
+ case WM_MBUTTONDOWN: {
event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskMiddle);
break;
- case WM_RBUTTONDOWN:
+ }
+ case WM_RBUTTONDOWN: {
event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight);
break;
- case WM_XBUTTONDOWN:
+ }
+ case WM_XBUTTONDOWN: {
if ((short)HIWORD(wParam) == XBUTTON1) {
event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton4);
}
@@ -1794,16 +1749,20 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton5);
}
break;
- case WM_LBUTTONUP:
+ }
+ case WM_LBUTTONUP: {
event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft);
break;
- case WM_MBUTTONUP:
+ }
+ case WM_MBUTTONUP: {
event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle);
break;
- case WM_RBUTTONUP:
+ }
+ case WM_RBUTTONUP: {
event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight);
break;
- case WM_XBUTTONUP:
+ }
+ case WM_XBUTTONUP: {
if ((short)HIWORD(wParam) == XBUTTON1) {
event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton4);
}
@@ -1811,7 +1770,8 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton5);
}
break;
- case WM_MOUSEMOVE:
+ }
+ case WM_MOUSEMOVE: {
if (!window->m_mousePresent) {
WINTAB_PRINTF("HWND %p mouse enter\n", window->getHWND());
TRACKMOUSEEVENT tme = {sizeof(tme)};
@@ -1828,6 +1788,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
event = processCursorEvent(window);
break;
+ }
case WM_MOUSEWHEEL: {
/* The WM_MOUSEWHEEL message is sent to the focus window
* when the mouse wheel is rotated. The DefWindowProc
@@ -1843,7 +1804,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
#endif
break;
}
- case WM_SETCURSOR:
+ case WM_SETCURSOR: {
/* The WM_SETCURSOR message is sent to a window if the mouse causes the cursor
* to move within a window and mouse input is not captured.
* This means we have to set the cursor shape every time the mouse moves!
@@ -1851,16 +1812,17 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
* arrow if it is not in the client area.
*/
if (LOWORD(lParam) == HTCLIENT) {
- // Load the current cursor
+ /* Load the current cursor. */
window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
- // Bypass call to DefWindowProc
+ /* Bypass call to #DefWindowProc. */
return 0;
}
else {
- // Outside of client area show standard cursor
+ /* Outside of client area show standard cursor. */
window->loadCursor(true, GHOST_kStandardCursorDefault);
}
break;
+ }
case WM_MOUSELEAVE: {
WINTAB_PRINTF("HWND %p mouse leave\n", window->getHWND());
window->m_mousePresent = false;
@@ -1873,26 +1835,27 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
}
break;
}
- ////////////////////////////////////////////////////////////////////////
- // Mouse events, ignored
- ////////////////////////////////////////////////////////////////////////
- case WM_NCMOUSEMOVE:
- /* The WM_NCMOUSEMOVE message is posted to a window when the cursor is moved
- * within the non-client area of the window. This message is posted to the window that
- * contains the cursor. If a window has captured the mouse, this message is not posted.
- */
- case WM_NCHITTEST:
+ /* =====================
+ * Mouse events, ignored
+ * ===================== */
+ case WM_NCMOUSEMOVE: {
+ /* The WM_NCMOUSEMOVE message is posted to a window when the cursor is moved
+ * within the non-client area of the window. This message is posted to the window that
+ * contains the cursor. If a window has captured the mouse, this message is not posted.
+ */
+ }
+ case WM_NCHITTEST: {
/* The WM_NCHITTEST message is sent to a window when the cursor moves, or
* when a mouse button is pressed or released. If the mouse is not captured,
* the message is sent to the window beneath the cursor. Otherwise, the message
* is sent to the window that has captured the mouse.
*/
break;
-
- ////////////////////////////////////////////////////////////////////////
- // Window events, processed
- ////////////////////////////////////////////////////////////////////////
- case WM_CLOSE:
+ }
+ /* ========================
+ * Window events, processed
+ * ======================== */
+ case WM_CLOSE: {
/* The WM_CLOSE message is sent as a signal that a window
* or an application should terminate. Restore if minimized. */
if (IsIconic(hwnd)) {
@@ -1900,31 +1863,29 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
}
event = processWindowEvent(GHOST_kEventWindowClose, window);
break;
- case WM_ACTIVATE:
+ }
+ case WM_ACTIVATE: {
/* The WM_ACTIVATE message is sent to both the window being activated and the window
* being deactivated. If the windows use the same input queue, the message is sent
* synchronously, first to the window procedure of the top-level window being
* deactivated, then to the window procedure of the top-level window being activated.
* If the windows use different input queues, the message is sent asynchronously,
* so the window is activated immediately. */
- {
- GHOST_ModifierKeys modifiers;
- modifiers.clear();
- system->storeModifierKeys(modifiers);
- system->m_wheelDeltaAccum = 0;
- system->m_keycode_last_repeat_key = 0;
- event = processWindowEvent(LOWORD(wParam) ? GHOST_kEventWindowActivate :
- GHOST_kEventWindowDeactivate,
- window);
- /* WARNING: Let DefWindowProc handle WM_ACTIVATE, otherwise WM_MOUSEWHEEL
- * will not be dispatched to OUR active window if we minimize one of OUR windows. */
- if (LOWORD(wParam) == WA_INACTIVE)
- window->lostMouseCapture();
-
- lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
- break;
+
+ system->m_wheelDeltaAccum = 0;
+ system->m_keycode_last_repeat_key = 0;
+ event = processWindowEvent(
+ LOWORD(wParam) ? GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate, window);
+ /* WARNING: Let DefWindowProc handle WM_ACTIVATE, otherwise WM_MOUSEWHEEL
+ * will not be dispatched to OUR active window if we minimize one of OUR windows. */
+ if (LOWORD(wParam) == WA_INACTIVE) {
+ window->lostMouseCapture();
}
- case WM_ENTERSIZEMOVE:
+
+ lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
+ break;
+ }
+ case WM_ENTERSIZEMOVE: {
/* The WM_ENTERSIZEMOVE message is sent one time to a window after it enters the moving
* or sizing modal loop. The window enters the moving or sizing modal loop when the user
* clicks the window's title bar or sizing border, or when the window passes the
@@ -1934,10 +1895,12 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
*/
window->m_inLiveResize = 1;
break;
- case WM_EXITSIZEMOVE:
+ }
+ case WM_EXITSIZEMOVE: {
window->m_inLiveResize = 0;
break;
- case WM_PAINT:
+ }
+ case WM_PAINT: {
/* An application sends the WM_PAINT message when the system or another application
* makes a request to paint a portion of an application's window. The message is sent
* when the UpdateWindow or RedrawWindow function is called, or by the DispatchMessage
@@ -1952,7 +1915,8 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
eventHandled = true;
}
break;
- case WM_GETMINMAXINFO:
+ }
+ case WM_GETMINMAXINFO: {
/* The WM_GETMINMAXINFO message is sent to a window when the size or
* position of the window is about to change. An application can use
* this message to override the window's default maximized size and
@@ -1961,10 +1925,12 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
processMinMaxInfo((MINMAXINFO *)lParam);
/* Let DefWindowProc handle it. */
break;
- case WM_SIZING:
+ }
+ case WM_SIZING: {
event = processWindowSizeEvent(window);
break;
- case WM_SIZE:
+ }
+ case WM_SIZE: {
/* The WM_SIZE message is sent to a window after its size has changed.
* The WM_SIZE and WM_MOVE messages are not sent if an application handles the
* WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
@@ -1973,15 +1939,17 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
*/
event = processWindowSizeEvent(window);
break;
- case WM_CAPTURECHANGED:
+ }
+ case WM_CAPTURECHANGED: {
window->lostMouseCapture();
break;
+ }
case WM_MOVING:
/* The WM_MOVING message is sent to a window that the user is moving. By processing
* this message, an application can monitor the size and position of the drag rectangle
* and, if needed, change its size or position.
*/
- case WM_MOVE:
+ case WM_MOVE: {
/* The WM_SIZE and WM_MOVE messages are not sent if an application handles the
* WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
* to perform any move or size change processing during the WM_WINDOWPOSCHANGED
@@ -1997,33 +1965,33 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
}
break;
- case WM_DPICHANGED:
+ }
+ case WM_DPICHANGED: {
/* The WM_DPICHANGED message is sent when the effective dots per inch (dpi) for a
* window has changed. The DPI is the scale factor for a window. There are multiple
* events that can cause the DPI to change such as when the window is moved to a monitor
- * with a different DPI.
- */
- {
- // The suggested new size and position of the window.
- RECT *const suggestedWindowRect = (RECT *)lParam;
+ * with a different DPI. */
- // Push DPI change event first
- system->pushEvent(processWindowEvent(GHOST_kEventWindowDPIHintChanged, window));
- system->dispatchEvents();
- eventHandled = true;
+ /* The suggested new size and position of the window. */
+ RECT *const suggestedWindowRect = (RECT *)lParam;
- // Then move and resize window
- SetWindowPos(hwnd,
- NULL,
- suggestedWindowRect->left,
- suggestedWindowRect->top,
- suggestedWindowRect->right - suggestedWindowRect->left,
- suggestedWindowRect->bottom - suggestedWindowRect->top,
- SWP_NOZORDER | SWP_NOACTIVATE);
+ /* Push DPI change event first. */
+ system->pushEvent(processWindowEvent(GHOST_kEventWindowDPIHintChanged, window));
+ system->dispatchEvents();
+ eventHandled = true;
- window->updateDPI();
- }
+ /* Then move and resize window. */
+ SetWindowPos(hwnd,
+ NULL,
+ suggestedWindowRect->left,
+ suggestedWindowRect->top,
+ suggestedWindowRect->right - suggestedWindowRect->left,
+ suggestedWindowRect->bottom - suggestedWindowRect->top,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+
+ window->updateDPI();
break;
+ }
case WM_DISPLAYCHANGE: {
GHOST_Wintab *wt = window->getWintab();
if (wt) {
@@ -2031,7 +1999,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
}
break;
}
- case WM_KILLFOCUS:
+ case WM_KILLFOCUS: {
/* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard
* focus. We want to prevent this if a window is still active and it loses focus to
* nowhere. */
@@ -2039,73 +2007,73 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
::SetFocus(hwnd);
}
break;
- case WM_SETTINGCHANGE:
+ }
+ case WM_SETTINGCHANGE: {
/* Microsoft: "Note that some applications send this message with lParam set to NULL" */
if ((lParam != NULL) && (wcscmp(LPCWSTR(lParam), L"ImmersiveColorSet") == 0)) {
window->ThemeRefresh();
}
break;
- ////////////////////////////////////////////////////////////////////////
- // Window events, ignored
- ////////////////////////////////////////////////////////////////////////
+ }
+ /* ======================
+ * Window events, ignored
+ * ====================== */
case WM_WINDOWPOSCHANGED:
- /* The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place
- * in the Z order has changed as a result of a call to the SetWindowPos function or
- * another window-management function.
- * The WM_SIZE and WM_MOVE messages are not sent if an application handles the
- * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
- * to perform any move or size change processing during the WM_WINDOWPOSCHANGED
- * message without calling DefWindowProc.
- */
+ /* The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place
+ * in the Z order has changed as a result of a call to the SetWindowPos function or
+ * another window-management function.
+ * The WM_SIZE and WM_MOVE messages are not sent if an application handles the
+ * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
+ * to perform any move or size change processing during the WM_WINDOWPOSCHANGED
+ * message without calling DefWindowProc.
+ */
case WM_ERASEBKGND:
- /* An application sends the WM_ERASEBKGND message when the window background must be
- * erased (for example, when a window is resized). The message is sent to prepare an
- * invalidated portion of a window for painting.
- */
+ /* An application sends the WM_ERASEBKGND message when the window background must be
+ * erased (for example, when a window is resized). The message is sent to prepare an
+ * invalidated portion of a window for painting. */
case WM_NCPAINT:
- /* An application sends the WM_NCPAINT message to a window
- * when its frame must be painted. */
+ /* An application sends the WM_NCPAINT message to a window
+ * when its frame must be painted. */
case WM_NCACTIVATE:
- /* The WM_NCACTIVATE message is sent to a window when its non-client area needs to be
- * changed to indicate an active or inactive state. */
+ /* The WM_NCACTIVATE message is sent to a window when its non-client area needs to be
+ * changed to indicate an active or inactive state. */
case WM_DESTROY:
- /* The WM_DESTROY message is sent when a window is being destroyed. It is sent to the
- * window procedure of the window being destroyed after the window is removed from the
- * screen. This message is sent first to the window being destroyed and then to the child
- * windows (if any) as they are destroyed. During the processing of the message, it can
- * be assumed that all child windows still exist. */
- case WM_NCDESTROY:
+ /* The WM_DESTROY message is sent when a window is being destroyed. It is sent to the
+ * window procedure of the window being destroyed after the window is removed from the
+ * screen. This message is sent first to the window being destroyed and then to the child
+ * windows (if any) as they are destroyed. During the processing of the message, it can
+ * be assumed that all child windows still exist. */
+ case WM_NCDESTROY: {
/* The WM_NCDESTROY message informs a window that its non-client area is being
* destroyed. The DestroyWindow function sends the WM_NCDESTROY message to the window
* following the WM_DESTROY message. WM_DESTROY is used to free the allocated memory
* object associated with the window.
*/
break;
+ }
case WM_SHOWWINDOW:
- /* The WM_SHOWWINDOW message is sent to a window when the window is
- * about to be hidden or shown. */
+ /* The WM_SHOWWINDOW message is sent to a window when the window is
+ * about to be hidden or shown. */
case WM_WINDOWPOSCHANGING:
- /* The WM_WINDOWPOSCHANGING message is sent to a window whose size, position, or place in
- * the Z order is about to change as a result of a call to the SetWindowPos function or
- * another window-management function.
- */
- case WM_SETFOCUS:
+ /* The WM_WINDOWPOSCHANGING message is sent to a window whose size, position, or place in
+ * the Z order is about to change as a result of a call to the SetWindowPos function or
+ * another window-management function. */
+ case WM_SETFOCUS: {
/* The WM_SETFOCUS message is sent to a window after it has gained the keyboard focus. */
break;
- ////////////////////////////////////////////////////////////////////////
- // Other events
- ////////////////////////////////////////////////////////////////////////
+ }
+ /* ============
+ * Other events
+ * ============ */
case WM_GETTEXT:
- /* An application sends a WM_GETTEXT message to copy the text that
- * corresponds to a window into a buffer provided by the caller.
- */
+ /* An application sends a WM_GETTEXT message to copy the text that
+ * corresponds to a window into a buffer provided by the caller. */
case WM_ACTIVATEAPP:
- /* The WM_ACTIVATEAPP message is sent when a window belonging to a
- * different application than the active window is about to be activated.
- * The message is sent to the application whose window is being activated
- * and to the application whose window is being deactivated.
- */
- case WM_TIMER:
+ /* The WM_ACTIVATEAPP message is sent when a window belonging to a
+ * different application than the active window is about to be activated.
+ * The message is sent to the application whose window is being activated
+ * and to the application whose window is being deactivated. */
+ case WM_TIMER: {
/* The WIN32 docs say:
* The WM_TIMER message is posted to the installing thread's message queue
* when a timer expires. You can process the message by providing a WM_TIMER
@@ -2113,19 +2081,20 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
* call the TimerProc callback function specified in the call to the SetTimer
* function used to install the timer.
*
- * In GHOST, we let DefWindowProc call the timer callback.
- */
+ * In GHOST, we let DefWindowProc call the timer callback. */
break;
- case DM_POINTERHITTEST:
+ }
+ case DM_POINTERHITTEST: {
/* The DM_POINTERHITTEST message is sent to a window, when pointer input is first
* detected, in order to determine the most probable input target for Direct
* Manipulation. */
window->onPointerHitTest(wParam);
break;
+ }
}
}
else {
- // Event found for a window before the pointer to the class has been set.
+ /* Event found for a window before the pointer to the class has been set. */
GHOST_PRINT("GHOST_SystemWin32::wndProc: GHOST window event before creation\n");
/* These are events we typically miss at this point:
* WM_GETMINMAXINFO 0x24
@@ -2137,7 +2106,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
}
}
else {
- // Events without valid hwnd
+ /* Events without valid `hwnd`. */
GHOST_PRINT("GHOST_SystemWin32::wndProc: event without window\n");
}
@@ -2146,16 +2115,15 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
eventHandled = true;
}
- if (!eventHandled)
+ if (!eventHandled) {
lResult = ::DefWindowProcW(hwnd, msg, wParam, lParam);
+ }
return lResult;
}
char *GHOST_SystemWin32::getClipboard(bool selection) const
{
- char *temp_buff;
-
if (IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(NULL)) {
wchar_t *buffer;
HANDLE hData = GetClipboardData(CF_UNICODETEXT);
@@ -2169,7 +2137,7 @@ char *GHOST_SystemWin32::getClipboard(bool selection) const
return NULL;
}
- temp_buff = alloc_utf_8_from_16(buffer, 0);
+ char *temp_buff = alloc_utf_8_from_16(buffer, 0);
/* Buffer mustn't be accessed after CloseClipboard
* it would like accessing free-d memory */
@@ -2178,7 +2146,7 @@ char *GHOST_SystemWin32::getClipboard(bool selection) const
return temp_buff;
}
- else if (IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(NULL)) {
+ if (IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(NULL)) {
char *buffer;
size_t len = 0;
HANDLE hData = GetClipboardData(CF_TEXT);
@@ -2193,7 +2161,7 @@ char *GHOST_SystemWin32::getClipboard(bool selection) const
}
len = strlen(buffer);
- temp_buff = (char *)malloc(len + 1);
+ char *temp_buff = (char *)malloc(len + 1);
strncpy(temp_buff, buffer, len);
temp_buff[len] = '\0';
@@ -2204,21 +2172,19 @@ char *GHOST_SystemWin32::getClipboard(bool selection) const
return temp_buff;
}
- else {
- return NULL;
- }
+ return nullptr;
}
void GHOST_SystemWin32::putClipboard(const char *buffer, bool selection) const
{
if (selection || !buffer) {
return;
- } // for copying the selection, used on X11
+ } /* For copying the selection, used on X11. */
if (OpenClipboard(NULL)) {
EmptyClipboard();
- // Get length of buffer including the terminating null
+ /* Get length of buffer including the terminating null. */
size_t len = count_utf_16_from_8(buffer);
HGLOBAL clipbuffer = GlobalAlloc(GMEM_MOVEABLE, sizeof(wchar_t) * len);
@@ -2275,7 +2241,7 @@ GHOST_TSuccess GHOST_SystemWin32::showMessageBox(const char *title,
case IDCONTINUE:
break;
default:
- break; // should never happen
+ break; /* Should never happen. */
}
free((void *)title_16);
@@ -2343,14 +2309,15 @@ static bool isStartedFromCommandPrompt()
}
/* When we're starting from a wrapper we need to compare with parent process ID. */
- if (pid != (start_from_launcher ? ppid : GetCurrentProcessId()))
+ if (pid != (start_from_launcher ? ppid : GetCurrentProcessId())) {
return true;
+ }
}
return false;
}
-int GHOST_SystemWin32::setConsoleWindowState(GHOST_TConsoleWindowState action)
+bool GHOST_SystemWin32::setConsoleWindowState(GHOST_TConsoleWindowState action)
{
HWND wnd = GetConsoleWindow();
@@ -2358,28 +2325,31 @@ int GHOST_SystemWin32::setConsoleWindowState(GHOST_TConsoleWindowState action)
case GHOST_kConsoleWindowStateHideForNonConsoleLaunch: {
if (!isStartedFromCommandPrompt()) {
ShowWindow(wnd, SW_HIDE);
- m_consoleStatus = 0;
+ m_consoleStatus = false;
}
break;
}
- case GHOST_kConsoleWindowStateHide:
+ case GHOST_kConsoleWindowStateHide: {
ShowWindow(wnd, SW_HIDE);
- m_consoleStatus = 0;
+ m_consoleStatus = false;
break;
- case GHOST_kConsoleWindowStateShow:
+ }
+ case GHOST_kConsoleWindowStateShow: {
ShowWindow(wnd, SW_SHOW);
if (!isStartedFromCommandPrompt()) {
DeleteMenu(GetSystemMenu(wnd, FALSE), SC_CLOSE, MF_BYCOMMAND);
}
- m_consoleStatus = 1;
+ m_consoleStatus = true;
break;
- case GHOST_kConsoleWindowStateToggle:
+ }
+ case GHOST_kConsoleWindowStateToggle: {
ShowWindow(wnd, m_consoleStatus ? SW_HIDE : SW_SHOW);
m_consoleStatus = !m_consoleStatus;
if (m_consoleStatus && !isStartedFromCommandPrompt()) {
DeleteMenu(GetSystemMenu(wnd, FALSE), SC_CLOSE, MF_BYCOMMAND);
}
break;
+ }
}
return m_consoleStatus;
diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h
index 689b78b0317..81c565ae732 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.h
+++ b/intern/ghost/intern/GHOST_SystemWin32.h
@@ -10,10 +10,10 @@
#ifndef WIN32
# error WIN32 only!
-#endif // WIN32
+#endif /* WIN32 */
#define WIN32_LEAN_AND_MEAN
-#include <ole2.h> // for drag-n-drop
+#include <ole2.h> /* For drag-n-drop. */
#include <windows.h>
#include "GHOST_System.h"
@@ -295,11 +295,10 @@ class GHOST_SystemWin32 : public GHOST_System {
/**
* Catches raw WIN32 key codes from WM_INPUT in the wndproc.
* \param raw: RawInput structure with detailed info about the key event.
- * \param keyDown: Pointer flag that specify if a key is down.
- * \param vk: Pointer to virtual key.
+ * \param r_key_down: Set true when the key is pressed, otherwise false.
* \return The GHOST key (GHOST_kKeyUnknown if no match).
*/
- GHOST_TKey hardKey(RAWINPUT const &raw, bool *r_keyDown, bool *r_is_repeated_modifier);
+ GHOST_TKey hardKey(RAWINPUT const &raw, bool *r_key_down);
/**
* Creates mouse button event.
@@ -310,7 +309,7 @@ class GHOST_SystemWin32 : public GHOST_System {
*/
static GHOST_EventButton *processButtonEvent(GHOST_TEventType type,
GHOST_WindowWin32 *window,
- GHOST_TButtonMask mask);
+ GHOST_TButton mask);
/**
* Creates tablet events from Wintab events.
@@ -387,7 +386,7 @@ class GHOST_SystemWin32 : public GHOST_System {
static GHOST_Event *processImeEvent(GHOST_TEventType type,
GHOST_WindowWin32 *window,
GHOST_TEventImeData *data);
-#endif // WITH_INPUT_IME
+#endif /* WITH_INPUT_IME */
/**
* Handles minimum window size.
@@ -417,19 +416,6 @@ class GHOST_SystemWin32 : public GHOST_System {
void processTrackpad();
/**
- * Returns the local state of the modifier keys (from the message queue).
- * \param keys: The state of the keys.
- */
- inline void retrieveModifierKeys(GHOST_ModifierKeys &keys) const;
-
- /**
- * Stores the state of the modifier keys locally.
- * For internal use only!
- * param keys The new state of the modifier keys.
- */
- inline void storeModifierKeys(const GHOST_ModifierKeys &keys);
-
- /**
* Check current key layout for AltGr
*/
inline void handleKeyboardChange(void);
@@ -444,10 +430,8 @@ class GHOST_SystemWin32 : public GHOST_System {
* \param action: console state
* \return current status (1 -visible, 0 - hidden)
*/
- int setConsoleWindowState(GHOST_TConsoleWindowState action);
+ bool setConsoleWindowState(GHOST_TConsoleWindowState action);
- /** The current state of the modifier keys. */
- GHOST_ModifierKeys m_modifierKeys;
/** The virtual-key code (VKey) of the last press event. Used to detect repeat events. */
unsigned short m_keycode_last_repeat_key;
/** State variable set at initialization. */
@@ -466,36 +450,26 @@ class GHOST_SystemWin32 : public GHOST_System {
HKL m_keylayout;
/** Console status. */
- int m_consoleStatus;
+ bool m_consoleStatus;
/** Wheel delta accumulator. */
int m_wheelDeltaAccum;
};
-inline void GHOST_SystemWin32::retrieveModifierKeys(GHOST_ModifierKeys &keys) const
-{
- keys = m_modifierKeys;
-}
-
-inline void GHOST_SystemWin32::storeModifierKeys(const GHOST_ModifierKeys &keys)
-{
- m_modifierKeys = keys;
-}
-
inline void GHOST_SystemWin32::handleKeyboardChange(void)
{
- m_keylayout = GetKeyboardLayout(0); // get keylayout for current thread
+ m_keylayout = GetKeyboardLayout(0); /* Get keylayout for current thread. */
int i;
SHORT s;
- // save the language identifier.
+ /* Save the language identifier. */
m_langId = LOWORD(m_keylayout);
for (m_hasAltGr = false, i = 32; i < 256; ++i) {
s = VkKeyScanEx((char)i, m_keylayout);
- // s == -1 means no key that translates passed char code
- // high byte contains shift state. bit 2 ctrl pressed, bit 4 alt pressed
- // if both are pressed, we have AltGr keycombo on keylayout
+ /* `s == -1` means no key that translates passed char code high byte contains shift state.
+ * bit 2 Control pressed, bit 4 `Alt` pressed if both are pressed,
+ * we have `AltGr` key-combination on key-layout. */
if (s != -1 && (s & 0x600) == 0x600) {
m_hasAltGr = true;
break;
diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp
index bed9cd6c784..bb98c0de19b 100644
--- a/intern/ghost/intern/GHOST_SystemX11.cpp
+++ b/intern/ghost/intern/GHOST_SystemX11.cpp
@@ -25,6 +25,7 @@
#ifdef WITH_INPUT_NDOF
# include "GHOST_NDOFManagerUnix.h"
#endif
+#include "GHOST_utildefines.h"
#ifdef WITH_XDND
# include "GHOST_DropTargetX11.h"
@@ -32,12 +33,8 @@
#include "GHOST_Debug.h"
-#if defined(WITH_GL_EGL)
-# include "GHOST_ContextEGL.h"
-# include <EGL/eglext.h>
-#else
-# include "GHOST_ContextGLX.h"
-#endif
+#include "GHOST_ContextEGL.h"
+#include "GHOST_ContextGLX.h"
#ifdef WITH_XF86KEYSYM
# include <X11/XF86keysym.h>
@@ -104,8 +101,7 @@ GHOST_SystemX11::GHOST_SystemX11() : GHOST_System(), m_xkb_descr(nullptr), m_sta
m_display = XOpenDisplay(nullptr);
if (!m_display) {
- std::cerr << "Unable to open a display" << std::endl;
- abort(); /* was return before, but this would just mean it will crash later */
+ throw std::runtime_error("X11: Unable to open a display");
}
#ifdef USE_X11_ERROR_HANDLERS
@@ -234,10 +230,6 @@ GHOST_SystemX11::~GHOST_SystemX11()
clearXInputDevices();
#endif /* WITH_X11_XINPUT */
-#ifdef WITH_GL_EGL
- ::eglTerminate(::eglGetDisplay(m_display));
-#endif
-
if (m_xkb_descr) {
XkbFreeKeyboard(m_xkb_descr, XkbAllComponentsMask, true);
}
@@ -286,7 +278,7 @@ uint8_t GHOST_SystemX11::getNumDisplays() const
void GHOST_SystemX11::getMainDisplayDimensions(uint32_t &width, uint32_t &height) const
{
if (m_display) {
- /* NOTE(campbell): for this to work as documented,
+ /* NOTE(@campbellbarton): for this to work as documented,
* we would need to use Xinerama check r54370 for code that did this,
* we've since removed since its not worth the extra dependency. */
getAllDisplayDimensions(width, height);
@@ -353,7 +345,6 @@ GHOST_IWindow *GHOST_SystemX11::createWindow(const char *title,
is_dialog,
((glSettings.flags & GHOST_glStereoVisual) != 0),
exclusive,
- ((glSettings.flags & GHOST_glAlphaBackground) != 0),
(glSettings.flags & GHOST_glDebugContext) != 0);
if (window) {
@@ -374,6 +365,56 @@ GHOST_IWindow *GHOST_SystemX11::createWindow(const char *title,
return window;
}
+#ifdef USE_EGL
+static GHOST_Context *create_egl_context(
+ GHOST_SystemX11 *system, Display *display, bool debug_context, int ver_major, int ver_minor)
+{
+ GHOST_Context *context;
+ context = new GHOST_ContextEGL(system,
+ false,
+ EGLNativeWindowType(nullptr),
+ EGLNativeDisplayType(display),
+ EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
+ ver_major,
+ ver_minor,
+ GHOST_OPENGL_EGL_CONTEXT_FLAGS |
+ (debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0),
+ GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
+ EGL_OPENGL_API);
+
+ if (context->initializeDrawingContext()) {
+ return context;
+ }
+ delete context;
+
+ return nullptr;
+}
+#endif
+
+static GHOST_Context *create_glx_context(Display *display,
+ bool debug_context,
+ int ver_major,
+ int ver_minor)
+{
+ GHOST_Context *context;
+ context = new GHOST_ContextGLX(false,
+ (Window) nullptr,
+ display,
+ (GLXFBConfig) nullptr,
+ GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
+ ver_major,
+ ver_minor,
+ GHOST_OPENGL_GLX_CONTEXT_FLAGS |
+ (debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
+ GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY);
+
+ if (context->initializeDrawingContext()) {
+ return context;
+ }
+ delete context;
+
+ return nullptr;
+}
/**
* Create a new off-screen context.
* Never explicitly delete the context, use #disposeContext() instead.
@@ -393,98 +434,33 @@ GHOST_IContext *GHOST_SystemX11::createOffscreenContext(GHOST_GLSettings glSetti
const bool debug_context = (glSettings.flags & GHOST_glDebugContext) != 0;
-#if defined(WITH_GL_PROFILE_CORE)
- {
- const char *version_major = (char *)glewGetString(GLEW_VERSION_MAJOR);
- if (version_major != nullptr && version_major[0] == '1') {
- fprintf(stderr, "Error: GLEW version 2.0 and above is required.\n");
- abort();
- }
- }
-#endif
-
- const int profile_mask =
-#ifdef WITH_GL_EGL
-# if defined(WITH_GL_PROFILE_CORE)
- EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT;
-# elif defined(WITH_GL_PROFILE_COMPAT)
- EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT;
-# else
-# error // must specify either core or compat at build time
-# endif
-#else
-# if defined(WITH_GL_PROFILE_CORE)
- GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
-# elif defined(WITH_GL_PROFILE_COMPAT)
- GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
-# else
-# error // must specify either core or compat at build time
-# endif
-#endif
-
GHOST_Context *context;
+#ifdef USE_EGL
+ /* Try to initialize an EGL context. */
for (int minor = 5; minor >= 0; --minor) {
-#if defined(WITH_GL_EGL)
- context = new GHOST_ContextEGL(this,
- false,
- EGLNativeWindowType(nullptr),
- EGLNativeDisplayType(m_display),
- profile_mask,
- 4,
- minor,
- GHOST_OPENGL_EGL_CONTEXT_FLAGS |
- (debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0),
- GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
- EGL_OPENGL_API);
-#else
- context = new GHOST_ContextGLX(false,
- (Window) nullptr,
- m_display,
- (GLXFBConfig) nullptr,
- profile_mask,
- 4,
- minor,
- GHOST_OPENGL_GLX_CONTEXT_FLAGS |
- (debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
- GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY);
-#endif
-
- if (context->initializeDrawingContext()) {
+ context = create_egl_context(this, m_display, debug_context, 4, minor);
+ if (context != nullptr) {
return context;
}
- delete context;
+ }
+ context = create_egl_context(this, m_display, debug_context, 3, 3);
+ if (context != nullptr) {
+ return context;
}
-#if defined(WITH_GL_EGL)
- context = new GHOST_ContextEGL(this,
- false,
- EGLNativeWindowType(nullptr),
- EGLNativeDisplayType(m_display),
- profile_mask,
- 3,
- 3,
- GHOST_OPENGL_EGL_CONTEXT_FLAGS |
- (debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0),
- GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
- EGL_OPENGL_API);
-#else
- context = new GHOST_ContextGLX(false,
- (Window) nullptr,
- m_display,
- (GLXFBConfig) nullptr,
- profile_mask,
- 3,
- 3,
- GHOST_OPENGL_GLX_CONTEXT_FLAGS |
- (debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
- GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY);
+ /* EGL initialization failed, try to fallback to a GLX context. */
#endif
-
- if (context->initializeDrawingContext()) {
+ for (int minor = 5; minor >= 0; --minor) {
+ context = create_glx_context(m_display, debug_context, 4, minor);
+ if (context != nullptr) {
+ return context;
+ }
+ }
+ context = create_glx_context(m_display, debug_context, 3, 3);
+ if (context != nullptr) {
return context;
}
- delete context;
return nullptr;
}
@@ -513,8 +489,9 @@ static void destroyIMCallback(XIM /*xim*/, XPointer ptr, XPointer /*data*/)
bool GHOST_SystemX11::openX11_IM()
{
- if (!m_display)
+ if (!m_display) {
return false;
+ }
/* set locale modifiers such as `@im=ibus` specified by XMODIFIERS. */
XSetLocaleModifiers("");
@@ -584,7 +561,7 @@ struct init_timestamp_data {
Time timestamp;
};
-static Bool init_timestamp_scanner(Display *, XEvent *event, XPointer arg)
+static Bool init_timestamp_scanner(Display * /*display*/, XEvent *event, XPointer arg)
{
init_timestamp_data *data = reinterpret_cast<init_timestamp_data *>(arg);
switch (event->type) {
@@ -663,7 +640,7 @@ bool GHOST_SystemX11::processEvents(bool waitForEvent)
/* open connection to XIM server and create input context (XIC)
* when receiving the first FocusIn or KeyPress event after startup,
* or recover XIM and XIC when the XIM server has been restarted */
- if (xevent.type == FocusIn || xevent.type == KeyPress) {
+ if (ELEM(xevent.type, FocusIn, KeyPress)) {
if (!m_xim && openX11_IM()) {
GHOST_PRINT("Connected to XIM server\n");
}
@@ -672,11 +649,12 @@ bool GHOST_SystemX11::processEvents(bool waitForEvent)
GHOST_WindowX11 *window = findGhostWindow(xevent.xany.window);
if (window && !window->getX11_XIC() && window->createX11_XIC()) {
GHOST_PRINT("XIM input context created\n");
- if (xevent.type == KeyPress)
+ if (xevent.type == KeyPress) {
/* we can assume the window has input focus
* here, because key events are received only
* when the window is focused. */
XSetICFocus(window->getX11_XIC());
+ }
}
}
}
@@ -724,9 +702,9 @@ bool GHOST_SystemX11::processEvents(bool waitForEvent)
* in order to confirm the window is active. */
XPeekEvent(m_display, &xev_next);
- if (xev_next.type == KeyPress || xev_next.type == KeyRelease) {
- /* XK_Hyper_L/R currently unused */
- const static KeySym modifiers[8] = {
+ if (ELEM(xev_next.type, KeyPress, KeyRelease)) {
+ /* XK_Hyper_L/R currently unused. */
+ const static KeySym modifiers[] = {
XK_Shift_L,
XK_Shift_R,
XK_Control_L,
@@ -737,16 +715,15 @@ bool GHOST_SystemX11::processEvents(bool waitForEvent)
XK_Super_R,
};
- for (int i = 0; i < (int)(sizeof(modifiers) / sizeof(*modifiers)); i++) {
+ for (int i = 0; i < (int)ARRAY_SIZE(modifiers); i++) {
KeyCode kc = XKeysymToKeycode(m_display, modifiers[i]);
if (kc != 0 && ((xevent.xkeymap.key_vector[kc >> 3] >> (kc & 7)) & 1) != 0) {
pushEvent(new GHOST_EventKey(getMilliSeconds(),
GHOST_kEventKeyDown,
window,
ghost_key_from_keysym(modifiers[i]),
- '\0',
- nullptr,
- false));
+ false,
+ nullptr));
}
}
}
@@ -822,7 +799,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
/* Detect auto-repeat. */
bool is_repeat = false;
- if (xe->type == KeyPress || xe->type == KeyRelease) {
+ if (ELEM(xe->type, KeyPress, KeyRelease)) {
XKeyEvent *xke = &(xe->xkey);
/* Set to true if this key will repeat. */
@@ -889,9 +866,11 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
if (xe->type == xi_presence) {
XDevicePresenceNotifyEvent *notify_event = (XDevicePresenceNotifyEvent *)xe;
- if ((notify_event->devchange == DeviceEnabled) ||
- (notify_event->devchange == DeviceDisabled) ||
- (notify_event->devchange == DeviceAdded) || (notify_event->devchange == DeviceRemoved)) {
+ if (ELEM(notify_event->devchange,
+ DeviceEnabled,
+ DeviceDisabled,
+ DeviceAdded,
+ DeviceRemoved)) {
refreshXInputDevices();
/* update all window events */
@@ -1014,7 +993,9 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
case KeyRelease: {
XKeyEvent *xke = &(xe->xkey);
KeySym key_sym;
+ char *utf8_buf = nullptr;
char ascii;
+
#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
/* utf8_array[] is initial buffer used for Xutf8LookupString().
* if the length of the utf8 string exceeds this array, allocate
@@ -1023,12 +1004,10 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
* at the end of this buffer when the constructor of GHOST_EventKey
* reads 6 bytes regardless of the effective data length. */
char utf8_array[16 * 6 + 5]; /* 16 utf8 characters */
- char *utf8_buf = utf8_array;
- int len = 1; /* at least one null character will be stored */
+ int len = 1; /* at least one null character will be stored */
#else
- char *utf8_buf = nullptr;
+ char utf8_array[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'};
#endif
-
GHOST_TEventType type = (xke->type == KeyPress) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp;
GHOST_TKey gkey;
@@ -1148,81 +1127,94 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
#endif
#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
- /* Setting unicode on key-up events gives #XLookupNone status. */
- XIC xic = window->getX11_XIC();
- if (xic && xke->type == KeyPress) {
- Status status;
+ /* Only used for key-press. */
+ XIC xic = nullptr;
+#endif
- /* Use utf8 because its not locale repentant, from XORG docs. */
- if (!(len = Xutf8LookupString(
- xic, xke, utf8_buf, sizeof(utf8_array) - 5, &key_sym, &status))) {
- utf8_buf[0] = '\0';
- }
+ if (xke->type == KeyPress) {
+ utf8_buf = utf8_array;
+#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
+ /* Setting unicode on key-up events gives #XLookupNone status. */
+ xic = window->getX11_XIC();
+ if (xic) {
+ Status status;
+
+ /* Use utf8 because its not locale repentant, from XORG docs. */
+ if (!(len = Xutf8LookupString(
+ xic, xke, utf8_buf, sizeof(utf8_array) - 5, &key_sym, &status))) {
+ utf8_buf[0] = '\0';
+ }
- if (status == XBufferOverflow) {
- utf8_buf = (char *)malloc(len + 5);
- len = Xutf8LookupString(xic, xke, utf8_buf, len, &key_sym, &status);
- }
+ if (status == XBufferOverflow) {
+ utf8_buf = (char *)malloc(len + 5);
+ len = Xutf8LookupString(xic, xke, utf8_buf, len, &key_sym, &status);
+ }
- if ((status == XLookupChars || status == XLookupBoth)) {
- if ((unsigned char)utf8_buf[0] >= 32) { /* not an ascii control character */
- /* do nothing for now, this is valid utf8 */
+ if (ELEM(status, XLookupChars, XLookupBoth)) {
+ if ((unsigned char)utf8_buf[0] >= 32) { /* not an ascii control character */
+ /* do nothing for now, this is valid utf8 */
+ }
+ else {
+ utf8_buf[0] = '\0';
+ }
+ }
+ else if (status == XLookupKeySym) {
+ /* this key doesn't have a text representation, it is a command
+ * key of some sort */
}
else {
- utf8_buf[0] = '\0';
+ printf("Bad keycode lookup. Keysym 0x%x Status: %s\n",
+ (unsigned int)key_sym,
+ (status == XLookupNone ? "XLookupNone" :
+ status == XLookupKeySym ? "XLookupKeySym" :
+ "Unknown status"));
+
+ printf("'%.*s' %p %p\n", len, utf8_buf, xic, m_xim);
}
}
- else if (status == XLookupKeySym) {
- /* this key doesn't have a text representation, it is a command
- * key of some sort */
- }
else {
- printf("Bad keycode lookup. Keysym 0x%x Status: %s\n",
- (unsigned int)key_sym,
- (status == XLookupNone ? "XLookupNone" :
- status == XLookupKeySym ? "XLookupKeySym" :
- "Unknown status"));
-
- printf("'%.*s' %p %p\n", len, utf8_buf, xic, m_xim);
+ utf8_buf[0] = '\0';
}
- }
- else {
- utf8_buf[0] = '\0';
- }
#endif
+ if (!utf8_buf[0] && ascii) {
+ utf8_buf[0] = ascii;
+ utf8_buf[1] = '\0';
+ }
+ }
- g_event = new GHOST_EventKey(
- getMilliSeconds(), type, window, gkey, ascii, utf8_buf, is_repeat);
+ g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, is_repeat, utf8_buf);
#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
/* when using IM for some languages such as Japanese,
* one event inserts multiple utf8 characters */
- if (xic && xke->type == KeyPress) {
+ if (xke->type == KeyPress && xic) {
unsigned char c;
int i = 0;
- while (1) {
- /* search character boundary */
- if ((unsigned char)utf8_buf[i++] > 0x7f) {
+ while (true) {
+ /* Search character boundary. */
+ if ((uchar)utf8_buf[i++] > 0x7f) {
for (; i < len; ++i) {
c = utf8_buf[i];
- if (c < 0x80 || c > 0xbf)
+ if (c < 0x80 || c > 0xbf) {
break;
+ }
}
}
- if (i >= len)
+ if (i >= len) {
break;
-
- /* enqueue previous character */
+ }
+ /* Enqueue previous character. */
pushEvent(g_event);
g_event = new GHOST_EventKey(
- getMilliSeconds(), type, window, gkey, '\0', &utf8_buf[i], is_repeat);
+ getMilliSeconds(), type, window, gkey, is_repeat, &utf8_buf[i]);
}
}
- if (utf8_buf != utf8_array)
+ if (utf8_buf != utf8_array) {
free(utf8_buf);
+ }
#endif
break;
@@ -1231,7 +1223,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
case ButtonPress:
case ButtonRelease: {
XButtonEvent &xbe = xe->xbutton;
- GHOST_TButtonMask gbmask = GHOST_kButtonMaskLeft;
+ GHOST_TButton gbmask = GHOST_kButtonMaskLeft;
GHOST_TEventType type = (xbe.type == ButtonPress) ? GHOST_kEventButtonDown :
GHOST_kEventButtonUp;
@@ -1306,10 +1298,12 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
XIC xic = window->getX11_XIC();
if (xic) {
- if (xe->type == FocusIn)
+ if (xe->type == FocusIn) {
XSetICFocus(xic);
- else
+ }
+ else {
XUnsetICFocus(xic);
+ }
}
#endif
@@ -1449,8 +1443,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
nxe.xselection.time = xse->time;
/* Check to see if the requester is asking for String */
- if (xse->target == utf8_string || xse->target == string || xse->target == compound_text ||
- xse->target == c_string) {
+ if (ELEM(xse->target, utf8_string, string, compound_text, c_string)) {
if (xse->selection == XInternAtom(m_display, "PRIMARY", False)) {
XChangeProperty(m_display,
xse->requestor,
@@ -1503,7 +1496,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
default: {
#ifdef WITH_X11_XINPUT
for (GHOST_TabletX11 &xtablet : m_xtablets) {
- if (xe->type == xtablet.MotionEvent || xe->type == xtablet.PressEvent) {
+ if (ELEM(xe->type, xtablet.MotionEvent, xtablet.PressEvent)) {
XDeviceMotionEvent *data = (XDeviceMotionEvent *)xe;
if (data->deviceid != xtablet.ID) {
continue;
@@ -2367,10 +2360,11 @@ class DialogData {
/* Is the mouse inside the given button */
bool isInsideButton(XEvent &e, uint button_num)
{
- return ((e.xmotion.y > height - padding_y - button_height) &&
- (e.xmotion.y < height - padding_y) &&
- (e.xmotion.x > width - (padding_x + button_width) * button_num) &&
- (e.xmotion.x < width - padding_x - (padding_x + button_width) * (button_num - 1)));
+ return (
+ (e.xmotion.y > (int)(height - padding_y - button_height)) &&
+ (e.xmotion.y < (int)(height - padding_y)) &&
+ (e.xmotion.x > (int)(width - (padding_x + button_width) * button_num)) &&
+ (e.xmotion.x < (int)(width - padding_x - (padding_x + button_width) * (button_num - 1))));
}
};
@@ -2574,7 +2568,7 @@ int GHOST_X11_ApplicationIOErrorHandler(Display * /*display*/)
static bool is_filler_char(char c)
{
- return isspace(c) || c == '_' || c == '-' || c == ';' || c == ':';
+ return isspace(c) || ELEM(c, '_', '-', ';', ':');
}
/* These C functions are copied from Wine 3.12's `wintab.c` */
@@ -2674,7 +2668,7 @@ void GHOST_SystemX11::refreshXInputDevices()
XFree((void *)device_type);
}
- if (!(tablet_mode == GHOST_kTabletModeStylus || tablet_mode == GHOST_kTabletModeEraser)) {
+ if (!ELEM(tablet_mode, GHOST_kTabletModeStylus, GHOST_kTabletModeEraser)) {
continue;
}
diff --git a/intern/ghost/intern/GHOST_SystemX11.h b/intern/ghost/intern/GHOST_SystemX11.h
index 7938aa2b646..bd6ace101e9 100644
--- a/intern/ghost/intern/GHOST_SystemX11.h
+++ b/intern/ghost/intern/GHOST_SystemX11.h
@@ -253,7 +253,7 @@ class GHOST_SystemX11 : public GHOST_System {
/**
* \see GHOST_ISystem
*/
- int setConsoleWindowState(GHOST_TConsoleWindowState /*action*/)
+ bool setConsoleWindowState(GHOST_TConsoleWindowState /*action*/)
{
return 0;
}
diff --git a/intern/ghost/intern/GHOST_TimerManager.cpp b/intern/ghost/intern/GHOST_TimerManager.cpp
index 504cdbfb6c8..e54c2515029 100644
--- a/intern/ghost/intern/GHOST_TimerManager.cpp
+++ b/intern/ghost/intern/GHOST_TimerManager.cpp
@@ -71,10 +71,11 @@ uint64_t GHOST_TimerManager::nextFireTime()
TTimerVector::iterator iter;
for (iter = m_timers.begin(); iter != m_timers.end(); ++iter) {
- uint64_t next = (*iter)->getNext();
+ const uint64_t next = (*iter)->getNext();
- if (next < smallest)
+ if (next < smallest) {
smallest = next;
+ }
}
return smallest;
@@ -86,8 +87,9 @@ bool GHOST_TimerManager::fireTimers(uint64_t time)
bool anyProcessed = false;
for (iter = m_timers.begin(); iter != m_timers.end(); ++iter) {
- if (fireTimer(time, *iter))
+ if (fireTimer(time, *iter)) {
anyProcessed = true;
+ }
}
return anyProcessed;
@@ -113,9 +115,7 @@ bool GHOST_TimerManager::fireTimer(uint64_t time, GHOST_TimerTask *task)
return true;
}
- else {
- return false;
- }
+ return false;
}
void GHOST_TimerManager::disposeTimers()
diff --git a/intern/ghost/intern/GHOST_TimerManager.h b/intern/ghost/intern/GHOST_TimerManager.h
index 090a84d1f14..4458a107190 100644
--- a/intern/ghost/intern/GHOST_TimerManager.h
+++ b/intern/ghost/intern/GHOST_TimerManager.h
@@ -87,7 +87,7 @@ class GHOST_TimerManager {
*/
void disposeTimers();
- typedef std::vector<GHOST_TimerTask *> TTimerVector;
+ using TTimerVector = std::vector<GHOST_TimerTask *>;
/** The list with event consumers. */
TTimerVector m_timers;
diff --git a/intern/ghost/intern/GHOST_WaylandCursorSettings.h b/intern/ghost/intern/GHOST_WaylandCursorSettings.h
index 2491f6ca31f..f5649d20850 100644
--- a/intern/ghost/intern/GHOST_WaylandCursorSettings.h
+++ b/intern/ghost/intern/GHOST_WaylandCursorSettings.h
@@ -5,9 +5,13 @@
*/
#pragma once
-#include <dbus/dbus.h>
#include <string>
+#ifdef WITH_GHOST_WAYLAND_DBUS
+# include <dbus/dbus.h>
+#endif
+
+#ifdef WITH_GHOST_WAYLAND_DBUS
static DBusMessage *get_setting_sync(DBusConnection *const connection,
const char *key,
const char *value)
@@ -66,9 +70,11 @@ static bool parse_type(DBusMessage *const reply, const int type, void *value)
return true;
}
+#endif /* WITH_GHOST_WAYLAND_DBUS */
static bool get_cursor_settings(std::string &theme, int &size)
{
+#ifdef WITH_GHOST_WAYLAND_DBUS
static const char name[] = "org.gnome.desktop.interface";
static const char key_theme[] = "cursor-theme";
static const char key_size[] = "cursor-size";
@@ -113,4 +119,11 @@ static bool get_cursor_settings(std::string &theme, int &size)
dbus_message_unref(reply);
return true;
+#else
+ /* NOTE: eventually we could have alternative ways to access the theme,
+ * this uses the "default" theme which is functional (instead of a user-defined theme). */
+ (void)theme;
+ (void)size;
+ return false;
+#endif /* !WITH_GHOST_WAYLAND_DBUS */
}
diff --git a/intern/ghost/intern/GHOST_WaylandUtils.h b/intern/ghost/intern/GHOST_WaylandUtils.h
new file mode 100644
index 00000000000..0e1e133bc4c
--- /dev/null
+++ b/intern/ghost/intern/GHOST_WaylandUtils.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup GHOST
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+# undef wl_array_for_each
+/**
+ * This macro causes a warning for C++ code, define our own.
+ * See: https://gitlab.freedesktop.org/wayland/wayland/-/issues/34
+ */
+# define WL_ARRAY_FOR_EACH(pos, array) \
+ for (pos = (decltype(pos))((array)->data); \
+ (const char *)pos < ((const char *)(array)->data + (array)->size); \
+ (pos)++)
+#endif
diff --git a/intern/ghost/intern/GHOST_Window.cpp b/intern/ghost/intern/GHOST_Window.cpp
index de7c5422d3f..db4d6c3bb71 100644
--- a/intern/ghost/intern/GHOST_Window.cpp
+++ b/intern/ghost/intern/GHOST_Window.cpp
@@ -23,6 +23,7 @@ GHOST_Window::GHOST_Window(uint32_t width,
: m_drawingContextType(GHOST_kDrawingContextTypeNone),
m_cursorVisible(true),
m_cursorGrab(GHOST_kGrabDisable),
+ m_cursorGrabAxis(GHOST_kAxisNone),
m_cursorShape(GHOST_kStandardCursorDefault),
m_wantStereoVisual(wantStereoVisual),
m_context(new GHOST_ContextNone(false))
@@ -164,7 +165,8 @@ GHOST_TSuccess GHOST_Window::getCursorGrabBounds(GHOST_Rect &bounds)
void GHOST_Window::getCursorGrabState(GHOST_TGrabCursorMode &mode,
GHOST_TAxisFlag &wrap_axis,
- GHOST_Rect &bounds)
+ GHOST_Rect &bounds,
+ bool &use_software_cursor)
{
mode = m_cursorGrab;
if (m_cursorGrab == GHOST_kGrabWrap) {
@@ -176,8 +178,16 @@ void GHOST_Window::getCursorGrabState(GHOST_TGrabCursorMode &mode,
bounds.m_r = -1;
bounds.m_t = -1;
bounds.m_b = -1;
- wrap_axis = GHOST_kGrabAxisNone;
+ wrap_axis = GHOST_kAxisNone;
}
+ use_software_cursor = (m_cursorGrab != GHOST_kGrabDisable) ? getCursorGrabUseSoftwareDisplay() :
+ false;
+}
+
+bool GHOST_Window::getCursorGrabUseSoftwareDisplay()
+{
+ /* Sub-classes may override, by default don't use software cursor. */
+ return false;
}
GHOST_TSuccess GHOST_Window::setCursorShape(GHOST_TStandardCursor cursorShape)
@@ -199,6 +209,12 @@ GHOST_TSuccess GHOST_Window::setCustomCursorShape(
return GHOST_kFailure;
}
+GHOST_TSuccess GHOST_Window::getCursorBitmap(GHOST_CursorBitmapRef * /*bitmap*/)
+{
+ /* Sub-classes may override. */
+ return GHOST_kFailure;
+}
+
void GHOST_Window::setAcceptDragOperation(bool canAccept)
{
m_canAcceptDragOperation = canAccept;
diff --git a/intern/ghost/intern/GHOST_Window.h b/intern/ghost/intern/GHOST_Window.h
index adbc29eb84e..5ff91c05b16 100644
--- a/intern/ghost/intern/GHOST_Window.h
+++ b/intern/ghost/intern/GHOST_Window.h
@@ -117,6 +117,8 @@ class GHOST_Window : public GHOST_IWindow {
int hotY,
bool canInvertColor);
+ GHOST_TSuccess getCursorBitmap(GHOST_CursorBitmapRef *bitmap);
+
/**
* Returns the visibility state of the cursor.
* \return The visibility state of the cursor.
@@ -154,7 +156,12 @@ class GHOST_Window : public GHOST_IWindow {
void getCursorGrabState(GHOST_TGrabCursorMode &mode,
GHOST_TAxisFlag &axis_flag,
- GHOST_Rect &bounds);
+ GHOST_Rect &bounds,
+ bool &use_software_cursor);
+ /**
+ * Return true when a software cursor should be used.
+ */
+ bool getCursorGrabUseSoftwareDisplay();
/**
* Sets the progress bar value displayed in the window/application icon
diff --git a/intern/ghost/intern/GHOST_WindowManager.cpp b/intern/ghost/intern/GHOST_WindowManager.cpp
index 19684a44169..e8785cbdb24 100644
--- a/intern/ghost/intern/GHOST_WindowManager.cpp
+++ b/intern/ghost/intern/GHOST_WindowManager.cpp
@@ -162,17 +162,3 @@ GHOST_IWindow *GHOST_WindowManager::getWindowAssociatedWithOSWindow(void *osWind
}
return nullptr;
}
-
-bool GHOST_WindowManager::getAnyModifiedState()
-{
- bool isAnyModified = false;
- std::vector<GHOST_IWindow *>::iterator iter;
-
- for (iter = m_windows.begin(); iter != m_windows.end(); ++iter) {
- if ((*iter)->getModifiedState()) {
- isAnyModified = true;
- }
- }
-
- return isAnyModified;
-}
diff --git a/intern/ghost/intern/GHOST_WindowManager.h b/intern/ghost/intern/GHOST_WindowManager.h
index 9d20413c433..bf7a0f4ec61 100644
--- a/intern/ghost/intern/GHOST_WindowManager.h
+++ b/intern/ghost/intern/GHOST_WindowManager.h
@@ -109,12 +109,6 @@ class GHOST_WindowManager {
*/
GHOST_IWindow *getWindowAssociatedWithOSWindow(void *osWindow);
- /**
- * Return true if any windows has a modified status
- * \return True if any window has unsaved changes
- */
- bool getAnyModifiedState();
-
protected:
/** The list of windows managed */
std::vector<GHOST_IWindow *> m_windows;
diff --git a/intern/ghost/intern/GHOST_WindowNULL.h b/intern/ghost/intern/GHOST_WindowNULL.h
index 01b50251d69..f9c0a593d5f 100644
--- a/intern/ghost/intern/GHOST_WindowNULL.h
+++ b/intern/ghost/intern/GHOST_WindowNULL.h
@@ -11,32 +11,31 @@
#include <map>
-class GHOST_SystemNULL;
+class GHOST_SystemHeadless;
class GHOST_WindowNULL : public GHOST_Window {
public:
- GHOST_TSuccess hasCursorShape(GHOST_TStandardCursor)
+ GHOST_TSuccess hasCursorShape(GHOST_TStandardCursor /*cursorShape*/) override
{
return GHOST_kSuccess;
}
- GHOST_WindowNULL(GHOST_SystemNULL *system,
- const char *title,
- int32_t left,
- int32_t top,
+ GHOST_WindowNULL(const char *title,
+ int32_t /*left*/,
+ int32_t /*top*/,
uint32_t width,
uint32_t height,
GHOST_TWindowState state,
- const GHOST_IWindow *parentWindow,
- GHOST_TDrawingContextType type,
+ const GHOST_IWindow * /*parentWindow*/,
+ GHOST_TDrawingContextType /*type*/,
const bool stereoVisual)
- : GHOST_Window(width, height, state, stereoVisual, false), m_system(system)
+ : GHOST_Window(width, height, state, stereoVisual, false)
{
setTitle(title);
}
protected:
- GHOST_TSuccess installDrawingContext(GHOST_TDrawingContextType type)
+ GHOST_TSuccess installDrawingContext(GHOST_TDrawingContextType /*type*/)
{
return GHOST_kSuccess;
}
@@ -44,114 +43,111 @@ class GHOST_WindowNULL : public GHOST_Window {
{
return GHOST_kSuccess;
}
- GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode)
+ GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode /*mode*/) override
{
return GHOST_kSuccess;
}
- GHOST_TSuccess setWindowCursorShape(GHOST_TStandardCursor shape)
+ GHOST_TSuccess setWindowCursorShape(GHOST_TStandardCursor /*shape*/) override
{
return GHOST_kSuccess;
}
- GHOST_TSuccess setWindowCustomCursorShape(uint8_t *bitmap,
- uint8_t *mask,
- int sizex,
- int sizey,
- int hotX,
- int hotY,
- bool canInvertColor)
+ GHOST_TSuccess setWindowCustomCursorShape(uint8_t * /*bitmap*/,
+ uint8_t * /*mask*/,
+ int /*sizex*/,
+ int /*sizey*/,
+ int /*hotX*/,
+ int /*hotY*/,
+ bool /*canInvertColor*/) override
{
return GHOST_kSuccess;
}
- bool getValid() const
+ bool getValid() const override
{
return true;
}
- void setTitle(const char *title)
+ void setTitle(const char * /*title*/) override
{ /* nothing */
}
- std::string getTitle() const
+ std::string getTitle() const override
{
return "untitled";
}
- void getWindowBounds(GHOST_Rect &bounds) const
+ void getWindowBounds(GHOST_Rect &bounds) const override
{
getClientBounds(bounds);
}
- void getClientBounds(GHOST_Rect &bounds) const
+ void getClientBounds(GHOST_Rect & /*bounds*/) const override
{ /* nothing */
}
- GHOST_TSuccess setClientWidth(uint32_t width)
+ GHOST_TSuccess setClientWidth(uint32_t /*width*/) override
{
return GHOST_kFailure;
}
- GHOST_TSuccess setClientHeight(uint32_t height)
+ GHOST_TSuccess setClientHeight(uint32_t /*height*/) override
{
return GHOST_kFailure;
}
- GHOST_TSuccess setClientSize(uint32_t width, uint32_t height)
+ GHOST_TSuccess setClientSize(uint32_t /*width*/, uint32_t /*height*/) override
{
return GHOST_kFailure;
}
- void screenToClient(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const
+ void screenToClient(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const override
{
outX = inX;
outY = inY;
}
- void clientToScreen(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const
+ void clientToScreen(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const override
{
outX = inX;
outY = inY;
}
- GHOST_TSuccess swapBuffers()
+ GHOST_TSuccess swapBuffers() override
{
return GHOST_kFailure;
}
- GHOST_TSuccess activateDrawingContext()
+ GHOST_TSuccess activateDrawingContext() override
{
return GHOST_kFailure;
}
- ~GHOST_WindowNULL()
- { /* nothing */
- }
- GHOST_TSuccess setWindowCursorVisibility(bool visible)
+ ~GHOST_WindowNULL() override = default;
+
+ GHOST_TSuccess setWindowCursorVisibility(bool /*visible*/) override
{
return GHOST_kSuccess;
}
- GHOST_TSuccess setState(GHOST_TWindowState state)
+ GHOST_TSuccess setState(GHOST_TWindowState /*state*/) override
{
return GHOST_kSuccess;
}
- GHOST_TWindowState getState() const
+ GHOST_TWindowState getState() const override
{
return GHOST_kWindowStateNormal;
}
- GHOST_TSuccess invalidate()
+ GHOST_TSuccess invalidate() override
{
return GHOST_kSuccess;
}
- GHOST_TSuccess setOrder(GHOST_TWindowOrder order)
+ GHOST_TSuccess setOrder(GHOST_TWindowOrder /*order*/) override
{
return GHOST_kSuccess;
}
- GHOST_TSuccess beginFullScreen() const
+ GHOST_TSuccess beginFullScreen() const override
{
return GHOST_kSuccess;
}
- GHOST_TSuccess endFullScreen() const
+ GHOST_TSuccess endFullScreen() const override
{
return GHOST_kSuccess;
}
private:
- GHOST_SystemNULL *m_system;
-
/**
* \param type: The type of rendering context create.
* \return Indication of success.
*/
- GHOST_Context *newDrawingContext(GHOST_TDrawingContextType type)
+ GHOST_Context *newDrawingContext(GHOST_TDrawingContextType /*type*/) override
{
return nullptr;
}
diff --git a/intern/ghost/intern/GHOST_WindowSDL.cpp b/intern/ghost/intern/GHOST_WindowSDL.cpp
index 09192d989e4..59dc80cf7e6 100644
--- a/intern/ghost/intern/GHOST_WindowSDL.cpp
+++ b/intern/ghost/intern/GHOST_WindowSDL.cpp
@@ -6,7 +6,6 @@
#include "GHOST_WindowSDL.h"
#include "SDL_mouse.h"
-#include "glew-mx.h"
#include "GHOST_ContextSDL.h"
diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp
index 21e3793d3b1..5f3cb3e3f3a 100644
--- a/intern/ghost/intern/GHOST_WindowWayland.cpp
+++ b/intern/ghost/intern/GHOST_WindowWayland.cpp
@@ -6,20 +6,39 @@
#include "GHOST_WindowWayland.h"
#include "GHOST_SystemWayland.h"
+#include "GHOST_WaylandUtils.h"
#include "GHOST_WindowManager.h"
+#include "GHOST_utildefines.h"
#include "GHOST_Event.h"
#include "GHOST_ContextEGL.h"
#include "GHOST_ContextNone.h"
+#include <wayland-client-protocol.h>
+
+#ifdef WITH_GHOST_WAYLAND_DYNLOAD
+# include <wayland_dynload_egl.h>
+#endif
#include <wayland-egl.h>
#include <algorithm> /* For `std::find`. */
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+# ifdef WITH_GHOST_WAYLAND_DYNLOAD
+# include <wayland_dynload_libdecor.h>
+# endif
+# include <libdecor.h>
+#endif
+
+/* Logging, use `ghost.wl.*` prefix. */
+#include "CLG_log.h"
+
static constexpr size_t base_dpi = 96;
-struct window_t {
+static GHOST_WindowManager *window_manager = nullptr;
+
+struct GWL_Window {
GHOST_WindowWayland *w = nullptr;
struct wl_surface *wl_surface = nullptr;
/**
@@ -28,7 +47,7 @@ struct window_t {
* This is an ordered set (whoever adds to this is responsible for keeping members unique).
* In practice this is rarely manipulated and is limited by the number of physical displays.
*/
- std::vector<output_t *> outputs;
+ std::vector<GWL_Output *> outputs;
/** The scale value written to #wl_surface_set_buffer_scale. */
int scale = 0;
@@ -40,10 +59,17 @@ struct window_t {
*/
uint32_t dpi = 0;
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+ struct libdecor_frame *decor_frame = nullptr;
+ bool decor_configured = false;
+#else
struct xdg_surface *xdg_surface = nullptr;
- struct xdg_toplevel *xdg_toplevel = nullptr;
struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration = nullptr;
+ struct xdg_toplevel *xdg_toplevel = nullptr;
+
enum zxdg_toplevel_decoration_v1_mode decoration_mode = (enum zxdg_toplevel_decoration_v1_mode)0;
+#endif
+
wl_egl_window *egl_window = nullptr;
bool is_maximised = false;
bool is_fullscreen = false;
@@ -61,7 +87,7 @@ struct window_t {
/**
* Return -1 if `output_a` has a scale smaller than `output_b`, 0 when there equal, otherwise 1.
*/
-static int output_scale_cmp(const output_t *output_a, const output_t *output_b)
+static int output_scale_cmp(const GWL_Output *output_a, const GWL_Output *output_b)
{
if (output_a->scale < output_b->scale) {
return -1;
@@ -86,12 +112,12 @@ static int output_scale_cmp(const output_t *output_a, const output_t *output_b)
return 0;
}
-static int outputs_max_scale_or_default(const std::vector<output_t *> &outputs,
+static int outputs_max_scale_or_default(const std::vector<GWL_Output *> &outputs,
const int32_t scale_default,
uint32_t *r_dpi)
{
- const output_t *output_max = nullptr;
- for (const output_t *reg_output : outputs) {
+ const GWL_Output *output_max = nullptr;
+ for (const GWL_Output *reg_output : outputs) {
if (!output_max || (output_scale_cmp(output_max, reg_output) == -1)) {
output_max = reg_output;
}
@@ -120,10 +146,21 @@ static int outputs_max_scale_or_default(const std::vector<output_t *> &outputs,
/** \name Listener (XDG Top Level), #xdg_toplevel_listener
* \{ */
-static void xdg_toplevel_handle_configure(
- void *data, xdg_toplevel * /*xdg_toplevel*/, int32_t width, int32_t height, wl_array *states)
+#ifndef WITH_GHOST_WAYLAND_LIBDECOR
+
+static CLG_LogRef LOG_WL_XDG_TOPLEVEL = {"ghost.wl.handle.xdg_toplevel"};
+# define LOG (&LOG_WL_XDG_TOPLEVEL)
+
+static void xdg_toplevel_handle_configure(void *data,
+ xdg_toplevel * /*xdg_toplevel*/,
+ const int32_t width,
+ const int32_t height,
+ wl_array *states)
{
- window_t *win = static_cast<window_t *>(data);
+ /* TODO: log `states`, not urgent. */
+ CLOG_INFO(LOG, 2, "configure (size=[%d, %d])", width, height);
+
+ GWL_Window *win = static_cast<GWL_Window *>(data);
win->size_pending[0] = win->scale * width;
win->size_pending[1] = win->scale * height;
@@ -131,12 +168,8 @@ static void xdg_toplevel_handle_configure(
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++) {
+ enum xdg_toplevel_state *state;
+ WL_ARRAY_FOR_EACH (state, states) {
switch (*state) {
case XDG_TOPLEVEL_STATE_MAXIMIZED:
win->is_maximised = true;
@@ -155,7 +188,8 @@ static void xdg_toplevel_handle_configure(
static void xdg_toplevel_handle_close(void *data, xdg_toplevel * /*xdg_toplevel*/)
{
- static_cast<window_t *>(data)->w->close();
+ CLOG_INFO(LOG, 2, "close");
+ static_cast<GWL_Window *>(data)->w->close();
}
static const xdg_toplevel_listener toplevel_listener = {
@@ -163,42 +197,144 @@ static const xdg_toplevel_listener toplevel_listener = {
xdg_toplevel_handle_close,
};
+# undef LOG
+
+#endif /* !WITH_GHOST_WAYLAND_LIBDECOR. */
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (LibDecor Frame), #libdecor_frame_interface
+ * \{ */
+
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+
+static CLG_LogRef LOG_WL_LIBDECOR_FRAME = {"ghost.wl.handle.libdecor_frame"};
+# define LOG (&LOG_WL_LIBDECOR_FRAME)
+
+static void frame_handle_configure(struct libdecor_frame *frame,
+ struct libdecor_configuration *configuration,
+ void *data)
+{
+ CLOG_INFO(LOG, 2, "configure");
+
+ GWL_Window *win = static_cast<GWL_Window *>(data);
+
+ int size_next[2];
+ enum libdecor_window_state window_state;
+ struct libdecor_state *state;
+
+ if (!libdecor_configuration_get_content_size(
+ configuration, frame, &size_next[0], &size_next[1])) {
+ size_next[0] = win->size[0] / win->scale;
+ size_next[1] = win->size[1] / win->scale;
+ }
+
+ win->size[0] = win->scale * size_next[0];
+ win->size[1] = win->scale * size_next[1];
+
+ wl_egl_window_resize(win->egl_window, UNPACK2(win->size), 0, 0);
+ win->w->notify_size();
+
+ if (!libdecor_configuration_get_window_state(configuration, &window_state)) {
+ window_state = LIBDECOR_WINDOW_STATE_NONE;
+ }
+
+ win->is_maximised = window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED;
+ win->is_fullscreen = window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN;
+ win->is_active = window_state & LIBDECOR_WINDOW_STATE_ACTIVE;
+
+ win->is_active ? win->w->activate() : win->w->deactivate();
+
+ state = libdecor_state_new(UNPACK2(size_next));
+ libdecor_frame_commit(frame, state, configuration);
+ libdecor_state_free(state);
+
+ win->decor_configured = true;
+}
+
+static void frame_handle_close(struct libdecor_frame * /*frame*/, void *data)
+{
+ CLOG_INFO(LOG, 2, "close");
+
+ static_cast<GWL_Window *>(data)->w->close();
+}
+
+static void frame_handle_commit(struct libdecor_frame * /*frame*/, void *data)
+{
+ CLOG_INFO(LOG, 2, "commit");
+
+ /* We have to swap twice to keep any pop-up menus alive. */
+ static_cast<GWL_Window *>(data)->w->swapBuffers();
+ static_cast<GWL_Window *>(data)->w->swapBuffers();
+}
+
+static struct libdecor_frame_interface libdecor_frame_iface = {
+ frame_handle_configure,
+ frame_handle_close,
+ frame_handle_commit,
+};
+
+# undef LOG
+
+#endif /* WITH_GHOST_WAYLAND_LIBDECOR. */
+
/** \} */
/* -------------------------------------------------------------------- */
/** \name Listener (XDG Decoration Listener), #zxdg_toplevel_decoration_v1_listener
* \{ */
+#ifndef WITH_GHOST_WAYLAND_LIBDECOR
+
+static CLG_LogRef LOG_WL_XDG_TOPLEVEL_DECORATION = {"ghost.wl.handle.xdg_toplevel_decoration"};
+# define LOG (&LOG_WL_XDG_TOPLEVEL_DECORATION)
+
static void xdg_toplevel_decoration_handle_configure(
void *data,
struct zxdg_toplevel_decoration_v1 * /*zxdg_toplevel_decoration_v1*/,
- uint32_t mode)
+ const uint32_t mode)
{
- static_cast<window_t *>(data)->decoration_mode = zxdg_toplevel_decoration_v1_mode(mode);
+ CLOG_INFO(LOG, 2, "configure (mode=%u)", mode);
+ static_cast<GWL_Window *>(data)->decoration_mode = (zxdg_toplevel_decoration_v1_mode)mode;
}
static const zxdg_toplevel_decoration_v1_listener toplevel_decoration_v1_listener = {
xdg_toplevel_decoration_handle_configure,
};
+# undef LOG
+
+#endif /* !WITH_GHOST_WAYLAND_LIBDECOR. */
+
/** \} */
/* -------------------------------------------------------------------- */
/** \name Listener (XDG Surface Handle Configure), #xdg_surface_listener
* \{ */
-static void xdg_surface_handle_configure(void *data, xdg_surface *xdg_surface, uint32_t serial)
+#ifndef WITH_GHOST_WAYLAND_LIBDECOR
+
+static CLG_LogRef LOG_WL_XDG_SURFACE = {"ghost.wl.handle.xdg_surface"};
+# define LOG (&LOG_WL_XDG_SURFACE)
+
+static void xdg_surface_handle_configure(void *data,
+ xdg_surface *xdg_surface,
+ const uint32_t serial)
{
- window_t *win = static_cast<window_t *>(data);
+ GWL_Window *win = static_cast<GWL_Window *>(data);
if (win->xdg_surface != xdg_surface) {
+ CLOG_INFO(LOG, 2, "configure (skipped)");
return;
}
+ const bool do_resize = win->size_pending[0] != 0 && win->size_pending[1] != 0;
+ CLOG_INFO(LOG, 2, "configure (do_resize=%d)", do_resize);
- if (win->size_pending[0] != 0 && win->size_pending[1] != 0) {
+ if (do_resize) {
win->size[0] = win->size_pending[0];
win->size[1] = win->size_pending[1];
- wl_egl_window_resize(win->egl_window, win->size[0], win->size[1], 0, 0);
+ wl_egl_window_resize(win->egl_window, UNPACK2(win->size), 0, 0);
win->size_pending[0] = 0;
win->size_pending[1] = 0;
win->w->notify_size();
@@ -218,53 +354,66 @@ static const xdg_surface_listener xdg_surface_listener = {
xdg_surface_handle_configure,
};
+# undef LOG
+
+#endif /* !WITH_GHOST_WAYLAND_LIBDECOR. */
+
/** \} */
/* -------------------------------------------------------------------- */
/** \name Listener (Surface), #wl_surface_listener
* \{ */
+static CLG_LogRef LOG_WL_SURFACE = {"ghost.wl.handle.surface"};
+#define LOG (&LOG_WL_SURFACE)
+
static void surface_handle_enter(void *data,
struct wl_surface * /*wl_surface*/,
- struct wl_output *output)
+ struct wl_output *wl_output)
{
- GHOST_WindowWayland *w = static_cast<GHOST_WindowWayland *>(data);
- output_t *reg_output = w->output_find_by_wl(output);
- if (reg_output == nullptr) {
+ if (!ghost_wl_output_own(wl_output)) {
+ CLOG_INFO(LOG, 2, "enter (skipped)");
return;
}
+ CLOG_INFO(LOG, 2, "enter");
- if (w->outputs_enter(reg_output)) {
- w->outputs_changed_update_scale();
+ GWL_Output *reg_output = ghost_wl_output_user_data(wl_output);
+ GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(data);
+ if (win->outputs_enter(reg_output)) {
+ win->outputs_changed_update_scale();
}
}
static void surface_handle_leave(void *data,
struct wl_surface * /*wl_surface*/,
- struct wl_output *output)
+ struct wl_output *wl_output)
{
- GHOST_WindowWayland *w = static_cast<GHOST_WindowWayland *>(data);
- output_t *reg_output = w->output_find_by_wl(output);
- if (reg_output == nullptr) {
+ if (!ghost_wl_output_own(wl_output)) {
+ CLOG_INFO(LOG, 2, "leave (skipped)");
return;
}
+ CLOG_INFO(LOG, 2, "leave");
- if (w->outputs_leave(reg_output)) {
- w->outputs_changed_update_scale();
+ GWL_Output *reg_output = ghost_wl_output_user_data(wl_output);
+ GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(data);
+ if (win->outputs_leave(reg_output)) {
+ win->outputs_changed_update_scale();
}
}
-struct wl_surface_listener wl_surface_listener = {
+static struct wl_surface_listener wl_surface_listener = {
surface_handle_enter,
surface_handle_leave,
};
+#undef LOG
+
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Ghost Implementation
+/** \name GHOST Implementation
*
- * Wayland specific implementation of the GHOST_Window interface.
+ * WAYLAND specific implementation of the #GHOST_Window interface.
* \{ */
GHOST_TSuccess GHOST_WindowWayland::hasCursorShape(GHOST_TStandardCursor cursorShape)
@@ -274,20 +423,25 @@ GHOST_TSuccess GHOST_WindowWayland::hasCursorShape(GHOST_TStandardCursor cursorS
GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
const char *title,
- int32_t /*left*/,
- int32_t /*top*/,
- uint32_t width,
- uint32_t height,
- GHOST_TWindowState state,
+ const int32_t /*left*/,
+ const int32_t /*top*/,
+ const uint32_t width,
+ const uint32_t height,
+ const GHOST_TWindowState state,
const GHOST_IWindow *parentWindow,
- GHOST_TDrawingContextType type,
+ const 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(new GWL_Window)
{
+ /* Globally store pointer to window manager. */
+ if (!window_manager) {
+ window_manager = m_system->getWindowManager();
+ }
+
w->w = this;
w->size[0] = int32_t(width);
@@ -308,20 +462,37 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
/* Window surfaces. */
w->wl_surface = wl_compositor_create_surface(m_system->compositor());
- wl_surface_set_buffer_scale(this->surface(), w->scale);
+ ghost_wl_surface_tag(w->wl_surface);
+
+ wl_surface_set_buffer_scale(w->wl_surface, w->scale);
wl_surface_add_listener(w->wl_surface, &wl_surface_listener, this);
w->egl_window = wl_egl_window_create(w->wl_surface, int(w->size[0]), int(w->size[1]));
- w->xdg_surface = xdg_wm_base_get_xdg_surface(m_system->xdg_shell(), w->wl_surface);
- w->xdg_toplevel = xdg_surface_get_toplevel(w->xdg_surface);
-
/* NOTE: The limit is in points (not pixels) so Hi-DPI will limit to larger number of pixels.
* This has the advantage that the size limit is the same when moving the window between monitors
* with different scales set. If it was important to limit in pixels it could be re-calculated
* when the `w->scale` changed. */
- xdg_toplevel_set_min_size(w->xdg_toplevel, 320, 240);
+ const int32_t size_min[2] = {320, 240};
+
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+ /* create window decorations */
+ w->decor_frame = libdecor_decorate(
+ m_system->decor_context(), w->wl_surface, &libdecor_frame_iface, w);
+ libdecor_frame_map(w->decor_frame);
+
+ libdecor_frame_set_min_content_size(w->decor_frame, UNPACK2(size_min));
+
+ if (parentWindow) {
+ libdecor_frame_set_parent(
+ w->decor_frame, dynamic_cast<const GHOST_WindowWayland *>(parentWindow)->w->decor_frame);
+ }
+#else
+ w->xdg_surface = xdg_wm_base_get_xdg_surface(m_system->xdg_shell(), w->wl_surface);
+ w->xdg_toplevel = xdg_surface_get_toplevel(w->xdg_surface);
+
+ xdg_toplevel_set_min_size(w->xdg_toplevel, UNPACK2(size_min));
if (m_system->xdg_decoration_manager()) {
w->xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
@@ -332,8 +503,6 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
}
- wl_surface_set_user_data(w->wl_surface, this);
-
xdg_surface_add_listener(w->xdg_surface, &xdg_surface_listener, w);
xdg_toplevel_add_listener(w->xdg_toplevel, &toplevel_listener, w);
@@ -342,147 +511,63 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
w->xdg_toplevel, dynamic_cast<const GHOST_WindowWayland *>(parentWindow)->w->xdg_toplevel);
}
+#endif /* !WITH_GHOST_WAYLAND_LIBDECOR */
+
+ setTitle(title);
+
+ wl_surface_set_user_data(w->wl_surface, this);
+
/* Call top-level callbacks. */
wl_surface_commit(w->wl_surface);
wl_display_roundtrip(m_system->display());
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+ /* It's important not to return until the window is configured or
+ * calls to `setState` from Blender will crash `libdecor`. */
+ while (!w->decor_configured) {
+ if (libdecor_dispatch(m_system->decor_context(), 0) < 0) {
+ break;
+ }
+ }
+#endif
+
#ifdef GHOST_OPENGL_ALPHA
setOpaque();
#endif
+#ifndef WITH_GHOST_WAYLAND_LIBDECOR /* Causes a glitch with `libdecor` for some reason. */
setState(state);
-
- setTitle(title);
+#endif
/* EGL context. */
if (setDrawingContextType(type) == GHOST_kFailure) {
GHOST_PRINT("Failed to create EGL context" << std::endl);
}
- /* set swap interval to 0 to prevent blocking */
+ /* Set swap interval to 0 to prevent blocking. */
setSwapInterval(0);
}
-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()
-{
-#ifdef GHOST_OPENGL_ALPHA
- setOpaque();
-#endif
-
- return m_system->pushEvent(
- new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowSize, this));
-}
-
-wl_surface *GHOST_WindowWayland::surface() const
-{
- return w->wl_surface;
-}
-
-const std::vector<output_t *> &GHOST_WindowWayland::outputs()
-{
- return w->outputs;
-}
-
-output_t *GHOST_WindowWayland::output_find_by_wl(struct wl_output *output)
+GHOST_TSuccess GHOST_WindowWayland::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
{
- for (output_t *reg_output : this->m_system->outputs()) {
- if (reg_output->wl_output == output) {
- return reg_output;
+ GHOST_Rect bounds_buf;
+ GHOST_Rect *bounds = nullptr;
+ if (m_cursorGrab == GHOST_kGrabWrap) {
+ if (getCursorGrabBounds(bounds_buf) == GHOST_kFailure) {
+ getClientBounds(bounds_buf);
}
+ bounds = &bounds_buf;
}
- return nullptr;
-}
-
-bool GHOST_WindowWayland::outputs_changed_update_scale()
-{
- uint32_t dpi_next;
- const int scale_next = outputs_max_scale_or_default(this->outputs(), 0, &dpi_next);
- if (scale_next == 0) {
- return false;
- }
-
- window_t *win = this->w;
- const uint32_t dpi_curr = win->dpi;
- const int scale_curr = win->scale;
- bool changed = false;
-
- if (scale_next != scale_curr) {
- /* Unlikely but possible there is a pending size change is set. */
- win->size_pending[0] = (win->size_pending[0] / scale_curr) * scale_next;
- win->size_pending[1] = (win->size_pending[1] / scale_curr) * scale_next;
-
- win->scale = scale_next;
- wl_surface_set_buffer_scale(this->surface(), scale_next);
- changed = true;
- }
-
- if (dpi_next != dpi_curr) {
- /* Using the real DPI will cause wrong scaling of the UI
- * use a multiplier for the default DPI as workaround. */
- win->dpi = dpi_next;
- changed = true;
+ if (m_system->window_cursor_grab_set(mode,
+ m_cursorGrab,
+ m_cursorGrabInitPos,
+ bounds,
+ m_cursorGrabAxis,
+ w->wl_surface,
+ w->scale)) {
+ return GHOST_kSuccess;
}
-
- return changed;
-}
-
-bool GHOST_WindowWayland::outputs_enter(output_t *reg_output)
-{
- std::vector<output_t *> &outputs = w->outputs;
- auto it = std::find(outputs.begin(), outputs.end(), reg_output);
- if (it != outputs.end()) {
- return false;
- }
- outputs.push_back(reg_output);
- return true;
-}
-
-bool GHOST_WindowWayland::outputs_leave(output_t *reg_output)
-{
- std::vector<output_t *> &outputs = w->outputs;
- auto it = std::find(outputs.begin(), outputs.end(), reg_output);
- if (it == outputs.end()) {
- return false;
- }
- outputs.erase(it);
- return true;
-}
-
-uint16_t GHOST_WindowWayland::dpi()
-{
- return w->dpi;
-}
-
-int GHOST_WindowWayland::scale()
-{
- return w->scale;
-}
-
-GHOST_TSuccess GHOST_WindowWayland::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
-{
- return m_system->setCursorGrab(mode, m_cursorGrab, w->wl_surface);
+ return GHOST_kFailure;
}
GHOST_TSuccess GHOST_WindowWayland::setWindowCursorShape(GHOST_TStandardCursor shape)
@@ -492,16 +577,32 @@ GHOST_TSuccess GHOST_WindowWayland::setWindowCursorShape(GHOST_TStandardCursor s
return ok;
}
+bool GHOST_WindowWayland::getCursorGrabUseSoftwareDisplay()
+{
+ return m_system->getCursorGrabUseSoftwareDisplay(m_cursorGrab);
+}
+
GHOST_TSuccess GHOST_WindowWayland::setWindowCustomCursorShape(
uint8_t *bitmap, uint8_t *mask, int sizex, int sizey, int hotX, int hotY, bool canInvertColor)
{
return m_system->setCustomCursorShape(bitmap, mask, sizex, sizey, hotX, hotY, canInvertColor);
}
+GHOST_TSuccess GHOST_WindowWayland::getCursorBitmap(GHOST_CursorBitmapRef *bitmap)
+{
+ return m_system->getCursorBitmap(bitmap);
+}
+
void GHOST_WindowWayland::setTitle(const char *title)
{
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+ libdecor_frame_set_app_id(w->decor_frame, title);
+ libdecor_frame_set_title(w->decor_frame, title);
+#else
xdg_toplevel_set_title(w->xdg_toplevel, title);
xdg_toplevel_set_app_id(w->xdg_toplevel, title);
+#endif
+
this->title = title;
}
@@ -517,20 +618,20 @@ void GHOST_WindowWayland::getWindowBounds(GHOST_Rect &bounds) const
void GHOST_WindowWayland::getClientBounds(GHOST_Rect &bounds) const
{
- bounds.set(0, 0, w->size[0], w->size[1]);
+ bounds.set(0, 0, UNPACK2(w->size));
}
-GHOST_TSuccess GHOST_WindowWayland::setClientWidth(uint32_t width)
+GHOST_TSuccess GHOST_WindowWayland::setClientWidth(const uint32_t width)
{
return setClientSize(width, uint32_t(w->size[1]));
}
-GHOST_TSuccess GHOST_WindowWayland::setClientHeight(uint32_t height)
+GHOST_TSuccess GHOST_WindowWayland::setClientHeight(const uint32_t height)
{
return setClientSize(uint32_t(w->size[0]), height);
}
-GHOST_TSuccess GHOST_WindowWayland::setClientSize(uint32_t width, uint32_t height)
+GHOST_TSuccess GHOST_WindowWayland::setClientSize(const uint32_t width, const uint32_t height)
{
wl_egl_window_resize(w->egl_window, int(width), int(height), 0, 0);
@@ -569,13 +670,28 @@ GHOST_WindowWayland::~GHOST_WindowWayland()
releaseNativeHandles();
wl_egl_window_destroy(w->egl_window);
+
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+ libdecor_frame_unref(w->decor_frame);
+#else
if (w->xdg_toplevel_decoration) {
zxdg_toplevel_decoration_v1_destroy(w->xdg_toplevel_decoration);
}
xdg_toplevel_destroy(w->xdg_toplevel);
xdg_surface_destroy(w->xdg_surface);
+#endif
+
+ /* Clear any pointers to this window. This is needed because there are no guarantees
+ * that flushing the display will the "leave" handlers before handling events. */
+ m_system->window_surface_unref(w->wl_surface);
+
wl_surface_destroy(w->wl_surface);
+ /* NOTE(@campbellbarton): Flushing will often run the appropriate handlers event
+ * (#wl_surface_listener.leave in particular) to avoid attempted access to the freed surfaces.
+ * This is not fool-proof though, hence the call to #window_surface_unref, see: T99078. */
+ wl_display_flush(m_system->display());
+
delete w;
}
@@ -596,23 +712,43 @@ GHOST_TSuccess GHOST_WindowWayland::setState(GHOST_TWindowState state)
/* Unset states. */
switch (getState()) {
case GHOST_kWindowStateMaximized:
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+ libdecor_frame_unset_maximized(w->decor_frame);
+#else
xdg_toplevel_unset_maximized(w->xdg_toplevel);
+#endif
break;
case GHOST_kWindowStateFullScreen:
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+ libdecor_frame_unset_fullscreen(w->decor_frame);
+#else
xdg_toplevel_unset_fullscreen(w->xdg_toplevel);
+#endif
break;
default:
break;
}
break;
case GHOST_kWindowStateMaximized:
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+ libdecor_frame_set_maximized(w->decor_frame);
+#else
xdg_toplevel_set_maximized(w->xdg_toplevel);
+#endif
break;
case GHOST_kWindowStateMinimized:
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+ libdecor_frame_set_minimized(w->decor_frame);
+#else
xdg_toplevel_set_minimized(w->xdg_toplevel);
+#endif
break;
case GHOST_kWindowStateFullScreen:
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+ libdecor_frame_set_fullscreen(w->decor_frame, nullptr);
+#else
xdg_toplevel_set_fullscreen(w->xdg_toplevel, nullptr);
+#endif
break;
case GHOST_kWindowStateEmbedded:
return GHOST_kFailure;
@@ -643,13 +779,21 @@ GHOST_TSuccess GHOST_WindowWayland::setOrder(GHOST_TWindowOrder /*order*/)
GHOST_TSuccess GHOST_WindowWayland::beginFullScreen() const
{
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+ libdecor_frame_set_fullscreen(w->decor_frame, nullptr);
+#else
xdg_toplevel_set_fullscreen(w->xdg_toplevel, nullptr);
+#endif
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_WindowWayland::endFullScreen() const
{
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+ libdecor_frame_unset_fullscreen(w->decor_frame);
+#else
xdg_toplevel_unset_fullscreen(w->xdg_toplevel);
+#endif
return GHOST_kSuccess;
}
@@ -665,7 +809,7 @@ void GHOST_WindowWayland::setOpaque() const
/* Make the window opaque. */
region = wl_compositor_create_region(m_system->compositor());
- wl_region_add(region, 0, 0, w->size[0], w->size[1]);
+ wl_region_add(region, 0, 0, UNPACK2(w->size));
wl_surface_set_opaque_region(w->surface, region);
wl_region_destroy(region);
}
@@ -716,3 +860,143 @@ GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Public WAYLAND Direct Data Access
+ *
+ * Expose some members via methods.
+ * \{ */
+
+uint16_t GHOST_WindowWayland::dpi() const
+{
+ return w->dpi;
+}
+
+int GHOST_WindowWayland::scale() const
+{
+ return w->scale;
+}
+
+wl_surface *GHOST_WindowWayland::wl_surface() const
+{
+ return w->wl_surface;
+}
+
+const std::vector<GWL_Output *> &GHOST_WindowWayland::outputs()
+{
+ return w->outputs;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Public WAYLAND Window Level Functions
+ *
+ * High Level Windowing Utilities.
+ * \{ */
+
+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()
+{
+#ifdef GHOST_OPENGL_ALPHA
+ setOpaque();
+#endif
+
+ return m_system->pushEvent(
+ new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowSize, this));
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Public WAYLAND Utility Functions
+ *
+ * Functionality only used for the WAYLAND implementation.
+ * \{ */
+
+/**
+ * Return true when the windows scale or DPI changes.
+ */
+bool GHOST_WindowWayland::outputs_changed_update_scale()
+{
+ uint32_t dpi_next;
+ const int scale_next = outputs_max_scale_or_default(this->outputs(), 0, &dpi_next);
+ if (UNLIKELY(scale_next == 0)) {
+ return false;
+ }
+
+ GWL_Window *win = this->w;
+ const uint32_t dpi_curr = win->dpi;
+ const int scale_curr = win->scale;
+ bool changed = false;
+
+ if (scale_next != scale_curr) {
+ /* Unlikely but possible there is a pending size change is set. */
+ win->size_pending[0] = (win->size_pending[0] / scale_curr) * scale_next;
+ win->size_pending[1] = (win->size_pending[1] / scale_curr) * scale_next;
+
+ win->scale = scale_next;
+ wl_surface_set_buffer_scale(w->wl_surface, scale_next);
+ changed = true;
+ }
+
+ if (dpi_next != dpi_curr) {
+ /* Using the real DPI will cause wrong scaling of the UI
+ * use a multiplier for the default DPI as workaround. */
+ win->dpi = dpi_next;
+ changed = true;
+
+ /* As this is a low-level function, we might want adding this event to be optional,
+ * always add the event unless it causes issues. */
+ GHOST_System *system = (GHOST_System *)GHOST_ISystem::getSystem();
+ system->pushEvent(
+ new GHOST_Event(system->getMilliSeconds(), GHOST_kEventWindowDPIHintChanged, this));
+ }
+
+ return changed;
+}
+
+bool GHOST_WindowWayland::outputs_enter(GWL_Output *output)
+{
+ std::vector<GWL_Output *> &outputs = w->outputs;
+ auto it = std::find(outputs.begin(), outputs.end(), output);
+ if (it != outputs.end()) {
+ return false;
+ }
+ outputs.push_back(output);
+ return true;
+}
+
+bool GHOST_WindowWayland::outputs_leave(GWL_Output *output)
+{
+ std::vector<GWL_Output *> &outputs = w->outputs;
+ auto it = std::find(outputs.begin(), outputs.end(), output);
+ if (it == outputs.end()) {
+ return false;
+ }
+ outputs.erase(it);
+ return true;
+}
+
+/** \} */
diff --git a/intern/ghost/intern/GHOST_WindowWayland.h b/intern/ghost/intern/GHOST_WindowWayland.h
index b6d9fa04079..9b4c17ecd95 100644
--- a/intern/ghost/intern/GHOST_WindowWayland.h
+++ b/intern/ghost/intern/GHOST_WindowWayland.h
@@ -10,13 +10,12 @@
#include "GHOST_Window.h"
-#include <unordered_set>
#include <vector>
class GHOST_SystemWayland;
-struct output_t;
-struct window_t;
+struct GWL_Output;
+struct GWL_Window;
class GHOST_WindowWayland : public GHOST_Window {
public:
@@ -37,10 +36,10 @@ class GHOST_WindowWayland : public GHOST_Window {
~GHOST_WindowWayland() override;
- uint16_t getDPIHint() override;
-
/* Ghost API */
+ uint16_t getDPIHint() override;
+
GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode) override;
GHOST_TSuccess setWindowCursorShape(GHOST_TStandardCursor shape) override;
@@ -52,6 +51,9 @@ class GHOST_WindowWayland : public GHOST_Window {
int hotX,
int hotY,
bool canInvertColor) override;
+ bool getCursorGrabUseSoftwareDisplay() override;
+
+ GHOST_TSuccess getCursorBitmap(GHOST_CursorBitmapRef *bitmap) override;
void setTitle(const char *title) override;
@@ -91,33 +93,30 @@ class GHOST_WindowWayland : public GHOST_Window {
void setOpaque() const;
#endif
- /* WAYLAND utility functions. */
+ /* WAYLAND direct-data access. */
- GHOST_TSuccess close();
+ uint16_t dpi() const;
+ int scale() const;
+ struct wl_surface *wl_surface() const;
+ const std::vector<GWL_Output *> &outputs();
- GHOST_TSuccess activate();
+ /* WAYLAND window-level functions. */
+ GHOST_TSuccess close();
+ GHOST_TSuccess activate();
GHOST_TSuccess deactivate();
-
GHOST_TSuccess notify_size();
- struct wl_surface *surface() const;
-
- output_t *output_find_by_wl(struct wl_output *output);
+ /* WAYLAND utility functions. */
- const std::vector<output_t *> &outputs();
+ bool outputs_enter(GWL_Output *output);
+ bool outputs_leave(GWL_Output *output);
- bool outputs_enter(output_t *reg_output);
- bool outputs_leave(output_t *reg_output);
bool outputs_changed_update_scale();
- uint16_t dpi();
-
- int scale();
-
private:
GHOST_SystemWayland *m_system;
- struct window_t *w;
+ struct GWL_Window *w;
std::string title;
/**
diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp
index 2e17454d24f..50ee9385e39 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.cpp
+++ b/intern/ghost/intern/GHOST_WindowWin32.cpp
@@ -26,7 +26,7 @@
#ifndef GET_POINTERID_WPARAM
# define GET_POINTERID_WPARAM(wParam) (LOWORD(wParam))
-#endif // GET_POINTERID_WPARAM
+#endif /* GET_POINTERID_WPARAM */
const wchar_t *GHOST_WindowWin32::s_windowClassName = L"GHOST_WindowClass";
const int GHOST_WindowWin32::s_maxTitleLength = 128;
@@ -93,18 +93,18 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
adjustWindowRectForClosestMonitor(&win_rect, style, extended_style);
wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0);
- m_hWnd = ::CreateWindowExW(extended_style, // window extended style
- s_windowClassName, // pointer to registered class name
- title_16, // pointer to window name
- style, // window style
- win_rect.left, // horizontal position of window
- win_rect.top, // vertical position of window
- win_rect.right - win_rect.left, // window width
- win_rect.bottom - win_rect.top, // window height
- m_parentWindowHwnd, // handle to parent or owner window
- 0, // handle to menu or child-window identifier
- ::GetModuleHandle(0), // handle to application instance
- 0); // pointer to window-creation data
+ m_hWnd = ::CreateWindowExW(extended_style, /* window extended style */
+ s_windowClassName, /* pointer to registered class name */
+ title_16, /* pointer to window name */
+ style, /* window style */
+ win_rect.left, /* horizontal position of window */
+ win_rect.top, /* vertical position of window */
+ win_rect.right - win_rect.left, /* window width */
+ win_rect.bottom - win_rect.top, /* window height */
+ m_parentWindowHwnd, /* handle to parent or owner window */
+ 0, /* handle to menu or child-window identifier */
+ ::GetModuleHandle(0), /* handle to application instance */
+ 0); /* pointer to window-creation data */
free(title_16);
if (m_hWnd == NULL) {
@@ -197,7 +197,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
/* Force an initial paint of the window. */
::UpdateWindow(m_hWnd);
- /* Initialize Wintab. */
+ /* Initialize WINTAB. */
if (system->getTabletAPI() != GHOST_kTabletWinPointer) {
loadWintab(GHOST_kWindowStateMinimized != state);
}
@@ -221,7 +221,7 @@ void GHOST_WindowWin32::updateDirectManipulation()
void GHOST_WindowWin32::onPointerHitTest(WPARAM wParam)
{
- /* Only DM_POINTERHITTEST can be the first message of input sequence of touchpad input. */
+ /* Only #DM_POINTERHITTEST can be the first message of input sequence of touch-pad input. */
if (!m_directManipulationHelper) {
return;
@@ -280,9 +280,9 @@ GHOST_WindowWin32::~GHOST_WindowWin32()
}
if (m_dropTarget) {
- // Disable DragDrop
+ /* Disable DragDrop. */
RevokeDragDrop(m_hWnd);
- // Release our reference of the DropTarget and it will delete itself eventually.
+ /* Release our reference of the DropTarget and it will delete itself eventually. */
m_dropTarget->Release();
m_dropTarget = NULL;
}
@@ -531,7 +531,8 @@ GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
break;
}
::SetWindowLongPtr(m_hWnd, GWL_STYLE, style);
- /* SetWindowLongPtr Docs: frame changes not visible until SetWindowPos with SWP_FRAMECHANGED. */
+ /* #SetWindowLongPtr Docs:
+ * Frame changes not visible until #SetWindowPos with #SWP_FRAMECHANGED. */
::SetWindowPos(m_hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
return ::SetWindowPlacement(m_hWnd, &wp) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
}
@@ -580,7 +581,6 @@ GHOST_Context *GHOST_WindowWin32::newDrawingContext(GHOST_TDrawingContextType ty
if (type == GHOST_kDrawingContextTypeOpenGL) {
GHOST_Context *context;
-#if defined(WITH_GL_PROFILE_CORE)
/* - AMD and Intel give us exactly this version
* - NVIDIA gives at least this version <-- desired behavior
* So we ask for 4.5, 4.4 ... 3.3 in descending order
@@ -619,40 +619,14 @@ GHOST_Context *GHOST_WindowWin32::newDrawingContext(GHOST_TDrawingContextType ty
}
return context;
-
-#elif defined(WITH_GL_PROFILE_COMPAT)
- // ask for 2.1 context, driver gives any GL version >= 2.1
- // (hopefully the latest compatibility profile)
- // 2.1 ignores the profile bit & is incompatible with core profile
- context = new GHOST_ContextWGL(m_wantStereoVisual,
- m_wantAlphaBackground,
- m_hWnd,
- m_hDC,
- 0, // no profile bit
- 2,
- 1,
- (m_debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
- GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
-
- if (context->initializeDrawingContext()) {
- return context;
- }
- else {
- delete context;
- }
-#else
-# error // must specify either core or compat at build time
-#endif
}
else if (type == GHOST_kDrawingContextTypeD3D) {
GHOST_Context *context;
context = new GHOST_ContextD3D(false, m_hWnd);
- if (context->initializeDrawingContext()) {
- return context;
- }
- else {
+ if (!context->initializeDrawingContext()) {
delete context;
+ context = nullptr;
}
return context;
@@ -705,7 +679,7 @@ void GHOST_WindowWin32::updateMouseCapture(GHOST_MouseCaptureEventWin32 event)
HCURSOR GHOST_WindowWin32::getStandardCursor(GHOST_TStandardCursor shape) const
{
- // Convert GHOST cursor to Windows OEM cursor
+ /* Convert GHOST cursor to Windows OEM cursor. */
HANDLE cursor = NULL;
HMODULE module = ::GetModuleHandle(0);
uint32_t flags = LR_SHARED | LR_DEFAULTSIZE;
@@ -763,36 +737,36 @@ HCURSOR GHOST_WindowWin32::getStandardCursor(GHOST_TStandardCursor shape) const
break;
case GHOST_kStandardCursorHelp:
cursor = ::LoadImage(NULL, IDC_HELP, IMAGE_CURSOR, cx, cy, flags);
- break; // Arrow and question mark
+ break; /* Arrow and question mark */
case GHOST_kStandardCursorWait:
cursor = ::LoadImage(NULL, IDC_WAIT, IMAGE_CURSOR, cx, cy, flags);
- break; // Hourglass
+ break; /* Hourglass */
case GHOST_kStandardCursorText:
cursor = ::LoadImage(NULL, IDC_IBEAM, IMAGE_CURSOR, cx, cy, flags);
- break; // I-beam
+ break; /* I-beam */
case GHOST_kStandardCursorCrosshair:
cursor = ::LoadImage(module, "cross_cursor", IMAGE_CURSOR, cx, cy, flags);
- break; // Standard Cross
+ break; /* Standard Cross */
case GHOST_kStandardCursorCrosshairA:
cursor = ::LoadImage(module, "crossA_cursor", IMAGE_CURSOR, cx, cy, flags);
- break; // Crosshair A
+ break; /* Crosshair A */
case GHOST_kStandardCursorCrosshairB:
cursor = ::LoadImage(module, "crossB_cursor", IMAGE_CURSOR, cx, cy, flags);
- break; // Diagonal Crosshair B
+ break; /* Diagonal Crosshair B */
case GHOST_kStandardCursorCrosshairC:
cursor = ::LoadImage(module, "crossC_cursor", IMAGE_CURSOR, cx, cy, flags);
- break; // Minimal Crosshair C
+ break; /* Minimal Crosshair C */
case GHOST_kStandardCursorBottomSide:
case GHOST_kStandardCursorUpDown:
cursor = ::LoadImage(module, "movens_cursor", IMAGE_CURSOR, cx, cy, flags);
- break; // Double-pointed arrow pointing north and south
+ break; /* Double-pointed arrow pointing north and south */
case GHOST_kStandardCursorLeftSide:
case GHOST_kStandardCursorLeftRight:
cursor = ::LoadImage(module, "moveew_cursor", IMAGE_CURSOR, cx, cy, flags);
- break; // Double-pointed arrow pointing west and east
+ break; /* Double-pointed arrow pointing west and east */
case GHOST_kStandardCursorTopSide:
cursor = ::LoadImage(NULL, IDC_UPARROW, IMAGE_CURSOR, cx, cy, flags);
- break; // Vertical arrow
+ break; /* Vertical arrow */
case GHOST_kStandardCursorTopLeftCorner:
cursor = ::LoadImage(NULL, IDC_SIZENWSE, IMAGE_CURSOR, cx, cy, flags);
break;
@@ -814,7 +788,7 @@ HCURSOR GHOST_WindowWin32::getStandardCursor(GHOST_TStandardCursor shape) const
case GHOST_kStandardCursorDestroy:
case GHOST_kStandardCursorStop:
cursor = ::LoadImage(module, "forbidden_cursor", IMAGE_CURSOR, cx, cy, flags);
- break; // Slashed circle
+ break; /* Slashed circle */
case GHOST_kStandardCursorDefault:
cursor = NULL;
break;
@@ -863,8 +837,9 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCursorGrab(GHOST_TGrabCursorMode mode
m_system->getCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
setCursorGrabAccum(0, 0);
- if (mode == GHOST_kGrabHide)
+ if (mode == GHOST_kGrabHide) {
setWindowCursorVisibility(false);
+ }
}
updateMouseCapture(OperatorGrab);
}
@@ -874,9 +849,9 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCursorGrab(GHOST_TGrabCursorMode mode
setWindowCursorVisibility(true);
}
if (m_cursorGrab != GHOST_kGrabNormal) {
- /* use to generate a mouse move event, otherwise the last event
+ /* Use to generate a mouse move event, otherwise the last event
* blender gets can be outside the screen causing menus not to show
- * properly unless the user moves the mouse */
+ * properly unless the user moves the mouse. */
int32_t pos[2];
m_system->getCursorPosition(pos[0], pos[1]);
m_system->setCursorPosition(pos[0], pos[1]);
@@ -927,7 +902,7 @@ GHOST_TSuccess GHOST_WindowWin32::getPointerInfo(
for (uint32_t i = 0; i < outCount; i++) {
POINTER_INFO pointerApiInfo = pointerPenInfo[i].pointerInfo;
- // Obtain the basic information from the event
+ /* Obtain the basic information from the event. */
outPointerInfo[i].pointerId = pointerId;
outPointerInfo[i].isPrimary = isPrimary;
@@ -1069,8 +1044,9 @@ void GHOST_WindowWin32::ThemeRefresh()
&pcbData) == ERROR_SUCCESS) {
BOOL DarkMode = !lightMode;
- /* 20 == DWMWA_USE_IMMERSIVE_DARK_MODE in Windows 11 SDK. This value was undocumented for
- * Windows 10 versions 2004 and later, supported for Windows 11 Build 22000 and later. */
+ /* `20 == DWMWA_USE_IMMERSIVE_DARK_MODE` in Windows 11 SDK.
+ * This value was undocumented for Windows 10 versions 2004 and later,
+ * supported for Windows 11 Build 22000 and later. */
DwmSetWindowAttribute(this->m_hWnd, 20, &DarkMode, sizeof(DarkMode));
}
}
@@ -1126,8 +1102,9 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(
int x, y, cols;
cols = sizeX / 8; /* Number of whole bytes per row (width of bitmap/mask). */
- if (sizeX % 8)
+ if (sizeX % 8) {
cols++;
+ }
if (m_customCursor) {
DestroyCursor(m_customCursor);
@@ -1165,16 +1142,18 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(
GHOST_TSuccess GHOST_WindowWin32::setProgressBar(float progress)
{
/* #SetProgressValue sets state to #TBPF_NORMAL automatically. */
- if (m_Bar && S_OK == m_Bar->SetProgressValue(m_hWnd, 10000 * progress, 10000))
+ if (m_Bar && S_OK == m_Bar->SetProgressValue(m_hWnd, 10000 * progress, 10000)) {
return GHOST_kSuccess;
+ }
return GHOST_kFailure;
}
GHOST_TSuccess GHOST_WindowWin32::endProgressBar()
{
- if (m_Bar && S_OK == m_Bar->SetProgressState(m_hWnd, TBPF_NOPROGRESS))
+ if (m_Bar && S_OK == m_Bar->SetProgressState(m_hWnd, TBPF_NOPROGRESS)) {
return GHOST_kSuccess;
+ }
return GHOST_kFailure;
}
diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h
index c958a89ac48..44071f0915e 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.h
+++ b/intern/ghost/intern/GHOST_WindowWin32.h
@@ -10,7 +10,7 @@
#ifndef WIN32
# error WIN32 only!
-#endif // WIN32
+#endif /* WIN32 */
#include "GHOST_TaskbarWin32.h"
#include "GHOST_TrackpadWin32.h"
@@ -25,7 +25,7 @@
class GHOST_SystemWin32;
class GHOST_DropTargetWin32;
-// typedefs for user32 functions to allow dynamic loading of Windows 10 DPI scaling functions
+/* typedefs for user32 functions to allow dynamic loading of Windows 10 DPI scaling functions. */
typedef UINT(API *GHOST_WIN32_GetDpiForWindow)(HWND);
typedef BOOL(API *GHOST_WIN32_AdjustWindowRectExForDpi)(
@@ -34,7 +34,7 @@ typedef BOOL(API *GHOST_WIN32_AdjustWindowRectExForDpi)(
struct GHOST_PointerInfoWin32 {
int32_t pointerId;
int32_t isPrimary;
- GHOST_TButtonMask buttonMask;
+ GHOST_TButton buttonMask;
POINT pixelLocation;
uint64_t time;
GHOST_TabletData tabletData;
diff --git a/intern/ghost/intern/GHOST_WindowX11.cpp b/intern/ghost/intern/GHOST_WindowX11.cpp
index ac7a476c76f..0b2617c1b9e 100644
--- a/intern/ghost/intern/GHOST_WindowX11.cpp
+++ b/intern/ghost/intern/GHOST_WindowX11.cpp
@@ -10,24 +10,19 @@
#include <X11/Xmd.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>
-#ifdef WITH_X11_ALPHA
-# include <X11/extensions/Xrender.h>
-#endif
+
#include "GHOST_Debug.h"
#include "GHOST_IconX11.h"
#include "GHOST_SystemX11.h"
#include "GHOST_WindowX11.h"
+#include "GHOST_utildefines.h"
#ifdef WITH_XDND
# include "GHOST_DropTargetX11.h"
#endif
-#ifdef WITH_GL_EGL
-# include "GHOST_ContextEGL.h"
-# include <EGL/eglext.h>
-#else
-# include "GHOST_ContextGLX.h"
-#endif
+#include "GHOST_ContextEGL.h"
+#include "GHOST_ContextGLX.h"
/* for XIWarpPointer */
#ifdef WITH_X11_XINPUT
@@ -87,9 +82,7 @@ enum {
#define _NET_WM_STATE_ADD 1
// #define _NET_WM_STATE_TOGGLE 2 // UNUSED
-#ifdef WITH_GL_EGL
-
-static XVisualInfo *x11_visualinfo_from_egl(Display *display)
+static XVisualInfo *get_x11_visualinfo(Display *display)
{
int num_visuals;
XVisualInfo vinfo_template;
@@ -97,103 +90,6 @@ static XVisualInfo *x11_visualinfo_from_egl(Display *display)
return XGetVisualInfo(display, VisualScreenMask, &vinfo_template, &num_visuals);
}
-#else
-
-static XVisualInfo *x11_visualinfo_from_glx(Display *display,
- bool stereoVisual,
- bool needAlpha,
- GLXFBConfig *fbconfig)
-{
- int glx_major, glx_minor, glx_version; /* GLX version: major.minor */
- int glx_attribs[64];
-
- *fbconfig = nullptr;
-
- /* Set up the minimum attributes that we require and see if
- * X can find us a visual matching those requirements. */
-
- if (!glXQueryVersion(display, &glx_major, &glx_minor)) {
- fprintf(stderr,
- "%s:%d: X11 glXQueryVersion() failed, "
- "verify working openGL system!\n",
- __FILE__,
- __LINE__);
-
- return nullptr;
- }
- glx_version = glx_major * 100 + glx_minor;
-# ifndef WITH_X11_ALPHA
- (void)glx_version;
-# endif
-
-# ifdef WITH_X11_ALPHA
- if (needAlpha && glx_version >= 103 &&
- (glXChooseFBConfig || (glXChooseFBConfig = (PFNGLXCHOOSEFBCONFIGPROC)glXGetProcAddressARB(
- (const GLubyte *)"glXChooseFBConfig")) != nullptr) &&
- (glXGetVisualFromFBConfig ||
- (glXGetVisualFromFBConfig = (PFNGLXGETVISUALFROMFBCONFIGPROC)glXGetProcAddressARB(
- (const GLubyte *)"glXGetVisualFromFBConfig")) != nullptr)) {
-
- GHOST_X11_GL_GetAttributes(glx_attribs, 64, stereoVisual, needAlpha, true);
-
- int nbfbconfig;
- GLXFBConfig *fbconfigs = glXChooseFBConfig(
- display, DefaultScreen(display), glx_attribs, &nbfbconfig);
-
- /* Any sample level or even zero, which means oversampling disabled, is good
- * but we need a valid visual to continue */
- if (nbfbconfig > 0) {
- /* take a frame buffer config that has alpha cap */
- for (int i = 0; i < nbfbconfig; i++) {
- XVisualInfo *visual = (XVisualInfo *)glXGetVisualFromFBConfig(display, fbconfigs[i]);
- if (!visual)
- continue;
- /* if we don't need a alpha background, the first config will do, otherwise
- * test the alphaMask as it won't necessarily be present */
- if (needAlpha) {
- XRenderPictFormat *pict_format = XRenderFindVisualFormat(display, visual->visual);
- if (!pict_format)
- continue;
- if (pict_format->direct.alphaMask <= 0)
- continue;
- }
-
- *fbconfig = fbconfigs[i];
- XFree(fbconfigs);
-
- return visual;
- }
-
- XFree(fbconfigs);
- }
- }
- else
-# endif
- {
- /* legacy, don't use extension */
- GHOST_X11_GL_GetAttributes(glx_attribs, 64, stereoVisual, needAlpha, false);
-
- XVisualInfo *visual = glXChooseVisual(display, DefaultScreen(display), glx_attribs);
-
- /* Any sample level or even zero, which means oversampling disabled, is good
- * but we need a valid visual to continue */
- if (visual != nullptr) {
- return visual;
- }
- }
-
- /* All options exhausted, cannot continue */
- fprintf(stderr,
- "%s:%d: X11 glXChooseVisual() failed, "
- "verify working openGL system!\n",
- __FILE__,
- __LINE__);
-
- return nullptr;
-}
-
-#endif // WITH_GL_EGL
-
GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system,
Display *display,
const char *title,
@@ -207,7 +103,6 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system,
const bool is_dialog,
const bool stereoVisual,
const bool exclusive,
- const bool alphaBackground,
const bool is_debug)
: GHOST_Window(width, height, state, stereoVisual, exclusive),
m_display(display),
@@ -231,13 +126,7 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system,
m_is_debug_context(is_debug)
{
if (type == GHOST_kDrawingContextTypeOpenGL) {
-#ifdef WITH_GL_EGL
- m_visualInfo = x11_visualinfo_from_egl(m_display);
- (void)alphaBackground;
-#else
- m_visualInfo = x11_visualinfo_from_glx(
- m_display, stereoVisual, alphaBackground, (GLXFBConfig *)&m_fbconfig);
-#endif
+ m_visualInfo = get_x11_visualinfo(m_display);
}
else {
XVisualInfo tmp = {nullptr};
@@ -296,7 +185,7 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system,
GHOST_PRINT("Set drop target\n");
#endif
- if (state == GHOST_kWindowStateMaximized || state == GHOST_kWindowStateFullScreen) {
+ if (ELEM(state, GHOST_kWindowStateMaximized, GHOST_kWindowStateFullScreen)) {
Atom atoms[2];
int count = 0;
if (state == GHOST_kWindowStateMaximized) {
@@ -412,7 +301,7 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system,
32,
PropModeReplace,
(unsigned char *)BLENDER_ICONS_WM_X11,
- sizeof(BLENDER_ICONS_WM_X11) / sizeof(unsigned long));
+ ARRAY_SIZE(BLENDER_ICONS_WM_X11));
}
/* set the process ID (_NET_WM_PID) */
@@ -486,8 +375,9 @@ static Bool destroyICCallback(XIC /*xic*/, XPointer ptr, XPointer /*data*/)
bool GHOST_WindowX11::createX11_XIC()
{
XIM xim = m_system->getX11_XIM();
- if (!xim)
+ if (!xim) {
return false;
+ }
XICCallback destroy;
destroy.callback = (XICProc)destroyICCallback;
@@ -535,17 +425,21 @@ void GHOST_WindowX11::refreshXInputDevices()
XEventClass ev;
DeviceMotionNotify(xtablet.Device, xtablet.MotionEvent, ev);
- if (ev)
+ if (ev) {
xevents.push_back(ev);
+ }
DeviceButtonPress(xtablet.Device, xtablet.PressEvent, ev);
- if (ev)
+ if (ev) {
xevents.push_back(ev);
+ }
ProximityIn(xtablet.Device, xtablet.ProxInEvent, ev);
- if (ev)
+ if (ev) {
xevents.push_back(ev);
+ }
ProximityOut(xtablet.Device, xtablet.ProxOutEvent, ev);
- if (ev)
+ if (ev) {
xevents.push_back(ev);
+ }
}
XSelectExtensionEvent(m_display, m_window, xevents.data(), (int)xevents.size());
@@ -657,15 +551,15 @@ GHOST_TSuccess GHOST_WindowX11::setClientSize(uint32_t width, uint32_t height)
void GHOST_WindowX11::screenToClient(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const
{
- /* This is correct! */
-
int ax, ay;
Window temp;
+ /* Use (0, 0) instead of (inX, inY) to work around overflow of signed int16 in
+ * the implementation of this function. */
XTranslateCoordinates(
- m_display, RootWindow(m_display, m_visualInfo->screen), m_window, inX, inY, &ax, &ay, &temp);
- outX = ax;
- outY = ay;
+ m_display, RootWindow(m_display, m_visualInfo->screen), m_window, 0, 0, &ax, &ay, &temp);
+ outX = ax + inX;
+ outY = ay + inY;
}
void GHOST_WindowX11::clientToScreen(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const
@@ -984,7 +878,7 @@ GHOST_TWindowState GHOST_WindowX11::getState() const
* In the Iconic and Withdrawn state, the window
* is unmapped, so only need return a Minimized state.
*/
- if ((state == IconicState) || (state == WithdrawnState)) {
+ if (ELEM(state, IconicState, WithdrawnState)) {
state_ret = GHOST_kWindowStateMinimized;
}
else if (netwmIsFullScreen() == True) {
@@ -1284,6 +1178,65 @@ GHOST_WindowX11::~GHOST_WindowX11()
}
}
+#ifdef USE_EGL
+static GHOST_Context *create_egl_context(GHOST_SystemX11 *system,
+ Window window,
+ Display *display,
+ bool want_stereo,
+ bool debug_context,
+ int ver_major,
+ int ver_minor)
+{
+ GHOST_Context *context;
+ context = new GHOST_ContextEGL(system,
+ want_stereo,
+ EGLNativeWindowType(window),
+ EGLNativeDisplayType(display),
+ EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
+ ver_major,
+ ver_minor,
+ GHOST_OPENGL_EGL_CONTEXT_FLAGS |
+ (debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0),
+ GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
+ EGL_OPENGL_API);
+
+ if (context->initializeDrawingContext()) {
+ return context;
+ }
+ delete context;
+
+ return nullptr;
+}
+#endif
+
+static GHOST_Context *create_glx_context(Window window,
+ Display *display,
+ GLXFBConfig fbconfig,
+ bool want_stereo,
+ bool debug_context,
+ int ver_major,
+ int ver_minor)
+{
+ GHOST_Context *context;
+ context = new GHOST_ContextGLX(want_stereo,
+ window,
+ display,
+ fbconfig,
+ GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
+ ver_major,
+ ver_minor,
+ GHOST_OPENGL_GLX_CONTEXT_FLAGS |
+ (debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
+ GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY);
+
+ if (context->initializeDrawingContext()) {
+ return context;
+ }
+ delete context;
+
+ return nullptr;
+}
+
GHOST_Context *GHOST_WindowX11::newDrawingContext(GHOST_TDrawingContextType type)
{
if (type == GHOST_kDrawingContextTypeOpenGL) {
@@ -1298,99 +1251,48 @@ GHOST_Context *GHOST_WindowX11::newDrawingContext(GHOST_TDrawingContextType type
* - Try 3.3 core profile
* - No fall-backs. */
-#if defined(WITH_GL_PROFILE_CORE)
- {
- const char *version_major = (char *)glewGetString(GLEW_VERSION_MAJOR);
- if (version_major != nullptr && version_major[0] == '1') {
- fprintf(stderr, "Error: GLEW version 2.0 and above is required.\n");
- abort();
- }
- }
-#endif
-
- const int profile_mask =
-#ifdef WITH_GL_EGL
-# if defined(WITH_GL_PROFILE_CORE)
- EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT;
-# elif defined(WITH_GL_PROFILE_COMPAT)
- EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT;
-# else
-# error // must specify either core or compat at build time
-# endif
-#else
-# if defined(WITH_GL_PROFILE_CORE)
- GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
-# elif defined(WITH_GL_PROFILE_COMPAT)
- GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
-# else
-# error // must specify either core or compat at build time
-# endif
-#endif
-
GHOST_Context *context;
+#ifdef USE_EGL
+ /* Try to initialize an EGL context. */
for (int minor = 5; minor >= 0; --minor) {
-#ifdef WITH_GL_EGL
- context = new GHOST_ContextEGL(
- this->m_system,
- m_wantStereoVisual,
- EGLNativeWindowType(m_window),
- EGLNativeDisplayType(m_display),
- profile_mask,
- 4,
- minor,
- GHOST_OPENGL_EGL_CONTEXT_FLAGS |
- (m_is_debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0),
- GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
- EGL_OPENGL_API);
-#else
- context = new GHOST_ContextGLX(m_wantStereoVisual,
- m_window,
- m_display,
- (GLXFBConfig)m_fbconfig,
- profile_mask,
- 4,
- minor,
- GHOST_OPENGL_GLX_CONTEXT_FLAGS |
- (m_is_debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
- GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY);
-#endif
-
- if (context->initializeDrawingContext()) {
+ context = create_egl_context(
+ this->m_system, m_window, m_display, m_wantStereoVisual, m_is_debug_context, 4, minor);
+ if (context != nullptr) {
return context;
}
- delete context;
}
-#ifdef WITH_GL_EGL
- context = new GHOST_ContextEGL(this->m_system,
- m_wantStereoVisual,
- EGLNativeWindowType(m_window),
- EGLNativeDisplayType(m_display),
- profile_mask,
- 3,
- 3,
- GHOST_OPENGL_EGL_CONTEXT_FLAGS |
- (m_is_debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0),
- GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
- EGL_OPENGL_API);
-#else
- context = new GHOST_ContextGLX(m_wantStereoVisual,
- m_window,
+ context = create_egl_context(
+ this->m_system, m_window, m_display, m_wantStereoVisual, m_is_debug_context, 3, 3);
+ if (context != nullptr) {
+ return context;
+ }
+
+ /* EGL initialization failed, try to fallback to a GLX context. */
+#endif
+ for (int minor = 5; minor >= 0; --minor) {
+ context = create_glx_context(m_window,
m_display,
(GLXFBConfig)m_fbconfig,
- profile_mask,
- 3,
- 3,
- GHOST_OPENGL_GLX_CONTEXT_FLAGS |
- (m_is_debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
- GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY);
-#endif
-
- if (context->initializeDrawingContext()) {
+ m_wantStereoVisual,
+ m_is_debug_context,
+ 4,
+ minor);
+ if (context != nullptr) {
+ return context;
+ }
+ }
+ context = create_glx_context(m_window,
+ m_display,
+ (GLXFBConfig)m_fbconfig,
+ m_wantStereoVisual,
+ m_is_debug_context,
+ 3,
+ 3);
+ if (context != nullptr) {
return context;
}
- delete context;
/* Ugly, but we get crashes unless a whole bunch of systems are patched. */
fprintf(stderr, "Error! Unsupported graphics card or driver.\n");
@@ -1514,7 +1416,7 @@ GHOST_TSuccess GHOST_WindowX11::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
{
if (mode != GHOST_kGrabDisable) {
if (mode != GHOST_kGrabNormal) {
- m_system->getCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
+ m_system->getCursorPosition(UNPACK2(m_cursorGrabInitPos));
setCursorGrabAccum(0, 0);
if (mode == GHOST_kGrabHide) {
@@ -1535,7 +1437,7 @@ GHOST_TSuccess GHOST_WindowX11::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
}
else {
if (m_cursorGrab == GHOST_kGrabHide) {
- m_system->setCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
+ m_system->setCursorPosition(UNPACK2(m_cursorGrabInitPos));
}
if (m_cursorGrab != GHOST_kGrabNormal) {
diff --git a/intern/ghost/intern/GHOST_WindowX11.h b/intern/ghost/intern/GHOST_WindowX11.h
index ac4edd83549..c7a6b5e7357 100644
--- a/intern/ghost/intern/GHOST_WindowX11.h
+++ b/intern/ghost/intern/GHOST_WindowX11.h
@@ -47,7 +47,6 @@ class GHOST_WindowX11 : public GHOST_Window {
* \param parentWindow: Parent (embedder) window.
* \param type: The type of drawing context installed in this window.
* \param stereoVisual: Stereo visual for quad buffered stereo.
- * \param alphaBackground: Enable alpha blending of window with display background.
*/
GHOST_WindowX11(GHOST_SystemX11 *system,
Display *display,
@@ -62,7 +61,6 @@ class GHOST_WindowX11 : public GHOST_Window {
const bool is_dialog = false,
const bool stereoVisual = false,
const bool exclusive = false,
- const bool alphaBackground = false,
const bool is_debug = false);
bool getValid() const;
diff --git a/intern/ghost/intern/GHOST_Wintab.cpp b/intern/ghost/intern/GHOST_Wintab.cpp
index b136acbe098..c75a39bb34b 100644
--- a/intern/ghost/intern/GHOST_Wintab.cpp
+++ b/intern/ghost/intern/GHOST_Wintab.cpp
@@ -373,7 +373,7 @@ void GHOST_Wintab::getInput(std::vector<GHOST_WintabInfoWin32> &outWintabInfo)
/* Iterate over button flag indices until all flags are clear. */
for (WORD buttonIndex = 0; buttonsChanged; buttonIndex++, buttonsChanged >>= 1) {
if (buttonsChanged & 1) {
- GHOST_TButtonMask button = mapWintabToGhostButton(pkt.pkCursor, buttonIndex);
+ GHOST_TButton button = mapWintabToGhostButton(pkt.pkCursor, buttonIndex);
if (button != GHOST_kButtonMaskNone) {
/* If this is not the first button found, push info for the prior Wintab button. */
@@ -397,7 +397,7 @@ void GHOST_Wintab::getInput(std::vector<GHOST_WintabInfoWin32> &outWintabInfo)
}
}
-GHOST_TButtonMask GHOST_Wintab::mapWintabToGhostButton(UINT cursor, WORD physicalButton)
+GHOST_TButton GHOST_Wintab::mapWintabToGhostButton(UINT cursor, WORD physicalButton)
{
const WORD numButtons = 32;
BYTE logicalButtons[numButtons] = {0};
diff --git a/intern/ghost/intern/GHOST_Wintab.h b/intern/ghost/intern/GHOST_Wintab.h
index 80eacf1f3fa..565aeb6ca02 100644
--- a/intern/ghost/intern/GHOST_Wintab.h
+++ b/intern/ghost/intern/GHOST_Wintab.h
@@ -54,7 +54,7 @@ struct GHOST_WintabInfoWin32 {
int32_t x = 0;
int32_t y = 0;
GHOST_TEventType type = GHOST_kEventCursorMove;
- GHOST_TButtonMask button = GHOST_kButtonMaskNone;
+ GHOST_TButton button = GHOST_kButtonMaskNone;
uint64_t time = 0;
GHOST_TabletData tabletData = GHOST_TABLET_DATA_NONE;
};
@@ -243,7 +243,7 @@ class GHOST_Wintab {
* \param physicalButton: The physical button ID to inspect.
* \return The system mapped button.
*/
- GHOST_TButtonMask mapWintabToGhostButton(UINT cursor, WORD physicalButton);
+ GHOST_TButton mapWintabToGhostButton(UINT cursor, WORD physicalButton);
/**
* Applies common modifications to Wintab context.
diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp
index 2ac3d9ec2a5..413e2670750 100644
--- a/intern/ghost/intern/GHOST_XrContext.cpp
+++ b/intern/ghost/intern/GHOST_XrContext.cpp
@@ -436,9 +436,11 @@ void GHOST_XrContext::getExtensionsToEnable(
r_ext_names.push_back(gpu_binding);
}
-#if defined(WITH_GHOST_X11) && defined(WITH_GL_EGL)
- assert(openxr_extension_is_available(m_oxr->extensions, XR_MNDX_EGL_ENABLE_EXTENSION_NAME));
- r_ext_names.push_back(XR_MNDX_EGL_ENABLE_EXTENSION_NAME);
+#if defined(WITH_GHOST_X11)
+ if (openxr_extension_is_available(m_oxr->extensions, XR_MNDX_EGL_ENABLE_EXTENSION_NAME)) {
+ /* Use EGL if that backend is available. */
+ r_ext_names.push_back(XR_MNDX_EGL_ENABLE_EXTENSION_NAME);
+ }
#endif
for (const std::string_view &ext : try_ext) {
diff --git a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp
index aa230bf8deb..6a7eb25925a 100644
--- a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp
+++ b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp
@@ -8,17 +8,16 @@
#include <list>
#include <sstream>
-#if defined(WITH_GL_EGL)
+#if defined(WITH_GHOST_X11)
# include "GHOST_ContextEGL.h"
-# if defined(WITH_GHOST_X11)
-# include "GHOST_SystemX11.h"
-# endif
-# if defined(WITH_GHOST_WAYLAND)
-# include "GHOST_SystemWayland.h"
-# endif
-#elif defined(WITH_GHOST_X11)
# include "GHOST_ContextGLX.h"
-#elif defined(WIN32)
+# include "GHOST_SystemX11.h"
+#endif
+#if defined(WITH_GHOST_WAYLAND)
+# include "GHOST_ContextEGL.h"
+# include "GHOST_SystemWayland.h"
+#endif
+#if defined(WIN32)
# include "GHOST_ContextD3D.h"
# include "GHOST_ContextWGL.h"
# include "GHOST_SystemWin32.h"
@@ -61,19 +60,30 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding {
XrSystemId system_id,
std::string *r_requirement_info) const override
{
-#if defined(WITH_GL_EGL)
- GHOST_ContextEGL &ctx_gl = static_cast<GHOST_ContextEGL &>(ghost_ctx);
-#elif defined(WITH_GHOST_X11)
- GHOST_ContextGLX &ctx_gl = static_cast<GHOST_ContextGLX &>(ghost_ctx);
-#else
+ int gl_major_version, gl_minor_version;
+#if defined(WIN32)
GHOST_ContextWGL &ctx_gl = static_cast<GHOST_ContextWGL &>(ghost_ctx);
+ gl_major_version = ctx_gl.m_contextMajorVersion;
+ gl_minor_version = ctx_gl.m_contextMinorVersion;
+#elif defined(WITH_GHOST_X11) || defined(WITH_GHOST_WAYLAND)
+ if (dynamic_cast<GHOST_ContextEGL *>(&ghost_ctx)) {
+ GHOST_ContextEGL &ctx_gl = static_cast<GHOST_ContextEGL &>(ghost_ctx);
+ gl_major_version = ctx_gl.m_contextMajorVersion;
+ gl_minor_version = ctx_gl.m_contextMinorVersion;
+ }
+# if defined(WITH_GHOST_X11)
+ else {
+ GHOST_ContextGLX &ctx_gl = static_cast<GHOST_ContextGLX &>(ghost_ctx);
+ gl_major_version = ctx_gl.m_contextMajorVersion;
+ gl_minor_version = ctx_gl.m_contextMinorVersion;
+ }
+# endif
#endif
static PFN_xrGetOpenGLGraphicsRequirementsKHR s_xrGetOpenGLGraphicsRequirementsKHR_fn =
nullptr;
// static XrInstance s_instance = XR_NULL_HANDLE;
XrGraphicsRequirementsOpenGLKHR gpu_requirements = {XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR};
- const XrVersion gl_version = XR_MAKE_VERSION(
- ctx_gl.m_contextMajorVersion, ctx_gl.m_contextMinorVersion, 0);
+ const XrVersion gl_version = XR_MAKE_VERSION(gl_major_version, gl_minor_version, 0);
/* Although it would seem reasonable that the proc address would not change if the instance was
* the same, in testing, repeated calls to #xrGetInstanceProcAddress() with the same instance
@@ -112,45 +122,67 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding {
void initFromGhostContext(GHOST_Context &ghost_ctx) override
{
-#if defined(WITH_GHOST_X11)
-# if defined(WITH_GL_EGL)
- GHOST_ContextEGL &ctx_egl = static_cast<GHOST_ContextEGL &>(ghost_ctx);
-
- if (dynamic_cast<const GHOST_SystemX11 *const>(ctx_egl.m_system)) {
- oxr_binding.egl.type = XR_TYPE_GRAPHICS_BINDING_EGL_MNDX;
- oxr_binding.egl.getProcAddress = eglGetProcAddress;
- oxr_binding.egl.display = ctx_egl.getDisplay();
- oxr_binding.egl.config = ctx_egl.getConfig();
- oxr_binding.egl.context = ctx_egl.getContext();
- }
+#if defined(WITH_GHOST_X11) || defined(WITH_GHOST_WAYLAND)
+ /* WAYLAND/X11 may be dynamically selected at load time but both may also be
+ * supported at compile time individually.
+ * Without `is_ctx_egl` & `is_wayland` preprocessor checks become an unmanageable soup. */
+ const bool is_ctx_egl = dynamic_cast<GHOST_ContextEGL *>(&ghost_ctx) != nullptr;
+ if (is_ctx_egl) {
+ GHOST_ContextEGL &ctx_egl = static_cast<GHOST_ContextEGL &>(ghost_ctx);
+ const bool is_wayland = (
+# if defined(WITH_GHOST_WAYLAND)
+ dynamic_cast<const GHOST_SystemWayland *const>(ctx_egl.m_system) != nullptr
# else
- GHOST_ContextGLX &ctx_glx = static_cast<GHOST_ContextGLX &>(ghost_ctx);
- XVisualInfo *visual_info = glXGetVisualFromFBConfig(ctx_glx.m_display, ctx_glx.m_fbconfig);
+ false
+# endif
+ );
+
+ if (is_wayland) {
+# if defined(WITH_GHOST_WAYLAND)
+ /* #GHOST_SystemWayland */
+ oxr_binding.wl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WAYLAND_KHR;
+ oxr_binding.wl.display = (struct wl_display *)ctx_egl.m_nativeDisplay;
+# else
+ GHOST_ASSERT(false, "Unexpected State: logical error, unreachable!");
+# endif /* !WITH_GHOST_WAYLAND */
+ }
+ else { /* `!is_wayland` */
+# if defined(WITH_GHOST_X11)
+ /* #GHOST_SystemX11. */
+ oxr_binding.egl.type = XR_TYPE_GRAPHICS_BINDING_EGL_MNDX;
+ oxr_binding.egl.getProcAddress = eglGetProcAddress;
+ oxr_binding.egl.display = ctx_egl.getDisplay();
+ oxr_binding.egl.config = ctx_egl.getConfig();
+ oxr_binding.egl.context = ctx_egl.getContext();
+# else
+ GHOST_ASSERT(false, "Unexpected State: built with only WAYLAND and no System found!");
+# endif /* !WITH_GHOST_X11 */
+ }
+ }
+ else { /* `!is_ctx_egl` */
+# 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);
- oxr_binding.glx.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR;
- oxr_binding.glx.xDisplay = ctx_glx.m_display;
- oxr_binding.glx.glxFBConfig = ctx_glx.m_fbconfig;
- oxr_binding.glx.glxDrawable = ctx_glx.m_window;
- oxr_binding.glx.glxContext = ctx_glx.m_context;
- oxr_binding.glx.visualid = visual_info->visualid;
+ oxr_binding.glx.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR;
+ oxr_binding.glx.xDisplay = ctx_glx.m_display;
+ oxr_binding.glx.glxFBConfig = ctx_glx.m_fbconfig;
+ oxr_binding.glx.glxDrawable = ctx_glx.m_window;
+ oxr_binding.glx.glxContext = ctx_glx.m_context;
+ oxr_binding.glx.visualid = visual_info->visualid;
- XFree(visual_info);
-# endif
+ XFree(visual_info);
+# else
+ GHOST_ASSERT(false, "Unexpected State: built without X11 and no EGL context is available!");
+# endif /* !WITH_GHOST_X11 */
+ }
#elif defined(WIN32)
GHOST_ContextWGL &ctx_wgl = static_cast<GHOST_ContextWGL &>(ghost_ctx);
oxr_binding.wgl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR;
oxr_binding.wgl.hDC = ctx_wgl.m_hDC;
oxr_binding.wgl.hGLRC = ctx_wgl.m_hGLRC;
-#endif
-
-#if defined(WITH_GHOST_WAYLAND)
- GHOST_ContextEGL &ctx_wl_egl = static_cast<GHOST_ContextEGL &>(ghost_ctx);
- if (dynamic_cast<const GHOST_SystemWayland *const>(ctx_wl_egl.m_system)) {
- oxr_binding.wl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WAYLAND_KHR;
- oxr_binding.wl.display = (struct wl_display *)ctx_wl_egl.m_nativeDisplay;
- }
-#endif
+#endif /* WIN32 */
/* Generate a frame-buffer to use for blitting into the texture. */
glGenFramebuffers(1, &m_fbo);
diff --git a/intern/ghost/intern/GHOST_Xr_openxr_includes.h b/intern/ghost/intern/GHOST_Xr_openxr_includes.h
index 9706f51c027..1c16f746f7a 100644
--- a/intern/ghost/intern/GHOST_Xr_openxr_includes.h
+++ b/intern/ghost/intern/GHOST_Xr_openxr_includes.h
@@ -28,13 +28,8 @@
# include <d3d12.h>
#endif
#ifdef WITH_GHOST_X11
-# ifdef WITH_GL_EGL
-/* TODO: Why do we have to create this typedef manually? */
-typedef void (*(*PFNEGLGETPROCADDRESSPROC)(const char *procname))(void);
-# include <GL/eglew.h>
-# else
-# include <GL/glxew.h>
-# endif
+# include <epoxy/egl.h>
+# include <epoxy/glx.h>
#endif
#include <openxr/openxr.h>
diff --git a/intern/ghost/intern/GHOST_utildefines.h b/intern/ghost/intern/GHOST_utildefines.h
new file mode 100644
index 00000000000..f0ae6e12d3e
--- /dev/null
+++ b/intern/ghost/intern/GHOST_utildefines.h
@@ -0,0 +1,210 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup GHOST
+ *
+ * Utility defines (avoid depending on `BLI_utildefines.h`).
+ */
+
+#pragma once
+
+#include "GHOST_utildefines_variadic.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Branch Prediction Macros
+ * \{ */
+
+/* hints for branch prediction, only use in code that runs a _lot_ where */
+#ifdef __GNUC__
+# define LIKELY(x) __builtin_expect(!!(x), 1)
+# define UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+# define LIKELY(x) (x)
+# define UNLIKELY(x) (x)
+#endif
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Array Unpacking Macros
+ * \{ */
+
+/* unpack vector for args */
+#define UNPACK2(a) ((a)[0]), ((a)[1])
+#define UNPACK3(a) UNPACK2(a), ((a)[2])
+#define UNPACK4(a) UNPACK3(a), ((a)[3])
+/* pre may be '&', '*' or func, post may be '->member' */
+#define UNPACK2_EX(pre, a, post) (pre((a)[0]) post), (pre((a)[1]) post)
+#define UNPACK3_EX(pre, a, post) UNPACK2_EX(pre, a, post), (pre((a)[2]) post)
+#define UNPACK4_EX(pre, a, post) UNPACK3_EX(pre, a, post), (pre((a)[3]) post)
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Array Macros
+ * \{ */
+
+/* Assuming a static array. */
+#if defined(__GNUC__) && !defined(__cplusplus) && !defined(__clang__) && !defined(__INTEL_COMPILER)
+# define ARRAY_SIZE(arr) \
+ ((sizeof(struct { int isnt_array : ((const void *)&(arr) == &(arr)[0]); }) * 0) + \
+ (sizeof(arr) / sizeof(*(arr))))
+#else
+# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(*(arr)))
+#endif
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Equal to Any Element (ELEM) Macro
+ * \{ */
+
+/* Manual line breaks for readability. */
+/* clang-format off */
+
+/* ELEM#(v, ...): is the first arg equal any others? */
+/* internal helpers. */
+#define _VA_ELEM2(v, a) ((v) == (a))
+#define _VA_ELEM3(v, a, b) \
+ (_VA_ELEM2(v, a) || _VA_ELEM2(v, b))
+#define _VA_ELEM4(v, a, b, c) \
+ (_VA_ELEM3(v, a, b) || _VA_ELEM2(v, c))
+#define _VA_ELEM5(v, a, b, c, d) \
+ (_VA_ELEM4(v, a, b, c) || _VA_ELEM2(v, d))
+#define _VA_ELEM6(v, a, b, c, d, e) \
+ (_VA_ELEM5(v, a, b, c, d) || _VA_ELEM2(v, e))
+#define _VA_ELEM7(v, a, b, c, d, e, f) \
+ (_VA_ELEM6(v, a, b, c, d, e) || _VA_ELEM2(v, f))
+#define _VA_ELEM8(v, a, b, c, d, e, f, g) \
+ (_VA_ELEM7(v, a, b, c, d, e, f) || _VA_ELEM2(v, g))
+#define _VA_ELEM9(v, a, b, c, d, e, f, g, h) \
+ (_VA_ELEM8(v, a, b, c, d, e, f, g) || _VA_ELEM2(v, h))
+#define _VA_ELEM10(v, a, b, c, d, e, f, g, h, i) \
+ (_VA_ELEM9(v, a, b, c, d, e, f, g, h) || _VA_ELEM2(v, i))
+#define _VA_ELEM11(v, a, b, c, d, e, f, g, h, i, j) \
+ (_VA_ELEM10(v, a, b, c, d, e, f, g, h, i) || _VA_ELEM2(v, j))
+#define _VA_ELEM12(v, a, b, c, d, e, f, g, h, i, j, k) \
+ (_VA_ELEM11(v, a, b, c, d, e, f, g, h, i, j) || _VA_ELEM2(v, k))
+#define _VA_ELEM13(v, a, b, c, d, e, f, g, h, i, j, k, l) \
+ (_VA_ELEM12(v, a, b, c, d, e, f, g, h, i, j, k) || _VA_ELEM2(v, l))
+#define _VA_ELEM14(v, a, b, c, d, e, f, g, h, i, j, k, l, m) \
+ (_VA_ELEM13(v, a, b, c, d, e, f, g, h, i, j, k, l) || _VA_ELEM2(v, m))
+#define _VA_ELEM15(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n) \
+ (_VA_ELEM14(v, a, b, c, d, e, f, g, h, i, j, k, l, m) || _VA_ELEM2(v, n))
+#define _VA_ELEM16(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) \
+ (_VA_ELEM15(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n) || _VA_ELEM2(v, o))
+#define _VA_ELEM17(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
+ (_VA_ELEM16(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) || _VA_ELEM2(v, p))
+/* clang-format on */
+
+/* reusable ELEM macro */
+#define ELEM(...) VA_NARGS_CALL_OVERLOAD(_VA_ELEM, __VA_ARGS__)
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Clamp Macros
+ * \{ */
+
+#define CLAMPIS(a, b, c) ((a) < (b) ? (b) : (a) > (c) ? (c) : (a))
+
+#define CLAMP(a, b, c) \
+ { \
+ if ((a) < (b)) { \
+ (a) = (b); \
+ } \
+ else if ((a) > (c)) { \
+ (a) = (c); \
+ } \
+ } \
+ (void)0
+
+#define CLAMP_MAX(a, c) \
+ { \
+ if ((a) > (c)) { \
+ (a) = (c); \
+ } \
+ } \
+ (void)0
+
+#define CLAMP_MIN(a, b) \
+ { \
+ if ((a) < (b)) { \
+ (a) = (b); \
+ } \
+ } \
+ (void)0
+
+#define CLAMP2(vec, b, c) \
+ { \
+ CLAMP((vec)[0], b, c); \
+ CLAMP((vec)[1], b, c); \
+ } \
+ (void)0
+
+#define CLAMP2_MIN(vec, b) \
+ { \
+ CLAMP_MIN((vec)[0], b); \
+ CLAMP_MIN((vec)[1], b); \
+ } \
+ (void)0
+
+#define CLAMP2_MAX(vec, b) \
+ { \
+ CLAMP_MAX((vec)[0], b); \
+ CLAMP_MAX((vec)[1], b); \
+ } \
+ (void)0
+
+#define CLAMP3(vec, b, c) \
+ { \
+ CLAMP((vec)[0], b, c); \
+ CLAMP((vec)[1], b, c); \
+ CLAMP((vec)[2], b, c); \
+ } \
+ (void)0
+
+#define CLAMP3_MIN(vec, b) \
+ { \
+ CLAMP_MIN((vec)[0], b); \
+ CLAMP_MIN((vec)[1], b); \
+ CLAMP_MIN((vec)[2], b); \
+ } \
+ (void)0
+
+#define CLAMP3_MAX(vec, b) \
+ { \
+ CLAMP_MAX((vec)[0], b); \
+ CLAMP_MAX((vec)[1], b); \
+ CLAMP_MAX((vec)[2], b); \
+ } \
+ (void)0
+
+#define CLAMP4(vec, b, c) \
+ { \
+ CLAMP((vec)[0], b, c); \
+ CLAMP((vec)[1], b, c); \
+ CLAMP((vec)[2], b, c); \
+ CLAMP((vec)[3], b, c); \
+ } \
+ (void)0
+
+#define CLAMP4_MIN(vec, b) \
+ { \
+ CLAMP_MIN((vec)[0], b); \
+ CLAMP_MIN((vec)[1], b); \
+ CLAMP_MIN((vec)[2], b); \
+ CLAMP_MIN((vec)[3], b); \
+ } \
+ (void)0
+
+#define CLAMP4_MAX(vec, b) \
+ { \
+ CLAMP_MAX((vec)[0], b); \
+ CLAMP_MAX((vec)[1], b); \
+ CLAMP_MAX((vec)[2], b); \
+ CLAMP_MAX((vec)[3], b); \
+ } \
+ (void)0
+
+/** \} */
diff --git a/intern/ghost/intern/GHOST_utildefines_variadic.h b/intern/ghost/intern/GHOST_utildefines_variadic.h
new file mode 100644
index 00000000000..4ee306a27b2
--- /dev/null
+++ b/intern/ghost/intern/GHOST_utildefines_variadic.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ */
+
+/* NOTE: copied from `BLI_utildefines_variadic.h` which would be a bad-level include. */
+
+/* Over wrapped args. */
+/* clang-format off */
+
+/* --- internal helpers --- */
+#define _VA_NARGS_GLUE(x, y) x y
+#define _VA_NARGS_RETURN_COUNT(\
+ _1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, _10_, _11_, _12_, _13_, _14_, _15_, _16_, \
+ _17_, _18_, _19_, _20_, _21_, _22_, _23_, _24_, _25_, _26_, _27_, _28_, _29_, _30_, _31_, _32_, \
+ _33_, _34_, _35_, _36_, _37_, _38_, _39_, _40_, _41_, _42_, _43_, _44_, _45_, _46_, _47_, _48_, \
+ _49_, _50_, _51_, _52_, _53_, _54_, _55_, _56_, _57_, _58_, _59_, _60_, _61_, _62_, _63_, _64_, \
+ count, ...) count
+#define _VA_NARGS_EXPAND(args) _VA_NARGS_RETURN_COUNT args
+#define _VA_NARGS_OVERLOAD_MACRO2(name, count) name##count
+#define _VA_NARGS_OVERLOAD_MACRO1(name, count) _VA_NARGS_OVERLOAD_MACRO2(name, count)
+#define _VA_NARGS_OVERLOAD_MACRO(name, count) _VA_NARGS_OVERLOAD_MACRO1(name, count)
+/* --- expose for re-use --- */
+/* 64 args max */
+#define VA_NARGS_COUNT(...) _VA_NARGS_EXPAND((__VA_ARGS__, \
+ 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, \
+ 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, \
+ 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \
+ 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
+#define VA_NARGS_CALL_OVERLOAD(name, ...) \
+ _VA_NARGS_GLUE(_VA_NARGS_OVERLOAD_MACRO(name, VA_NARGS_COUNT(__VA_ARGS__)), (__VA_ARGS__))
+
+/* clang-format on */