From b97039f3f58521ff84e67cfc2dd18b2c0e238ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Sun, 10 Sep 2017 02:55:29 +0200 Subject: [PATCH 01/25] Cleanup --- src/input.c | 123 ++++++++++++++++++++++------------------------ src/window.c | 135 +++++++++++++++++++++++++-------------------------- 2 files changed, 123 insertions(+), 135 deletions(-) diff --git a/src/input.c b/src/input.c index 101b6859b..7d9ff7741 100644 --- a/src/input.c +++ b/src/input.c @@ -354,10 +354,10 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode) return window->stickyKeys; case GLFW_STICKY_MOUSE_BUTTONS: return window->stickyMouseButtons; - default: - _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode); - return 0; } + + _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode); + return 0; } GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value) @@ -367,79 +367,72 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value) _GLFW_REQUIRE_INIT(); - switch (mode) + if (mode == GLFW_CURSOR) { - case GLFW_CURSOR: + if (value != GLFW_CURSOR_NORMAL && + value != GLFW_CURSOR_HIDDEN && + value != GLFW_CURSOR_DISABLED) { - if (value != GLFW_CURSOR_NORMAL && - value != GLFW_CURSOR_HIDDEN && - value != GLFW_CURSOR_DISABLED) - { - _glfwInputError(GLFW_INVALID_ENUM, - "Invalid cursor mode 0x%08X", - value); - return; - } - - if (window->cursorMode == value) - return; - - window->cursorMode = value; - - _glfwPlatformGetCursorPos(window, - &window->virtualCursorPosX, - &window->virtualCursorPosY); - - if (_glfwPlatformWindowFocused(window)) - _glfwPlatformSetCursorMode(window, value); - + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid cursor mode 0x%08X", + value); return; } - case GLFW_STICKY_KEYS: - { - if (window->stickyKeys == value) - return; - - if (!value) - { - int i; - - // Release all sticky keys - for (i = 0; i <= GLFW_KEY_LAST; i++) - { - if (window->keys[i] == _GLFW_STICK) - window->keys[i] = GLFW_RELEASE; - } - } - - window->stickyKeys = value ? GLFW_TRUE : GLFW_FALSE; + if (window->cursorMode == value) return; - } - case GLFW_STICKY_MOUSE_BUTTONS: - { - if (window->stickyMouseButtons == value) - return; + window->cursorMode = value; - if (!value) - { - int i; + _glfwPlatformGetCursorPos(window, + &window->virtualCursorPosX, + &window->virtualCursorPosY); - // Release all sticky mouse buttons - for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) - { - if (window->mouseButtons[i] == _GLFW_STICK) - window->mouseButtons[i] = GLFW_RELEASE; - } - } - - window->stickyMouseButtons = value ? GLFW_TRUE : GLFW_FALSE; - return; - } + if (_glfwPlatformWindowFocused(window)) + _glfwPlatformSetCursorMode(window, value); } + else if (mode == GLFW_STICKY_KEYS) + { + value = value ? GLFW_TRUE : GLFW_FALSE; + if (window->stickyKeys == value) + return; - _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode); + if (!value) + { + int i; + + // Release all sticky keys + for (i = 0; i <= GLFW_KEY_LAST; i++) + { + if (window->keys[i] == _GLFW_STICK) + window->keys[i] = GLFW_RELEASE; + } + } + + window->stickyKeys = value ? GLFW_TRUE : GLFW_FALSE; + } + else if (mode == GLFW_STICKY_MOUSE_BUTTONS) + { + value = value ? GLFW_TRUE : GLFW_FALSE; + if (window->stickyMouseButtons == value) + return; + + if (!value) + { + int i; + + // Release all sticky mouse buttons + for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) + { + if (window->mouseButtons[i] == _GLFW_STICK) + window->mouseButtons[i] = GLFW_RELEASE; + } + } + + window->stickyMouseButtons = value ? GLFW_TRUE : GLFW_FALSE; + } + else + _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode); } GLFWAPI const char* glfwGetKeyName(int key, int scancode) diff --git a/src/window.c b/src/window.c index b7498e1be..5c33217de 100644 --- a/src/window.c +++ b/src/window.c @@ -278,119 +278,118 @@ GLFWAPI void glfwWindowHint(int hint, int value) { case GLFW_RED_BITS: _glfw.hints.framebuffer.redBits = value; - break; + return; case GLFW_GREEN_BITS: _glfw.hints.framebuffer.greenBits = value; - break; + return; case GLFW_BLUE_BITS: _glfw.hints.framebuffer.blueBits = value; - break; + return; case GLFW_ALPHA_BITS: _glfw.hints.framebuffer.alphaBits = value; - break; + return; case GLFW_DEPTH_BITS: _glfw.hints.framebuffer.depthBits = value; - break; + return; case GLFW_STENCIL_BITS: _glfw.hints.framebuffer.stencilBits = value; - break; + return; case GLFW_ACCUM_RED_BITS: _glfw.hints.framebuffer.accumRedBits = value; - break; + return; case GLFW_ACCUM_GREEN_BITS: _glfw.hints.framebuffer.accumGreenBits = value; - break; + return; case GLFW_ACCUM_BLUE_BITS: _glfw.hints.framebuffer.accumBlueBits = value; - break; + return; case GLFW_ACCUM_ALPHA_BITS: _glfw.hints.framebuffer.accumAlphaBits = value; - break; + return; case GLFW_AUX_BUFFERS: _glfw.hints.framebuffer.auxBuffers = value; - break; + return; case GLFW_STEREO: _glfw.hints.framebuffer.stereo = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_DOUBLEBUFFER: _glfw.hints.framebuffer.doublebuffer = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_SAMPLES: _glfw.hints.framebuffer.samples = value; - break; + return; case GLFW_SRGB_CAPABLE: _glfw.hints.framebuffer.sRGB = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_RESIZABLE: _glfw.hints.window.resizable = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_DECORATED: _glfw.hints.window.decorated = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_FOCUSED: _glfw.hints.window.focused = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_AUTO_ICONIFY: _glfw.hints.window.autoIconify = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_FLOATING: _glfw.hints.window.floating = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_MAXIMIZED: _glfw.hints.window.maximized = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_VISIBLE: _glfw.hints.window.visible = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_COCOA_RETINA_FRAMEBUFFER: _glfw.hints.window.ns.retina = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_COCOA_FRAME_AUTOSAVE: _glfw.hints.window.ns.frame = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_COCOA_GRAPHICS_SWITCHING: _glfw.hints.context.nsgl.offline = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_CENTER_CURSOR: _glfw.hints.window.centerCursor = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_CLIENT_API: _glfw.hints.context.client = value; - break; + return; case GLFW_CONTEXT_CREATION_API: _glfw.hints.context.source = value; - break; + return; case GLFW_CONTEXT_VERSION_MAJOR: _glfw.hints.context.major = value; - break; + return; case GLFW_CONTEXT_VERSION_MINOR: _glfw.hints.context.minor = value; - break; + return; case GLFW_CONTEXT_ROBUSTNESS: _glfw.hints.context.robustness = value; - break; + return; case GLFW_OPENGL_FORWARD_COMPAT: _glfw.hints.context.forward = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_OPENGL_DEBUG_CONTEXT: _glfw.hints.context.debug = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_CONTEXT_NO_ERROR: _glfw.hints.context.noerror = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_OPENGL_PROFILE: _glfw.hints.context.profile = value; - break; + return; case GLFW_CONTEXT_RELEASE_BEHAVIOR: _glfw.hints.context.release = value; - break; + return; case GLFW_REFRESH_RATE: _glfw.hints.refreshRate = value; - break; - default: - _glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint 0x%08X", hint); - break; + return; } + + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint 0x%08X", hint); } GLFWAPI void glfwDestroyWindow(GLFWwindow* handle) @@ -770,41 +769,37 @@ GLFWAPI void glfwSetWindowAttrib(GLFWwindow* handle, int attrib, int value) value = value ? GLFW_TRUE : GLFW_FALSE; - switch (attrib) + if (attrib == GLFW_AUTO_ICONIFY) + window->autoIconify = value; + else if (attrib == GLFW_RESIZABLE) { - case GLFW_RESIZABLE: - if (window->resizable != value) - { - window->resizable = value; - if (!window->monitor) - _glfwPlatformSetWindowResizable(window, value); - } + if (window->resizable == value) return; - case GLFW_DECORATED: - if (window->decorated != value) - { - window->decorated = value; - if (!window->monitor) - _glfwPlatformSetWindowDecorated(window, value); - } - return; - - case GLFW_FLOATING: - if (window->floating != value) - { - window->floating = value; - if (!window->monitor) - _glfwPlatformSetWindowFloating(window, value); - } - return; - - case GLFW_AUTO_ICONIFY: - window->autoIconify = value; - return; + window->resizable = value; + if (!window->monitor) + _glfwPlatformSetWindowResizable(window, value); } + else if (attrib == GLFW_DECORATED) + { + if (window->decorated == value) + return; - _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib); + window->decorated = value; + if (!window->monitor) + _glfwPlatformSetWindowDecorated(window, value); + } + else if (attrib == GLFW_FLOATING) + { + if (window->floating == value) + return; + + window->floating = value; + if (!window->monitor) + _glfwPlatformSetWindowFloating(window, value); + } + else + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib); } GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* handle) From d099181307253a22a57b7932ab32df02e196035f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Sun, 10 Sep 2017 21:02:26 +0200 Subject: [PATCH 02/25] Add library name override macros --- docs/compile.dox | 6 ++++++ src/egl_context.c | 16 ++++++++++++---- src/glx_context.c | 4 +++- src/osmesa_context.c | 4 +++- src/vulkan.c | 4 +++- 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/docs/compile.dox b/docs/compile.dox index 75f938cac..35d657324 100644 --- a/docs/compile.dox +++ b/docs/compile.dox @@ -265,6 +265,12 @@ If you are linking the Vulkan loader statically into your application then you must also define @b _GLFW_VULKAN_STATIC. Otherwise, GLFW will attempt to use the external version. +If you are using a custom name for the Vulkan, EGL, GLX, OSMesa, OpenGL, GLESv1 +or GLESv2 library, you can override the default names by defining those you need +of @b _GLFW_VULKAN_LIBRARY, @b _GLFW_EGL_LIBRARY, @b _GLFW_GLX_LIBRARY, @b +_GLFW_OSMESA_LIBRARY, @b _GLFW_OPENGL_LIBRARY, @b _GLFW_GLESV1_LIBRARY and @b +_GLFW_GLESV2_LIBRARY. Otherwise, GLFW will use the built-in default names. + For the EGL context creation API, the following options are available: - @b _GLFW_USE_EGLPLATFORM_H to use `EGL/eglplatform.h` for native handle diff --git a/src/egl_context.c b/src/egl_context.c index d1bbbf08d..54257f25d 100644 --- a/src/egl_context.c +++ b/src/egl_context.c @@ -286,7 +286,9 @@ GLFWbool _glfwInitEGL(void) int i; const char* sonames[] = { -#if defined(_GLFW_WIN32) +#if defined(_GLFW_EGL_LIBRARY) + _GLFW_EGL_LIBRARY, +#elif defined(_GLFW_WIN32) "libEGL.dll", "EGL.dll", #elif defined(_GLFW_COCOA) @@ -601,7 +603,9 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, const char** sonames; const char* es1sonames[] = { -#if defined(_GLFW_WIN32) +#if defined(_GLFW_GLESV1_LIBRARY) + _GLFW_GLESV1_LIBRARY, +#elif defined(_GLFW_WIN32) "GLESv1_CM.dll", "libGLES_CM.dll", #elif defined(_GLFW_COCOA) @@ -614,7 +618,9 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, }; const char* es2sonames[] = { -#if defined(_GLFW_WIN32) +#if defined(_GLFW_GLESV2_LIBRARY) + _GLFW_GLESV2_LIBRARY, +#elif defined(_GLFW_WIN32) "GLESv2.dll", "libGLESv2.dll", #elif defined(_GLFW_COCOA) @@ -628,7 +634,9 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, }; const char* glsonames[] = { -#if defined(_GLFW_WIN32) +#if defined(_GLFW_OPENGL_LIBRARY) + _GLFW_OPENGL_LIBRARY, +#elif defined(_GLFW_WIN32) #elif defined(_GLFW_COCOA) #else "libGL.so.1", diff --git a/src/glx_context.c b/src/glx_context.c index dbac4ea77..e545fa0fe 100644 --- a/src/glx_context.c +++ b/src/glx_context.c @@ -244,7 +244,9 @@ GLFWbool _glfwInitGLX(void) int i; const char* sonames[] = { -#if defined(__CYGWIN__) +#if defined(_GLFW_GLX_LIBRARY) + _GLFW_GLX_LIBRARY, +#elif defined(__CYGWIN__) "libGL-1.so", #else "libGL.so.1", diff --git a/src/osmesa_context.c b/src/osmesa_context.c index 46102fbab..a7de33f26 100644 --- a/src/osmesa_context.c +++ b/src/osmesa_context.c @@ -113,7 +113,9 @@ GLFWbool _glfwInitOSMesa(void) int i; const char* sonames[] = { -#if defined(_WIN32) +#if defined(_GLFW_OSMESA_LIBRARY) + _GLFW_OSMESA_LIBRARY, +#elif defined(_WIN32) "libOSMesa.dll", "OSMesa.dll", #elif defined(__APPLE__) diff --git a/src/vulkan.c b/src/vulkan.c index f2473cf6e..1fd15fad2 100644 --- a/src/vulkan.c +++ b/src/vulkan.c @@ -49,7 +49,9 @@ GLFWbool _glfwInitVulkan(int mode) return GLFW_TRUE; #if !defined(_GLFW_VULKAN_STATIC) -#if defined(_GLFW_WIN32) +#if defined(_GLFW_VULKAN_LIBRARY) + _glfw.vk.handle = _glfw_dlopen(_GLFW_VULKAN_LIBRARY); +#elif defined(_GLFW_WIN32) _glfw.vk.handle = _glfw_dlopen("vulkan-1.dll"); #elif defined(_GLFW_COCOA) _glfw.vk.handle = _glfw_dlopen("libMoltenVK.dylib"); From 1955c37c48105836e30257ec21f56b33c4a88ce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Sun, 10 Sep 2017 21:03:17 +0200 Subject: [PATCH 03/25] Documentation work --- docs/header.html | 1 - docs/intro.dox | 53 +++++++++++++++++++++++++----------------------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/docs/header.html b/docs/header.html index 9759d8b02..16b09a721 100644 --- a/docs/header.html +++ b/docs/header.html @@ -25,7 +25,6 @@ $extrastylesheet diff --git a/docs/intro.dox b/docs/intro.dox index e0e7e8aeb..65aeef68b 100644 --- a/docs/intro.dox +++ b/docs/intro.dox @@ -82,6 +82,9 @@ Some hints are platform specific. These may be set on any platform but they will only affect their specific platform. Other platforms will simply ignore them. Setting these hints requires no platform specific headers or functions. + +@subsubsection init_hints_shared Shared init hints + @anchor GLFW_JOYSTICK_HAT_BUTTONS __GLFW_JOYSTICK_HAT_BUTTONS__ specifies whether to also expose joystick hats as buttons, for compatibility with earlier versions of GLFW that did not have @ref @@ -299,35 +302,39 @@ functions not on this list will not be made non-reentrant. @subsection thread_safety Thread safety -Most GLFW functions must only be called from the main thread, but some may be -called from any thread. However, no GLFW function may be called from any thread -but the main thread until GLFW has been successfully initialized, including -functions that may called before initialization. +Most GLFW functions must only be called from the main thread (the thread that +calls main), but some may be called from any thread once the library has been +initialized. Before initialization the whole library is thread-unsafe. The reference documentation for every GLFW function states whether it is limited to the main thread. -Initialization and termination, event processing and the creation and -destruction of windows, contexts and cursors are all limited to the main thread -due to limitations of one or several platforms. +Initialization, termination, event processing and the creation and +destruction of windows, cursors and OpenGL and OpenGL ES contexts are all +restricted to the main thread due to limitations of one or several platforms. Because event processing must be performed on the main thread, all callbacks except for the error callback will only be called on that thread. The error callback may be called on any thread, as any GLFW function may generate errors. -The posting of empty events may be done from any thread. The window user -pointer and close flag may also be accessed and modified from any thread, but -this is not synchronized by GLFW. The following window related functions may -be called from any thread: +The error code and description may be queried from any thread. + + - @ref glfwGetError + +Empty events may be posted from any thread. - @ref glfwPostEmptyEvent + +The window user pointer and close flag may be read and written from any thread, +but this is not synchronized by GLFW. + - @ref glfwGetWindowUserPointer - @ref glfwSetWindowUserPointer - @ref glfwWindowShouldClose - @ref glfwSetWindowShouldClose -Rendering may be done on any thread. The following context related functions -may be called from any thread: +These functions for working with OpenGL and OpenGL ES contexts may be called +from any thread, but the window object is not synchronized by GLFW. - @ref glfwMakeContextCurrent - @ref glfwGetCurrentContext @@ -336,27 +343,23 @@ may be called from any thread: - @ref glfwExtensionSupported - @ref glfwGetProcAddress -The raw timer may be queried from any thread. The following raw timer related -functions may be called from any thread: +The raw timer functions may be called from any thread. - @ref glfwGetTimerFrequency - @ref glfwGetTimerValue -The regular timer may be used from any thread, but the reading and writing of -the timer offset is not synchronized by GLFW. The following timer related -functions may be called from any thread: +The regular timer may be used from any thread, but reading and writing the timer +offset is not synchronized by GLFW. - @ref glfwGetTime - @ref glfwSetTime -Library version information may be queried from any thread. The following -version related functions may be called from any thread: +Library version information may be queried from any thread. - @ref glfwGetVersion - @ref glfwGetVersionString -Vulkan objects may be created and information queried from any thread. The -following Vulkan related functions may be called from any thread: +All Vulkan related functions may be called from any thread. - @ref glfwVulkanSupported - @ref glfwGetRequiredInstanceExtensions @@ -364,9 +367,9 @@ following Vulkan related functions may be called from any thread: - @ref glfwGetPhysicalDevicePresentationSupport - @ref glfwCreateWindowSurface -GLFW uses no synchronization objects internally except for thread-local storage -to keep track of the current context for each thread. Synchronization is left -to the application. +GLFW uses synchronization objects internally only to manage the per-thread +context and error states. Additional synchronization is left to the +application. Functions that may currently be called from any thread will always remain so, but functions that are currently limited to the main thread may be updated to From f8668c5a9fcec29adbfc6c2e096c88f1c985ce09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 7 Feb 2017 03:29:57 +0100 Subject: [PATCH 04/25] Win32: Fix key names not matching other platforms This brings printable key names more in line with the results provided on other platforms. Fixes #943. --- README.md | 1 + src/win32_init.c | 55 ++++++++++++++++++++++++++++++++++++++++++++ src/win32_platform.h | 3 ++- src/win32_window.c | 21 ++++++----------- 4 files changed, 65 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index f7d5d67fb..52349e486 100644 --- a/README.md +++ b/README.md @@ -193,6 +193,7 @@ information on what to include when reporting a bug. - [Win32] Bugfix: `glfw3native.h` would undefine a foreign `APIENTRY` (#1062) - [Win32] Bugfix: Disabled cursor mode prevented use of caption buttons (#650,#1071) +- [Win32] Bugfix: Returned key names did not match other platforms (#943) - [X11] Moved to XI2 `XI_RawMotion` for disable cursor mode motion input (#125) - [X11] Replaced `_GLFW_HAS_XF86VM` compile-time option with dynamic loading - [X11] Bugfix: `glfwGetVideoMode` would segfault on Cygwin/X diff --git a/src/win32_init.c b/src/win32_init.c index 732ee8677..392114d00 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -429,6 +429,60 @@ void _glfwInputErrorWin32(int error, const char* description) _glfwInputError(error, "%s: %s", description, message); } +// Updates key names according to the current keyboard layout +// +void _glfwUpdateKeyNamesWin32(void) +{ + int key; + BYTE state[256] = {0}; + + memset(_glfw.win32.keynames, 0, sizeof(_glfw.win32.keynames)); + + for (key = GLFW_KEY_SPACE; key <= GLFW_KEY_LAST; key++) + { + UINT vk; + int scancode, length; + WCHAR chars[16]; + + scancode = _glfw.win32.scancodes[key]; + if (scancode == -1) + continue; + + if (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_ADD) + { + const UINT vks[] = { + VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, + VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, + VK_NUMPAD8, VK_NUMPAD9, VK_DECIMAL, VK_DIVIDE, + VK_MULTIPLY, VK_SUBTRACT, VK_ADD + }; + + vk = vks[key - GLFW_KEY_KP_0]; + } + else + vk = MapVirtualKey(scancode, MAPVK_VSC_TO_VK); + + length = ToUnicode(vk, scancode, state, + chars, sizeof(chars) / sizeof(WCHAR), + 0); + + if (length == -1) + { + length = ToUnicode(vk, scancode, state, + chars, sizeof(chars) / sizeof(WCHAR), + 0); + } + + if (length < 1) + continue; + + WideCharToMultiByte(CP_UTF8, 0, chars, 1, + _glfw.win32.keynames[key], + sizeof(_glfw.win32.keynames[key]), + NULL, NULL); + } +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// @@ -448,6 +502,7 @@ int _glfwPlatformInit(void) return GLFW_FALSE; createKeyTables(); + _glfwUpdateKeyNamesWin32(); if (_glfw_SetProcessDpiAwareness) _glfw_SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); diff --git a/src/win32_platform.h b/src/win32_platform.h index 233510efe..d05722267 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -250,9 +250,9 @@ typedef struct _GLFWlibraryWin32 DWORD foregroundLockTimeout; int acquiredMonitorCount; char* clipboardString; - char keyName[64]; short int keycodes[512]; short int scancodes[GLFW_KEY_LAST + 1]; + char keynames[GLFW_KEY_LAST + 1][5]; // Where to place the cursor when re-enabled double restoreCursorPosX, restoreCursorPosY; // The window whose disabled cursor mode is active @@ -353,6 +353,7 @@ void _glfwUnregisterWindowClassWin32(void); WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source); char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source); void _glfwInputErrorWin32(int error, const char* description); +void _glfwUpdateKeyNamesWin32(void); void _glfwInitTimerWin32(void); diff --git a/src/win32_window.c b/src/win32_window.c index 2b4ddeded..aa7efc843 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -584,6 +584,12 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, return 0; } + case WM_INPUTLANGCHANGE: + { + _glfwUpdateKeyNamesWin32(); + break; + } + case WM_CHAR: case WM_SYSCHAR: case WM_UNICHAR: @@ -1652,20 +1658,7 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) const char* _glfwPlatformGetScancodeName(int scancode) { - WCHAR name[16]; - - if (!GetKeyNameTextW(scancode << 16, name, sizeof(name) / sizeof(WCHAR))) - return NULL; - - if (!WideCharToMultiByte(CP_UTF8, 0, name, -1, - _glfw.win32.keyName, - sizeof(_glfw.win32.keyName), - NULL, NULL)) - { - return NULL; - } - - return _glfw.win32.keyName; + return _glfw.win32.keynames[_glfw.win32.keycodes[scancode]]; } int _glfwPlatformGetKeyScancode(int key) From 7f0d5e0a0305d94278f19782d1ba0b49aaafdfd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 13 Sep 2017 17:47:49 +0200 Subject: [PATCH 05/25] Win32: Fix text conversion size semantics --- src/win32_init.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/win32_init.c b/src/win32_init.c index 392114d00..5a287076c 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -357,19 +357,19 @@ static HWND createHelperWindow(void) WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source) { WCHAR* target; - int length; + int count; - length = MultiByteToWideChar(CP_UTF8, 0, source, -1, NULL, 0); - if (!length) + count = MultiByteToWideChar(CP_UTF8, 0, source, -1, NULL, 0); + if (!count) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to convert string from UTF-8"); return NULL; } - target = calloc(length, sizeof(WCHAR)); + target = calloc(count, sizeof(WCHAR)); - if (!MultiByteToWideChar(CP_UTF8, 0, source, -1, target, length)) + if (!MultiByteToWideChar(CP_UTF8, 0, source, -1, target, count)) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to convert string from UTF-8"); @@ -385,19 +385,19 @@ WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source) char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source) { char* target; - int length; + int size; - length = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL); - if (!length) + size = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL); + if (!size) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to convert string to UTF-8"); return NULL; } - target = calloc(length, 1); + target = calloc(size, 1); - if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, length, NULL, NULL)) + if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, size, NULL, NULL)) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to convert string to UTF-8"); From 4637c31d822c5ced34c8550488e2555054284721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 13 Sep 2017 23:19:29 +0200 Subject: [PATCH 06/25] Win32: Remove unused header --- src/win32_platform.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/win32_platform.h b/src/win32_platform.h index d05722267..63aaa94dc 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -63,7 +63,6 @@ #include #include -#include #include #include #include From 16ae02ab85fc184c20e536b3f8e33be07dd6e39c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 14 Sep 2017 20:16:25 +0200 Subject: [PATCH 07/25] Add CMake target for updating gamepad mappings This adds the 'mappings' build target that downloads the upstream gamecontrollerdb.txt file and regenerates the mappings.h header. Related to #900. --- CMake/GenerateMappings.cmake | 33 +++++++++++++++++ README.md | 1 + src/mappings.h | 11 ++++++ src/mappings.h.in | 70 ++++++++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+) create mode 100644 CMake/GenerateMappings.cmake create mode 100644 src/mappings.h.in diff --git a/CMake/GenerateMappings.cmake b/CMake/GenerateMappings.cmake new file mode 100644 index 000000000..9fb304e87 --- /dev/null +++ b/CMake/GenerateMappings.cmake @@ -0,0 +1,33 @@ +# Usage: +# cmake -P GenerateMappings.cmake + +set(source_url "https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt") +set(source_path "${CMAKE_CURRENT_BINARY_DIR}/gamecontrollerdb.txt") +set(template_path "${CMAKE_ARGV3}") +set(target_path "${CMAKE_ARGV4}") + +if (NOT EXISTS "${template_path}") + message(FATAL_ERROR "Failed to find template file ${template_path}") +endif() + +file(DOWNLOAD "${source_url}" "${source_path}" + STATUS download_status + TLS_VERIFY on) + +list(GET download_status 0 status_code) +list(GET download_status 1 status_message) + +if (status_code) + message(FATAL_ERROR "Failed to download ${source_url}: ${status_message}") +endif() + +file(STRINGS "${source_path}" lines) +foreach(line ${lines}) + if ("${line}" MATCHES "^[0-9a-fA-F].*$") + set(GLFW_GAMEPAD_MAPPINGS "${GLFW_GAMEPAD_MAPPINGS}\"${line}\\n\"\n") + endif() +endforeach() + +configure_file("${template_path}" "${target_path}" @ONLY NEWLINE_STYLE UNIX) +file(REMOVE "${source_path}") + diff --git a/README.md b/README.md index 52349e486..5c5fe4737 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,7 @@ information on what to include when reporting a bug. - Added `GLFW_INCLUDE_ES32` for including the OpenGL ES 3.2 header - Added `GLFW_OSMESA_CONTEXT_API` for creating OpenGL contexts with [OSMesa](https://www.mesa3d.org/osmesa.html) (#281) +- Added `GenerateMappings.cmake` script for updating gamepad mappings - Removed `GLFW_USE_RETINA` compile-time option - Removed `GLFW_USE_CHDIR` compile-time option - Removed `GLFW_USE_MENUBAR` compile-time option diff --git a/src/mappings.h b/src/mappings.h index 2141e520d..6c1b800d7 100644 --- a/src/mappings.h +++ b/src/mappings.h @@ -23,6 +23,16 @@ // distribution. // //======================================================================== +// As mappings.h.in, this file is used by CMake to produce the mappings.h +// header file. If you are adding a GLFW specific gamepad mapping, this is +// where to put it. +//======================================================================== +// As mappings.h, this provides all pre-defined gamepad mappings, including +// all available in SDL_GameControllerDB. Do not edit this file. Any gamepad +// mappings not specific to GLFW should be submitted to SDL_GameControllerDB. +// This file can be re-generated from mappings.h.in and the upstream +// gamecontrollerdb.txt with the GenerateMappings.cmake script. +//======================================================================== // All gamepad mappings not labeled GLFW are copied from the // SDL_GameControllerDB project under the following license: @@ -220,6 +230,7 @@ const char* _glfwDefaultMappings = "03000000100800000300000010010000,USB Gamepad,platform:Linux,a:b2,b:b1,x:b3,y:b0,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,\n" "05000000ac0500003232000001000000,VR-BOX,platform:Linux,a:b0,b:b1,x:b2,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,\n" "03000000780000000600000010010000,Microntek USB Joystick,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftx:a0,lefty:a1,\n" + "78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,\n" "78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,\n" "78696e70757403000000000000000000,XInput Arcade Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,\n" diff --git a/src/mappings.h.in b/src/mappings.h.in new file mode 100644 index 000000000..bf7206701 --- /dev/null +++ b/src/mappings.h.in @@ -0,0 +1,70 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2006-2016 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// As mappings.h.in, this file is used by CMake to produce the mappings.h +// header file. If you are adding a GLFW specific gamepad mapping, this is +// where to put it. +//======================================================================== +// As mappings.h, this provides all pre-defined gamepad mappings, including +// all available in SDL_GameControllerDB. Do not edit this file. Any gamepad +// mappings not specific to GLFW should be submitted to SDL_GameControllerDB. +// This file can be re-generated from mappings.h.in and the upstream +// gamecontrollerdb.txt with the GenerateMappings.cmake script. +//======================================================================== + +// All gamepad mappings not labeled GLFW are copied from the +// SDL_GameControllerDB project under the following license: +// +// Simple DirectMedia Layer +// Copyright (C) 1997-2013 Sam Lantinga +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the +// use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. + +const char* _glfwDefaultMappings = +@GLFW_GAMEPAD_MAPPINGS@ +"78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,\n" +"78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,\n" +"78696e70757403000000000000000000,XInput Arcade Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,\n" +"78696e70757404000000000000000000,XInput Flight Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,\n" +"78696e70757405000000000000000000,XInput Dance Pad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,\n" +"78696e70757406000000000000000000,XInput Guitar (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,\n" +"78696e70757408000000000000000000,XInput Drum Kit (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,\n"; + From f30acd8f74adf0b26b18bbbf627b87d20c8c9b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 6 Sep 2017 04:30:18 +0200 Subject: [PATCH 08/25] Add OSMesa to context API list --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5c5fe4737..bc28c6987 100644 --- a/README.md +++ b/README.md @@ -45,8 +45,8 @@ guide](http://www.glfw.org/docs/latest/moving.html) for moving to the GLFW ## Compiling GLFW GLFW itself requires only the headers and libraries for your window system. It -does not need the headers for any context creation API (WGL, GLX, EGL, NSGL) or -rendering API (OpenGL, OpenGL ES, Vulkan) to enable support for them. +does not need the headers for any context creation API (WGL, GLX, EGL, NSGL, +OSMesa) or rendering API (OpenGL, OpenGL ES, Vulkan) to enable support for them. GLFW supports compilation on Windows with Visual C++ 2010 and later, MinGW and MinGW-w64, on macOS with Clang and on Linux and other Unix-like systems with GCC From f7dc6df02c9769e6d1c5cf7926481e7a831ce1d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 7 Aug 2017 23:23:37 +0200 Subject: [PATCH 09/25] X11: Add support for reading clipboard via INCR This allows glfwGetClipboardString to retrieve clipboard contents larger than (typically) 2^18 bytes. Related to #275. --- README.md | 1 + src/x11_init.c | 1 + src/x11_platform.h | 1 + src/x11_window.c | 98 +++++++++++++++++++++++++++++++++++++--------- 4 files changed, 83 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index bc28c6987..93c5c2c85 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,7 @@ information on what to include when reporting a bug. - [X11] Bugfix: IM-duplicated key events would leak at low polling rates (#747) - [X11] Bugfix: Gamma ramp setting via RandR did not validate ramp size - [X11] Bugfix: Key name string encoding depended on current locale (#981,#983) +- [X11] Bugfix: Incremental reading of selections was not supported (#275) - [Linux] Moved to evdev for joystick input (#906,#1005) - [Linux] Bugfix: Event processing did not detect joystick disconnection (#932) - [Linux] Bugfix: The joystick device path could be truncated (#1025) diff --git a/src/x11_init.c b/src/x11_init.c index c4e84474f..baf76fa40 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -675,6 +675,7 @@ static GLFWbool initExtensions(void) _glfw.x11.TARGETS = XInternAtom(_glfw.x11.display, "TARGETS", False); _glfw.x11.MULTIPLE = XInternAtom(_glfw.x11.display, "MULTIPLE", False); _glfw.x11.PRIMARY = XInternAtom(_glfw.x11.display, "PRIMARY", False); + _glfw.x11.INCR = XInternAtom(_glfw.x11.display, "INCR", False); _glfw.x11.CLIPBOARD = XInternAtom(_glfw.x11.display, "CLIPBOARD", False); // Clipboard manager atoms diff --git a/src/x11_platform.h b/src/x11_platform.h index 48d15bc92..39eaec056 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -265,6 +265,7 @@ typedef struct _GLFWlibraryX11 // Selection (clipboard) atoms Atom TARGETS; Atom MULTIPLE; + Atom INCR; Atom CLIPBOARD; Atom PRIMARY; Atom CLIPBOARD_MANAGER; diff --git a/src/x11_window.c b/src/x11_window.c index aee89d402..592a8eb75 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -164,6 +164,17 @@ static Bool isFrameExtentsEvent(Display* display, XEvent* event, XPointer pointe event->xproperty.atom == _glfw.x11.NET_FRAME_EXTENTS; } +// Returns whether it is a property event for the specified selection transfer +// +static Bool isSelPropNewValueNotify(Display* display, XEvent* event, XPointer pointer) +{ + XEvent* notification = (XEvent*) pointer; + return event->type == PropertyNotify && + event->xproperty.state == PropertyNewValue && + event->xproperty.window == notification->xselection.requestor && + event->xproperty.atom == notification->xselection.property; +} + // Translates a GLFW standard cursor to a font cursor shape // static int translateCursorShape(int shape) @@ -841,10 +852,10 @@ static const char* getSelectionString(Atom selection) { size_t i; char** selectionString = NULL; - const Atom formats[] = { _glfw.x11.UTF8_STRING, + const Atom targets[] = { _glfw.x11.UTF8_STRING, _glfw.x11.COMPOUND_STRING, XA_STRING }; - const size_t formatCount = sizeof(formats) / sizeof(formats[0]); + const size_t targetCount = sizeof(targets) / sizeof(targets[0]); if (selection == _glfw.x11.PRIMARY) selectionString = &_glfw.x11.primarySelectionString; @@ -862,14 +873,17 @@ static const char* getSelectionString(Atom selection) free(*selectionString); *selectionString = NULL; - for (i = 0; i < formatCount; i++) + for (i = 0; i < targetCount; i++) { char* data; - XEvent event; + Atom actualType; + int actualFormat; + unsigned long itemCount, bytesAfter; + XEvent notification, dummy; XConvertSelection(_glfw.x11.display, selection, - formats[i], + targets[i], _glfw.x11.GLFW_SELECTION, _glfw.x11.helperWindowHandle, CurrentTime); @@ -877,28 +891,76 @@ static const char* getSelectionString(Atom selection) while (!XCheckTypedWindowEvent(_glfw.x11.display, _glfw.x11.helperWindowHandle, SelectionNotify, - &event)) + ¬ification)) { waitForEvent(NULL); } - if (event.xselection.property == None) + if (notification.xselection.property == None) continue; - if (_glfwGetWindowPropertyX11(event.xselection.requestor, - event.xselection.property, - event.xselection.target, - (unsigned char**) &data)) + XCheckIfEvent(_glfw.x11.display, + &dummy, + isSelPropNewValueNotify, + (XPointer) ¬ification); + + XGetWindowProperty(_glfw.x11.display, + notification.xselection.requestor, + notification.xselection.property, + 0, + LONG_MAX, + True, + AnyPropertyType, + &actualType, + &actualFormat, + &itemCount, + &bytesAfter, + (unsigned char**) &data); + + if (actualType == _glfw.x11.INCR) { - *selectionString = strdup(data); + size_t size = 1; + + for (;;) + { + while (!XCheckIfEvent(_glfw.x11.display, + &dummy, + isSelPropNewValueNotify, + (XPointer) ¬ification)) + { + waitForEvent(NULL); + } + + XFree(data); + XGetWindowProperty(_glfw.x11.display, + notification.xselection.requestor, + notification.xselection.property, + 0, + LONG_MAX, + True, + AnyPropertyType, + &actualType, + &actualFormat, + &itemCount, + &bytesAfter, + (unsigned char**) &data); + + if (itemCount) + { + size += itemCount; + *selectionString = realloc(*selectionString, size); + (*selectionString)[size - itemCount - 1] = '\0'; + strcat(*selectionString, data); + } + + if (!itemCount) + break; + } } + else if (actualType == targets[i]) + *selectionString = strdup(data); - if (data) - XFree(data); - - XDeleteProperty(_glfw.x11.display, - event.xselection.requestor, - event.xselection.property); + XFree(data); if (*selectionString) break; From 9dbc935afb5b9fa8977e1d6f5c844038ece7fa73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 8 Aug 2017 14:04:51 +0200 Subject: [PATCH 10/25] X11: Stop reporting support for COMPOUND_TEXT --- README.md | 1 + src/x11_init.c | 5 +---- src/x11_window.c | 9 ++------- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 93c5c2c85..4ca7a0bec 100644 --- a/README.md +++ b/README.md @@ -206,6 +206,7 @@ information on what to include when reporting a bug. - [X11] Bugfix: Gamma ramp setting via RandR did not validate ramp size - [X11] Bugfix: Key name string encoding depended on current locale (#981,#983) - [X11] Bugfix: Incremental reading of selections was not supported (#275) +- [X11] Bugfix: Selection I/O reported but did not support `COMPOUND_TEXT` - [Linux] Moved to evdev for joystick input (#906,#1005) - [Linux] Bugfix: Event processing did not detect joystick disconnection (#932) - [Linux] Bugfix: The joystick device path could be truncated (#1025) diff --git a/src/x11_init.c b/src/x11_init.c index baf76fa40..6a8ba6597 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -661,10 +661,7 @@ static GLFWbool initExtensions(void) // String format atoms _glfw.x11.NULL_ = XInternAtom(_glfw.x11.display, "NULL", False); - _glfw.x11.UTF8_STRING = - XInternAtom(_glfw.x11.display, "UTF8_STRING", False); - _glfw.x11.COMPOUND_STRING = - XInternAtom(_glfw.x11.display, "COMPOUND_STRING", False); + _glfw.x11.UTF8_STRING = XInternAtom(_glfw.x11.display, "UTF8_STRING", False); _glfw.x11.ATOM_PAIR = XInternAtom(_glfw.x11.display, "ATOM_PAIR", False); // Custom selection property atom diff --git a/src/x11_window.c b/src/x11_window.c index 592a8eb75..a0dd81be7 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -683,9 +683,7 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request) { int i; char* selectionString = NULL; - const Atom formats[] = { _glfw.x11.UTF8_STRING, - _glfw.x11.COMPOUND_STRING, - XA_STRING }; + const Atom formats[] = { _glfw.x11.UTF8_STRING, XA_STRING }; const int formatCount = sizeof(formats) / sizeof(formats[0]); if (request->selection == _glfw.x11.PRIMARY) @@ -707,7 +705,6 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request) const Atom targets[] = { _glfw.x11.TARGETS, _glfw.x11.MULTIPLE, _glfw.x11.UTF8_STRING, - _glfw.x11.COMPOUND_STRING, XA_STRING }; XChangeProperty(_glfw.x11.display, @@ -852,9 +849,7 @@ static const char* getSelectionString(Atom selection) { size_t i; char** selectionString = NULL; - const Atom targets[] = { _glfw.x11.UTF8_STRING, - _glfw.x11.COMPOUND_STRING, - XA_STRING }; + const Atom targets[] = { _glfw.x11.UTF8_STRING, XA_STRING }; const size_t targetCount = sizeof(targets) / sizeof(targets[0]); if (selection == _glfw.x11.PRIMARY) From 0b5023bc6269fbca6507f246f28ad37d7d1d040e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 8 Aug 2017 16:25:13 +0200 Subject: [PATCH 11/25] X11: Fix Latin-1 text not being converted to UTF-8 --- README.md | 1 + src/x11_window.c | 155 +++++++++++++++++++++++++++++------------------ 2 files changed, 96 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index 4ca7a0bec..40e9544f4 100644 --- a/README.md +++ b/README.md @@ -207,6 +207,7 @@ information on what to include when reporting a bug. - [X11] Bugfix: Key name string encoding depended on current locale (#981,#983) - [X11] Bugfix: Incremental reading of selections was not supported (#275) - [X11] Bugfix: Selection I/O reported but did not support `COMPOUND_TEXT` +- [X11] Bugfix: Latin-1 text read from selections was not converted to UTF-8 - [Linux] Moved to evdev for joystick input (#906,#1005) - [Linux] Bugfix: Event processing did not detect joystick disconnection (#932) - [Linux] Bugfix: The joystick device path could be truncated (#1025) diff --git a/src/x11_window.c b/src/x11_window.c index a0dd81be7..ea49e55d0 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -460,6 +460,81 @@ static char** parseUriList(char* text, int* count) return paths; } +// Encode a Unicode code point to a UTF-8 stream +// Based on cutef8 by Jeff Bezanson (Public Domain) +// +static size_t encodeUTF8(char* s, unsigned int ch) +{ + size_t count = 0; + + if (ch < 0x80) + s[count++] = (char) ch; + else if (ch < 0x800) + { + s[count++] = (ch >> 6) | 0xc0; + s[count++] = (ch & 0x3f) | 0x80; + } + else if (ch < 0x10000) + { + s[count++] = (ch >> 12) | 0xe0; + s[count++] = ((ch >> 6) & 0x3f) | 0x80; + s[count++] = (ch & 0x3f) | 0x80; + } + else if (ch < 0x110000) + { + s[count++] = (ch >> 18) | 0xf0; + s[count++] = ((ch >> 12) & 0x3f) | 0x80; + s[count++] = ((ch >> 6) & 0x3f) | 0x80; + s[count++] = (ch & 0x3f) | 0x80; + } + + return count; +} + +// Decode a Unicode code point from a UTF-8 stream +// Based on cutef8 by Jeff Bezanson (Public Domain) +// +#if defined(X_HAVE_UTF8_STRING) +static unsigned int decodeUTF8(const char** s) +{ + unsigned int ch = 0, count = 0; + static const unsigned int offsets[] = + { + 0x00000000u, 0x00003080u, 0x000e2080u, + 0x03c82080u, 0xfa082080u, 0x82082080u + }; + + do + { + ch = (ch << 6) + (unsigned char) **s; + (*s)++; + count++; + } while ((**s & 0xc0) == 0x80); + + assert(count <= 6); + return ch - offsets[count - 1]; +} +#endif /*X_HAVE_UTF8_STRING*/ + +// Convert the specified Latin-1 string to UTF-8 +// +static char* convertLatin1toUTF8(const char* source) +{ + size_t size = 1; + const char* sp; + + for (sp = source; *sp; sp++) + size += (*sp & 0x80) ? 2 : 1; + + char* target = calloc(size, 1); + char* tp = target; + + for (sp = source; *sp; sp++) + tp += encodeUTF8(tp, *sp); + + return target; +} + // Centers the cursor over the window client area // static void centerCursor(_GLFWwindow* window) @@ -915,6 +990,7 @@ static const char* getSelectionString(Atom selection) if (actualType == _glfw.x11.INCR) { size_t size = 1; + char* string = NULL; for (;;) { @@ -943,17 +1019,32 @@ static const char* getSelectionString(Atom selection) if (itemCount) { size += itemCount; - *selectionString = realloc(*selectionString, size); - (*selectionString)[size - itemCount - 1] = '\0'; - strcat(*selectionString, data); + string = realloc(string, size); + string[size - itemCount - 1] = '\0'; + strcat(string, data); } if (!itemCount) + { + if (targets[i] == XA_STRING) + { + *selectionString = convertLatin1toUTF8(string); + free(string); + } + else + *selectionString = string; + break; + } } } else if (actualType == targets[i]) - *selectionString = strdup(data); + { + if (targets[i] == XA_STRING) + *selectionString = convertLatin1toUTF8(data); + else + *selectionString = strdup(data); + } XFree(data); @@ -1035,62 +1126,6 @@ static void releaseMonitor(_GLFWwindow* window) } } -// Encode a Unicode code point to a UTF-8 stream -// Based on cutef8 by Jeff Bezanson (Public Domain) -// -static size_t encodeUTF8(char* s, unsigned int ch) -{ - size_t count = 0; - - if (ch < 0x80) - s[count++] = (char) ch; - else if (ch < 0x800) - { - s[count++] = (ch >> 6) | 0xc0; - s[count++] = (ch & 0x3f) | 0x80; - } - else if (ch < 0x10000) - { - s[count++] = (ch >> 12) | 0xe0; - s[count++] = ((ch >> 6) & 0x3f) | 0x80; - s[count++] = (ch & 0x3f) | 0x80; - } - else if (ch < 0x110000) - { - s[count++] = (ch >> 18) | 0xf0; - s[count++] = ((ch >> 12) & 0x3f) | 0x80; - s[count++] = ((ch >> 6) & 0x3f) | 0x80; - s[count++] = (ch & 0x3f) | 0x80; - } - - return count; -} - -// Decode a Unicode code point from a UTF-8 stream -// Based on cutef8 by Jeff Bezanson (Public Domain) -// -#if defined(X_HAVE_UTF8_STRING) -static unsigned int decodeUTF8(const char** s) -{ - unsigned int ch = 0, count = 0; - static const unsigned int offsets[] = - { - 0x00000000u, 0x00003080u, 0x000e2080u, - 0x03c82080u, 0xfa082080u, 0x82082080u - }; - - do - { - ch = (ch << 6) + (unsigned char) **s; - (*s)++; - count++; - } while ((**s & 0xc0) == 0x80); - - assert(count <= 6); - return ch - offsets[count - 1]; -} -#endif /*X_HAVE_UTF8_STRING*/ - // Process the specified X event // static void processEvent(XEvent *event) From 3d110d2e1bc5994b90531feada3bd8552959d1c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Sun, 17 Sep 2017 13:54:17 +0200 Subject: [PATCH 12/25] X11: Fix selection error nomenclature [ci skip] --- src/x11_window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/x11_window.c b/src/x11_window.c index ea49e55d0..e07fea779 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -1055,7 +1055,7 @@ static const char* getSelectionString(Atom selection) if (!*selectionString) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, - "X11: Failed to convert clipboard to string"); + "X11: Failed to convert selection to string"); } return *selectionString; From e3be6b8ae0d8b1701f00288c1b3db9959e2dc168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Sun, 17 Sep 2017 14:39:17 +0200 Subject: [PATCH 13/25] Cleanup Break up some overly long lines. --- src/cocoa_init.m | 8 +++++--- src/cocoa_joystick.m | 7 +++++-- src/cocoa_monitor.m | 17 ++++++++++++++--- src/mir_window.c | 3 ++- src/win32_init.c | 9 ++++++--- src/win32_joystick.c | 40 ++++++++++++++++++++++++++-------------- src/win32_window.c | 3 ++- src/wl_window.c | 3 ++- src/x11_init.c | 4 ++-- src/x11_window.c | 31 +++++++++++++++++++------------ 10 files changed, 83 insertions(+), 42 deletions(-) diff --git a/src/cocoa_init.m b/src/cocoa_init.m index dfc35b6ff..01a746bae 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -215,8 +215,9 @@ static GLFWbool updateUnicodeDataNS(void) return GLFW_FALSE; } - _glfw.ns.unicodeData = TISGetInputSourceProperty(_glfw.ns.inputSource, - kTISPropertyUnicodeKeyLayoutData); + _glfw.ns.unicodeData = + TISGetInputSourceProperty(_glfw.ns.inputSource, + kTISPropertyUnicodeKeyLayoutData); if (!_glfw.ns.unicodeData) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -232,7 +233,8 @@ static GLFWbool updateUnicodeDataNS(void) static GLFWbool initializeTIS(void) { // This works only because Cocoa has already loaded it properly - _glfw.ns.tis.bundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HIToolbox")); + _glfw.ns.tis.bundle = + CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HIToolbox")); if (!_glfw.ns.tis.bundle) { _glfwInputError(GLFW_PLATFORM_ERROR, diff --git a/src/cocoa_joystick.m b/src/cocoa_joystick.m index 05745720e..3a5751ef0 100644 --- a/src/cocoa_joystick.m +++ b/src/cocoa_joystick.m @@ -73,7 +73,9 @@ static long getElementValue(_GLFWjoystick* js, _GLFWjoyelementNS* element) // Comparison function for matching the SDL element order // -static CFComparisonResult compareElements(const void* fp, const void* sp, void* user) +static CFComparisonResult compareElements(const void* fp, + const void* sp, + void* user) { const _GLFWjoyelementNS* fe = fp; const _GLFWjoyelementNS* se = sp; @@ -183,7 +185,8 @@ static void matchCallback(void* context, for (i = 0; i < CFArrayGetCount(elements); i++) { - IOHIDElementRef native = (IOHIDElementRef) CFArrayGetValueAtIndex(elements, i); + IOHIDElementRef native = (IOHIDElementRef) + CFArrayGetValueAtIndex(elements, i); if (CFGetTypeID(native) != IOHIDElementGetTypeID()) continue; diff --git a/src/cocoa_monitor.m b/src/cocoa_monitor.m index 41a6cff07..6020599fd 100644 --- a/src/cocoa_monitor.m +++ b/src/cocoa_monitor.m @@ -54,7 +54,8 @@ static char* getDisplayName(CGDirectDisplayID displayID) while ((service = IOIteratorNext(it)) != 0) { - info = IODisplayCreateInfoDictionary(service, kIODisplayOnlyPreferredName); + info = IODisplayCreateInfoDictionary(service, + kIODisplayOnlyPreferredName); CFNumberRef vendorIDRef = CFDictionaryGetValue(info, CFSTR(kDisplayVendorID)); @@ -185,7 +186,13 @@ static CGDisplayFadeReservationToken beginFadeReservation(void) CGDisplayFadeReservationToken token = kCGDisplayFadeReservationInvalidToken; if (CGAcquireDisplayFadeReservation(5, &token) == kCGErrorSuccess) - CGDisplayFade(token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE); + { + CGDisplayFade(token, 0.3, + kCGDisplayBlendNormal, + kCGDisplayBlendSolidColor, + 0.0, 0.0, 0.0, + TRUE); + } return token; } @@ -196,7 +203,11 @@ static void endFadeReservation(CGDisplayFadeReservationToken token) { if (token != kCGDisplayFadeReservationInvalidToken) { - CGDisplayFade(token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); + CGDisplayFade(token, 0.5, + kCGDisplayBlendSolidColor, + kCGDisplayBlendNormal, + 0.0, 0.0, 0.0, + FALSE); CGReleaseDisplayFadeReservation(token); } } diff --git a/src/mir_window.c b/src/mir_window.c index 3e8d5c5bf..4ed71f971 100644 --- a/src/mir_window.c +++ b/src/mir_window.c @@ -869,7 +869,8 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily) { - PFN_vkGetPhysicalDeviceMirPresentationSupportKHR vkGetPhysicalDeviceMirPresentationSupportKHR = + PFN_vkGetPhysicalDeviceMirPresentationSupportKHR + vkGetPhysicalDeviceMirPresentationSupportKHR = (PFN_vkGetPhysicalDeviceMirPresentationSupportKHR) vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceMirPresentationSupportKHR"); if (!vkGetPhysicalDeviceMirPresentationSupportKHR) diff --git a/src/win32_init.c b/src/win32_init.c index 5a287076c..60c974224 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -30,7 +30,8 @@ #include #include -static const GUID _glfw_GUID_DEVINTERFACE_HID = {0x4d1e55b2,0xf16f,0x11cf,{0x88,0xcb,0x00,0x11,0x11,0x00,0x00,0x30}}; +static const GUID _glfw_GUID_DEVINTERFACE_HID = + {0x4d1e55b2,0xf16f,0x11cf,{0x88,0xcb,0x00,0x11,0x11,0x00,0x00,0x30}}; #define GUID_DEVINTERFACE_HID _glfw_GUID_DEVINTERFACE_HID @@ -68,7 +69,8 @@ static GLFWbool loadLibraries(void) _glfw.win32.winmm.instance = LoadLibraryA("winmm.dll"); if (!_glfw.win32.winmm.instance) { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to load winmm.dll"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to load winmm.dll"); return GLFW_FALSE; } @@ -78,7 +80,8 @@ static GLFWbool loadLibraries(void) _glfw.win32.user32.instance = LoadLibraryA("user32.dll"); if (!_glfw.win32.user32.instance) { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to load user32.dll"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to load user32.dll"); return GLFW_FALSE; } diff --git a/src/win32_joystick.c b/src/win32_joystick.c index 394d371a5..6da81f7ad 100644 --- a/src/win32_joystick.c +++ b/src/win32_joystick.c @@ -50,16 +50,26 @@ typedef struct _GLFWobjenumWin32 // Define local copies of the necessary GUIDs // -static const GUID _glfw_IID_IDirectInput8W = {0xbf798031,0x483a,0x4da2,{0xaa,0x99,0x5d,0x64,0xed,0x36,0x97,0x00}}; -static const GUID _glfw_GUID_XAxis = {0xa36d02e0,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; -static const GUID _glfw_GUID_YAxis = {0xa36d02e1,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; -static const GUID _glfw_GUID_ZAxis = {0xa36d02e2,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; -static const GUID _glfw_GUID_RxAxis = {0xa36d02f4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; -static const GUID _glfw_GUID_RyAxis = {0xa36d02f5,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; -static const GUID _glfw_GUID_RzAxis = {0xa36d02e3,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; -static const GUID _glfw_GUID_Slider = {0xa36d02e4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; -static const GUID _glfw_GUID_Button = {0xa36d02f0,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; -static const GUID _glfw_GUID_POV = {0xa36d02f2,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_IID_IDirectInput8W = + {0xbf798031,0x483a,0x4da2,{0xaa,0x99,0x5d,0x64,0xed,0x36,0x97,0x00}}; +static const GUID _glfw_GUID_XAxis = + {0xa36d02e0,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_YAxis = + {0xa36d02e1,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_ZAxis = + {0xa36d02e2,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_RxAxis = + {0xa36d02f4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_RyAxis = + {0xa36d02f5,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_RzAxis = + {0xa36d02e3,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_Slider = + {0xa36d02e4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_Button = + {0xa36d02f0,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_POV = + {0xa36d02f2,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; #define IID_IDirectInput8W _glfw_IID_IDirectInput8W #define GUID_XAxis _glfw_GUID_XAxis @@ -345,10 +355,12 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (!_glfw.joysticks[jid].present) - continue; - if (memcmp(&_glfw.joysticks[jid].win32.guid, &di->guidInstance, sizeof(GUID)) == 0) - return DIENUM_CONTINUE; + _GLFWjoystick* js = _glfw.joysticks + jid; + if (js->present) + { + if (memcmp(&js->win32.guid, &di->guidInstance, sizeof(GUID)) == 0) + return DIENUM_CONTINUE; + } } if (supportsXInput(&di->guidProduct)) diff --git a/src/win32_window.c b/src/win32_window.c index aa7efc843..30d1932a9 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -1798,7 +1798,8 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily) { - PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR vkGetPhysicalDeviceWin32PresentationSupportKHR = + PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR + vkGetPhysicalDeviceWin32PresentationSupportKHR = (PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR) vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR"); if (!vkGetPhysicalDeviceWin32PresentationSupportKHR) diff --git a/src/wl_window.c b/src/wl_window.c index fbd58ffbe..6c974dfd3 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1019,7 +1019,8 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily) { - PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR = + PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR + vkGetPhysicalDeviceWaylandPresentationSupportKHR = (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR) vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR"); if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR) diff --git a/src/x11_init.c b/src/x11_init.c index 6a8ba6597..9f94cc5f6 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -237,8 +237,8 @@ static void createKeyTables(void) if (_glfw.x11.xkb.available) { - // Use XKB to determine physical key locations independently of the current - // keyboard layout + // Use XKB to determine physical key locations independently of the + // current keyboard layout char name[XkbKeyNameLength + 1]; XkbDescPtr desc = XkbGetMap(_glfw.x11.display, 0, XkbUseCoreKbd); diff --git a/src/x11_window.c b/src/x11_window.c index e07fea779..26ea0897f 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -1263,8 +1263,10 @@ static void processEvent(XEvent *event) count = XwcLookupString(window->x11.ic, &event->xkey, - buffer, sizeof(buffer) / sizeof(wchar_t), - NULL, &status); + buffer, + sizeof(buffer) / sizeof(wchar_t), + NULL, + &status); if (status == XBufferOverflow) { @@ -1437,7 +1439,8 @@ static void processEvent(XEvent *event) const int x = event->xmotion.x; const int y = event->xmotion.y; - if (x != window->x11.warpCursorPosX || y != window->x11.warpCursorPosY) + if (x != window->x11.warpCursorPosX || + y != window->x11.warpCursorPosY) { // The cursor was moved by something other than GLFW @@ -1516,14 +1519,15 @@ static void processEvent(XEvent *event) if (protocol == _glfw.x11.WM_DELETE_WINDOW) { - // The window manager was asked to close the window, for example by - // the user pressing a 'close' window decoration button + // The window manager was asked to close the window, for + // example by the user pressing a 'close' window decoration + // button _glfwInputWindowCloseRequest(window); } else if (protocol == _glfw.x11.NET_WM_PING) { - // The window manager is pinging the application to ensure it's - // still responding to events + // The window manager is pinging the application to ensure + // it's still responding to events XEvent reply = *event; reply.xclient.window = _glfw.x11.root; @@ -1865,9 +1869,10 @@ void _glfwPushSelectionToManagerX11(void) { if (event.xselection.target == _glfw.x11.SAVE_TARGETS) { - // This means one of two things; either the selection was - // not owned, which means there is no clipboard manager, or - // the transfer to the clipboard manager has completed + // This means one of two things; either the selection + // was not owned, which means there is no clipboard + // manager, or the transfer to the clipboard manager has + // completed // In either case, it means we are done here return; } @@ -2787,7 +2792,8 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) { - PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR vkGetPhysicalDeviceXcbPresentationSupportKHR = + PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR + vkGetPhysicalDeviceXcbPresentationSupportKHR = (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR) vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR"); if (!vkGetPhysicalDeviceXcbPresentationSupportKHR) @@ -2812,7 +2818,8 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, } else { - PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR = + PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR + vkGetPhysicalDeviceXlibPresentationSupportKHR = (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR) vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR"); if (!vkGetPhysicalDeviceXlibPresentationSupportKHR) From f4fb25b63df16c8a6d87077d176e7fe1c80c04d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Sun, 17 Sep 2017 16:06:02 +0200 Subject: [PATCH 14/25] X11: Fix init order breaking cursor hiding The hidden cursor was created before Xcursor was loaded. --- src/x11_init.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/x11_init.c b/src/x11_init.c index 9f94cc5f6..3bf07d2d5 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -860,12 +860,13 @@ int _glfwPlatformInit(void) _glfw.x11.screen = DefaultScreen(_glfw.x11.display); _glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen); _glfw.x11.context = XUniqueContext(); - _glfw.x11.helperWindowHandle = createHelperWindow(); - _glfw.x11.hiddenCursorHandle = createHiddenCursor(); if (!initExtensions()) return GLFW_FALSE; + _glfw.x11.helperWindowHandle = createHelperWindow(); + _glfw.x11.hiddenCursorHandle = createHiddenCursor(); + if (XSupportsLocale()) { XSetLocaleModifiers(""); From 176ab9a5d273e117bd2b9301430c67fd2125e942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Sun, 24 Sep 2017 17:04:47 +0200 Subject: [PATCH 15/25] Fix Doxyfile gen not handling paths with spaces Fixes #1081. --- README.md | 1 + docs/CMakeLists.txt | 2 +- docs/Doxyfile.in | 14 +++++++------- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 40e9544f4..dbdafe007 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,7 @@ information on what to include when reporting a bug. `vkGetInstanceProcAddr` when `_GLFW_VULKAN_STATIC` was enabled - Bugfix: Invalid library paths were used in test and example CMake files (#930) - Bugfix: The scancode for synthetic key release events was always zero +- Bugfix: The generated Doxyfile did not handle paths with spaces (#1081) - [Win32] Added system error strings to relevant GLFW error descriptions (#733) - [Win32] Moved to `WM_INPUT` for disabled cursor mode motion input (#125) - [Win32] Removed XInput circular deadzone from joystick axis data (#1045) diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index b3034dfb3..20d85ae9c 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -23,7 +23,7 @@ if (GLFW_DOCUMENT_INTERNALS) endif() foreach(arg ${glfw_DOCS_SOURCES}) - set(GLFW_DOCS_SOURCES "${GLFW_DOCS_SOURCES} ${arg}") + set(GLFW_DOCS_SOURCES "${GLFW_DOCS_SOURCES} \\\n\"${arg}\"") endforeach() configure_file(Doxyfile.in Doxyfile @ONLY) diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index 9bbd9245f..c2069c5ca 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -52,7 +52,7 @@ PROJECT_LOGO = # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. -OUTPUT_DIRECTORY = @GLFW_BINARY_DIR@/docs +OUTPUT_DIRECTORY = "@GLFW_BINARY_DIR@/docs" # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output @@ -651,7 +651,7 @@ WARN_FORMAT = "$file:$line: $text" # and error messages should be written. If left blank the output is written # to stderr. -WARN_LOGFILE = @GLFW_BINARY_DIR@/docs/warnings.txt +WARN_LOGFILE = "@GLFW_BINARY_DIR@/docs/warnings.txt" #--------------------------------------------------------------------------- # configuration options related to the input files @@ -722,7 +722,7 @@ EXCLUDE_SYMBOLS = APIENTRY GLFWAPI # directories that contain example code fragments that are included (see # the \include command). -EXAMPLE_PATH = @GLFW_SOURCE_DIR@/examples +EXAMPLE_PATH = "@GLFW_SOURCE_DIR@/examples" # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp @@ -898,13 +898,13 @@ HTML_FILE_EXTENSION = .html # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! -HTML_HEADER = @GLFW_SOURCE_DIR@/docs/header.html +HTML_HEADER = "@GLFW_SOURCE_DIR@/docs/header.html" # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. -HTML_FOOTER = @GLFW_SOURCE_DIR@/docs/footer.html +HTML_FOOTER = "@GLFW_SOURCE_DIR@/docs/footer.html" # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to @@ -923,7 +923,7 @@ HTML_STYLESHEET = # robust against future updates. Doxygen will copy the style sheet file to # the output directory. -HTML_EXTRA_STYLESHEET = @GLFW_SOURCE_DIR@/docs/extra.css +HTML_EXTRA_STYLESHEET = "@GLFW_SOURCE_DIR@/docs/extra.css" # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note @@ -932,7 +932,7 @@ HTML_EXTRA_STYLESHEET = @GLFW_SOURCE_DIR@/docs/extra.css # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. -HTML_EXTRA_FILES = @GLFW_SOURCE_DIR@/docs/spaces.svg +HTML_EXTRA_FILES = "@GLFW_SOURCE_DIR@/docs/spaces.svg" # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images From 25b7eba4a02ec15883d82e370a7479c4f8de6da7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 29 Aug 2017 20:05:44 +0200 Subject: [PATCH 16/25] Win32: Clean up dynamic loading and version checks --- src/win32_init.c | 14 +++++++------- src/win32_platform.h | 36 ++++++++++++++++++++++++++++++------ src/win32_window.c | 14 +++++++------- 3 files changed, 44 insertions(+), 20 deletions(-) diff --git a/src/win32_init.c b/src/win32_init.c index 60c974224..7a8ee2086 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -85,9 +85,9 @@ static GLFWbool loadLibraries(void) return GLFW_FALSE; } - _glfw.win32.user32.SetProcessDPIAware = (PFN_SetProcessDPIAware) + _glfw.win32.user32.SetProcessDPIAware_ = (PFN_SetProcessDPIAware) GetProcAddress(_glfw.win32.user32.instance, "SetProcessDPIAware"); - _glfw.win32.user32.ChangeWindowMessageFilterEx = (PFN_ChangeWindowMessageFilterEx) + _glfw.win32.user32.ChangeWindowMessageFilterEx_ = (PFN_ChangeWindowMessageFilterEx) GetProcAddress(_glfw.win32.user32.instance, "ChangeWindowMessageFilterEx"); _glfw.win32.dinput8.instance = LoadLibraryA("dinput8.dll"); @@ -136,7 +136,7 @@ static GLFWbool loadLibraries(void) _glfw.win32.shcore.instance = LoadLibraryA("shcore.dll"); if (_glfw.win32.shcore.instance) { - _glfw.win32.shcore.SetProcessDpiAwareness = (PFN_SetProcessDpiAwareness) + _glfw.win32.shcore.SetProcessDpiAwareness_ = (PFN_SetProcessDpiAwareness) GetProcAddress(_glfw.win32.shcore.instance, "SetProcessDpiAwareness"); } @@ -507,10 +507,10 @@ int _glfwPlatformInit(void) createKeyTables(); _glfwUpdateKeyNamesWin32(); - if (_glfw_SetProcessDpiAwareness) - _glfw_SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); - else if (_glfw_SetProcessDPIAware) - _glfw_SetProcessDPIAware(); + if (IsWindows8Point1OrGreater()) + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); + else if (IsWindowsVistaOrGreater()) + SetProcessDPIAware(); if (!_glfwRegisterWindowClassWin32()) return GLFW_FALSE; diff --git a/src/win32_platform.h b/src/win32_platform.h index 63aaa94dc..e829799d7 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -122,6 +122,30 @@ typedef enum PROCESS_DPI_AWARENESS } PROCESS_DPI_AWARENESS; #endif /*DPI_ENUMS_DECLARED*/ +// HACK: Define versionhelpers.h functions manually as MinGW lacks the header +FORCEINLINE BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp) +{ + OSVERSIONINFOEXW osvi = { sizeof(osvi), major, minor, 0, 0, {0}, sp }; + DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR; + ULONGLONG cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL); + cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL); + cond = VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); + return VerifyVersionInfoW(&osvi, mask, cond); +} + +#define IsWindowsVistaOrGreater() \ + IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), \ + LOBYTE(_WIN32_WINNT_VISTA), 0) +#define IsWindows7OrGreater() \ + IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), \ + LOBYTE(_WIN32_WINNT_WIN7), 0) +#define IsWindows8OrGreater() \ + IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), \ + LOBYTE(_WIN32_WINNT_WIN8), 0) +#define IsWindows8Point1OrGreater() \ + IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINBLUE), \ + LOBYTE(_WIN32_WINNT_WINBLUE), 0) + // HACK: Define macros that some xinput.h variants don't #ifndef XINPUT_CAPS_WIRELESS #define XINPUT_CAPS_WIRELESS 0x0002 @@ -173,8 +197,8 @@ typedef HRESULT (WINAPI * PFN_DirectInput8Create)(HINSTANCE,DWORD,REFIID,LPVOID* // user32.dll function pointer typedefs typedef BOOL (WINAPI * PFN_SetProcessDPIAware)(void); typedef BOOL (WINAPI * PFN_ChangeWindowMessageFilterEx)(HWND,UINT,DWORD,PCHANGEFILTERSTRUCT); -#define _glfw_SetProcessDPIAware _glfw.win32.user32.SetProcessDPIAware -#define _glfw_ChangeWindowMessageFilterEx _glfw.win32.user32.ChangeWindowMessageFilterEx +#define SetProcessDPIAware _glfw.win32.user32.SetProcessDPIAware_ +#define ChangeWindowMessageFilterEx _glfw.win32.user32.ChangeWindowMessageFilterEx_ // dwmapi.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*); @@ -184,7 +208,7 @@ typedef HRESULT (WINAPI * PFN_DwmFlush)(VOID); // shcore.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); -#define _glfw_SetProcessDpiAwareness _glfw.win32.shcore.SetProcessDpiAwareness +#define SetProcessDpiAwareness _glfw.win32.shcore.SetProcessDpiAwareness_ typedef VkFlags VkWin32SurfaceCreateFlagsKHR; @@ -278,8 +302,8 @@ typedef struct _GLFWlibraryWin32 struct { HINSTANCE instance; - PFN_SetProcessDPIAware SetProcessDPIAware; - PFN_ChangeWindowMessageFilterEx ChangeWindowMessageFilterEx; + PFN_SetProcessDPIAware SetProcessDPIAware_; + PFN_ChangeWindowMessageFilterEx ChangeWindowMessageFilterEx_; } user32; struct { @@ -290,7 +314,7 @@ typedef struct _GLFWlibraryWin32 struct { HINSTANCE instance; - PFN_SetProcessDpiAwareness SetProcessDpiAwareness; + PFN_SetProcessDpiAwareness SetProcessDpiAwareness_; } shcore; } _GLFWlibraryWin32; diff --git a/src/win32_window.c b/src/win32_window.c index 30d1932a9..9ce9e4361 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -1039,14 +1039,14 @@ static int createNativeWindow(_GLFWwindow* window, SetPropW(window->win32.handle, L"GLFW", window); - if (_glfw_ChangeWindowMessageFilterEx) + if (IsWindows7OrGreater()) { - _glfw_ChangeWindowMessageFilterEx(window->win32.handle, - WM_DROPFILES, MSGFLT_ALLOW, NULL); - _glfw_ChangeWindowMessageFilterEx(window->win32.handle, - WM_COPYDATA, MSGFLT_ALLOW, NULL); - _glfw_ChangeWindowMessageFilterEx(window->win32.handle, - WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL); + ChangeWindowMessageFilterEx(window->win32.handle, + WM_DROPFILES, MSGFLT_ALLOW, NULL); + ChangeWindowMessageFilterEx(window->win32.handle, + WM_COPYDATA, MSGFLT_ALLOW, NULL); + ChangeWindowMessageFilterEx(window->win32.handle, + WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL); } DragAcceptFiles(window->win32.handle, TRUE); From eed94448fde4da0b494865163b9e9eda5361e031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 19 Sep 2017 14:12:15 +0200 Subject: [PATCH 17/25] Win32: Remove unused GUID --- src/win32_joystick.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/win32_joystick.c b/src/win32_joystick.c index 6da81f7ad..ad2dbb2f1 100644 --- a/src/win32_joystick.c +++ b/src/win32_joystick.c @@ -66,8 +66,6 @@ static const GUID _glfw_GUID_RzAxis = {0xa36d02e3,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; static const GUID _glfw_GUID_Slider = {0xa36d02e4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; -static const GUID _glfw_GUID_Button = - {0xa36d02f0,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; static const GUID _glfw_GUID_POV = {0xa36d02f2,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; @@ -79,7 +77,6 @@ static const GUID _glfw_GUID_POV = #define GUID_RyAxis _glfw_GUID_RyAxis #define GUID_RzAxis _glfw_GUID_RzAxis #define GUID_Slider _glfw_GUID_Slider -#define GUID_Button _glfw_GUID_Button #define GUID_POV _glfw_GUID_POV // Object data array for our clone of c_dfDIJoystick From 019609b6cdf3e92aa10c368c8351df958415987e Mon Sep 17 00:00:00 2001 From: Wolfgang Draxinger Date: Mon, 22 Feb 2016 18:08:15 +0100 Subject: [PATCH 18/25] Add GLFW_TRANSPARENT and X11 implementation This is a squashed extract of several commits, minimally edited to ensure it compiles. Related to #197. Related to #715. --- examples/gears.c | 3 +++ include/GLFW/glfw3.h | 1 + src/egl_context.c | 49 +++++++++++++++++++++++++++++++++++++++----- src/egl_context.h | 3 ++- src/glx_context.c | 46 ++++++++++++++++++++++++++++++++++++----- src/glx_context.h | 4 ++-- src/internal.h | 2 ++ src/window.c | 7 +++++++ src/x11_init.c | 49 ++++++++++++++++++++++++++++++++++++++++++++ src/x11_platform.h | 28 ++++++++++++++++++++++++- src/x11_window.c | 4 ++-- 11 files changed, 180 insertions(+), 16 deletions(-) diff --git a/examples/gears.c b/examples/gears.c index a787baedf..f7526e05f 100644 --- a/examples/gears.c +++ b/examples/gears.c @@ -172,6 +172,7 @@ static GLfloat angle = 0.f; /* OpenGL draw function & timing */ static void draw(void) { + glClearColor(0., 0., 0., 0.); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); @@ -311,6 +312,8 @@ int main(int argc, char *argv[]) } glfwWindowHint(GLFW_DEPTH_BITS, 16); + glfwWindowHint(GLFW_ALPHA_BITS, 8); + glfwWindowHint(GLFW_TRANSPARENT, GLFW_TRUE); window = glfwCreateWindow( 300, 300, "Gears", NULL, NULL ); if (!window) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 05e76a9ae..58b7d7b91 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -787,6 +787,7 @@ extern "C" { * Cursor centering [window hint](@ref GLFW_CENTER_CURSOR_hint). */ #define GLFW_CENTER_CURSOR 0x00020009 +#define GLFW_TRANSPARENT 0x0002000A /*! @brief Framebuffer bit depth hint. * diff --git a/src/egl_context.c b/src/egl_context.c index 54257f25d..e5435468c 100644 --- a/src/egl_context.c +++ b/src/egl_context.c @@ -87,13 +87,21 @@ static int getEGLConfigAttrib(EGLConfig config, int attrib) // static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* desired, - EGLConfig* result) + EGLConfig* result, + GLFWbool findTransparent) { EGLConfig* nativeConfigs; _GLFWfbconfig* usableConfigs; const _GLFWfbconfig* closest; int i, nativeCount, usableCount; +#if defined(_GLFW_X11) + XVisualInfo visualTemplate = {0}; + if ( !(_glfw.xrender.major || _glfw.xrender.minor) ) { + findTransparent = GLFW_FALSE; + } +#endif // _GLFW_X11 + eglGetConfigs(_glfw.egl.display, NULL, 0, &nativeCount); if (!nativeCount) { @@ -107,6 +115,7 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); usableCount = 0; +selectionloop: for (i = 0; i < nativeCount; i++) { const EGLConfig n = nativeConfigs[i]; @@ -122,8 +131,31 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, #if defined(_GLFW_X11) // Only consider EGLConfigs with associated Visuals - if (!getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID)) + visualTemplate.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID); + if (!visualTemplate.visualid) continue; + + if( findTransparent ) { + int n_vi; + XVisualInfo *visualinfo; + XRenderPictFormat *pictFormat; + + visualinfo = XGetVisualInfo(_glfw.x11.display, VisualIDMask, &visualTemplate, &n_vi); + if (!visualinfo) + continue; + + pictFormat = XRenderFindVisualFormat(_glfw.x11.display, visualinfo->visual); + if( !pictFormat ) { + XFree( visualinfo ); + continue; + } + + if( !pictFormat->direct.alphaMask ) { + XFree( visualinfo ); + continue; + } + XFree( visualinfo ); + } #endif // _GLFW_X11 if (ctxconfig->client == GLFW_OPENGL_ES_API) @@ -159,6 +191,12 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, u->handle = (uintptr_t) n; usableCount++; } + // reiterate the selection loop without looking for transparency supporting + // formats if no matchig FB configs for a transparent window were found. + if( findTransparent && !usableCount ) { + findTransparent = GLFW_FALSE; + goto selectionloop; + } closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); if (closest) @@ -455,7 +493,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, if (ctxconfig->share) share = ctxconfig->share->context.egl.handle; - if (!chooseEGLConfig(ctxconfig, fbconfig, &config)) + if (!chooseEGLConfig(ctxconfig, fbconfig, &config, window->transparent)) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "EGL: Failed to find a suitable EGLConfig"); @@ -689,7 +727,8 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, // Returns the Visual and depth of the chosen EGLConfig // #if defined(_GLFW_X11) -GLFWbool _glfwChooseVisualEGL(const _GLFWctxconfig* ctxconfig, +GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, Visual** visual, int* depth) { @@ -699,7 +738,7 @@ GLFWbool _glfwChooseVisualEGL(const _GLFWctxconfig* ctxconfig, EGLint visualID = 0, count = 0; const long vimask = VisualScreenMask | VisualIDMask; - if (!chooseEGLConfig(ctxconfig, fbconfig, &native)) + if (!chooseEGLConfig(ctxconfig, fbconfig, &native, wndconfig->transparent)) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "EGL: Failed to find a suitable EGLConfig"); diff --git a/src/egl_context.h b/src/egl_context.h index bfab5111f..aa339baac 100644 --- a/src/egl_context.h +++ b/src/egl_context.h @@ -211,7 +211,8 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); #if defined(_GLFW_X11) -GLFWbool _glfwChooseVisualEGL(const _GLFWctxconfig* ctxconfig, +GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, Visual** visual, int* depth); #endif /*_GLFW_X11*/ diff --git a/src/glx_context.c b/src/glx_context.c index e545fa0fe..9d02a6fab 100644 --- a/src/glx_context.c +++ b/src/glx_context.c @@ -47,7 +47,10 @@ static int getGLXFBConfigAttrib(GLXFBConfig fbconfig, int attrib) // Return the GLXFBConfig most closely matching the specified hints // -static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* result) +static GLFWbool chooseGLXFBConfig( + const _GLFWfbconfig* desired, + GLXFBConfig* result, + GLFWbool findTransparent) { GLXFBConfig* nativeConfigs; _GLFWfbconfig* usableConfigs; @@ -56,6 +59,10 @@ static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* res const char* vendor; GLFWbool trustWindowBit = GLFW_TRUE; + if ( !(_glfw.xrender.major || _glfw.xrender.minor) ) { + findTransparent = GLFW_FALSE; + } + // HACK: This is a (hopefully temporary) workaround for Chromium // (VirtualBox GL) not setting the window bit on any GLXFBConfigs vendor = glXGetClientString(_glfw.x11.display, GLX_VENDOR); @@ -73,6 +80,7 @@ static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* res usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); usableCount = 0; +selectionloop: for (i = 0; i < nativeCount; i++) { const GLXFBConfig n = nativeConfigs[i]; @@ -89,6 +97,27 @@ static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* res continue; } + if( findTransparent ) { + XVisualInfo *visualinfo; + XRenderPictFormat *pictFormat; + + visualinfo = glXGetVisualFromFBConfig(_glfw.x11.display, n); + if (!visualinfo) + continue; + + pictFormat = XRenderFindVisualFormat(_glfw.x11.display, visualinfo->visual); + if( !pictFormat ) { + XFree( visualinfo ); + continue; + } + + if( !pictFormat->direct.alphaMask ) { + XFree( visualinfo ); + continue; + } + XFree( visualinfo ); + } + u->redBits = getGLXFBConfigAttrib(n, GLX_RED_SIZE); u->greenBits = getGLXFBConfigAttrib(n, GLX_GREEN_SIZE); u->blueBits = getGLXFBConfigAttrib(n, GLX_BLUE_SIZE); @@ -118,6 +147,12 @@ static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* res u->handle = (uintptr_t) n; usableCount++; } + // reiterate the selection loop without looking for transparency supporting + // formats if no matchig FB configs for a transparent window were found. + if( findTransparent && !usableCount ) { + findTransparent = GLFW_FALSE; + goto selectionloop; + } closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); if (closest) @@ -442,7 +477,7 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, if (ctxconfig->share) share = ctxconfig->share->context.glx.handle; - if (!chooseGLXFBConfig(fbconfig, &native)) + if (!chooseGLXFBConfig(fbconfig, &native, window->transparent)) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "GLX: Failed to find a suitable GLXFBConfig"); @@ -622,14 +657,15 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, // Returns the Visual and depth of the chosen GLXFBConfig // -GLFWbool _glfwChooseVisualGLX(const _GLFWctxconfig* ctxconfig, +GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, Visual** visual, int* depth) { GLXFBConfig native; XVisualInfo* result; - if (!chooseGLXFBConfig(fbconfig, &native)) + if (!chooseGLXFBConfig(fbconfig, &native, wndconfig->transparent)) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "GLX: Failed to find a suitable GLXFBConfig"); @@ -645,7 +681,7 @@ GLFWbool _glfwChooseVisualGLX(const _GLFWctxconfig* ctxconfig, } *visual = result->visual; - *depth = result->depth; + *depth = result->depth; XFree(result); return GLFW_TRUE; diff --git a/src/glx_context.h b/src/glx_context.h index 30743c112..f767cb141 100644 --- a/src/glx_context.h +++ b/src/glx_context.h @@ -168,14 +168,14 @@ typedef struct _GLFWlibraryGLX } _GLFWlibraryGLX; - GLFWbool _glfwInitGLX(void); void _glfwTerminateGLX(void); GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); void _glfwDestroyContextGLX(_GLFWwindow* window); -GLFWbool _glfwChooseVisualGLX(const _GLFWctxconfig* ctxconfig, +GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, Visual** visual, int* depth); diff --git a/src/internal.h b/src/internal.h index a95d1f867..01517ec16 100644 --- a/src/internal.h +++ b/src/internal.h @@ -299,6 +299,7 @@ struct _GLFWwndconfig GLFWbool resizable; GLFWbool visible; GLFWbool decorated; + GLFWbool transparent; GLFWbool focused; GLFWbool autoIconify; GLFWbool floating; @@ -402,6 +403,7 @@ struct _GLFWwindow // Window settings and state GLFWbool resizable; GLFWbool decorated; + GLFWbool transparent; GLFWbool autoIconify; GLFWbool floating; GLFWbool shouldClose; diff --git a/src/window.c b/src/window.c index 5c33217de..e8a68b61a 100644 --- a/src/window.c +++ b/src/window.c @@ -180,6 +180,7 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, window->monitor = (_GLFWmonitor*) monitor; window->resizable = wndconfig.resizable; window->decorated = wndconfig.decorated; + window->transparent = wndconfig.transparent; window->autoIconify = wndconfig.autoIconify; window->floating = wndconfig.floating; window->cursorMode = GLFW_CURSOR_NORMAL; @@ -249,6 +250,7 @@ void glfwDefaultWindowHints(void) _glfw.hints.window.resizable = GLFW_TRUE; _glfw.hints.window.visible = GLFW_TRUE; _glfw.hints.window.decorated = GLFW_TRUE; + _glfw.hints.window.transparent = GLFW_FALSE; _glfw.hints.window.focused = GLFW_TRUE; _glfw.hints.window.autoIconify = GLFW_TRUE; @@ -327,6 +329,9 @@ GLFWAPI void glfwWindowHint(int hint, int value) case GLFW_DECORATED: _glfw.hints.window.decorated = value ? GLFW_TRUE : GLFW_FALSE; return; + case GLFW_TRANSPARENT: + _glfw.hints.window.transparent = value ? GLFW_TRUE : GLFW_FALSE; + return; case GLFW_FOCUSED: _glfw.hints.window.focused = value ? GLFW_TRUE : GLFW_FALSE; return; @@ -728,6 +733,8 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) return window->resizable; case GLFW_DECORATED: return window->decorated; + case GLFW_TRANSPARENT: + return window->transparent; case GLFW_FLOATING: return window->floating; case GLFW_AUTO_ICONIFY: diff --git a/src/x11_init.c b/src/x11_init.c index 3bf07d2d5..195c8ce4f 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -717,6 +717,55 @@ static GLFWbool initExtensions(void) _glfw.x11.MOTIF_WM_HINTS = XInternAtom(_glfw.x11.display, "_MOTIF_WM_HINTS", False); + int i; + const char* sonames_xrender[] = + { +#if defined(__CYGWIN__) + "libXrender-1.so", +#else + "libXrender.so.1", + "libXrender.so", +#endif + NULL + }; + + // Xrender support is optional and not a requirement for GLX/EGL + // to work. Xrender is required for selecting a FB config that + // supports a picture format with an alpha mask, which in turn + // is required for transparent windows. I Xrender is not supported + // the GLFW_TRANSPARENT window hint is ignored. + for (i = 0; sonames_xrender[i]; i++) + { + _glfw.xrender.handle = dlopen(sonames_xrender[i], RTLD_LAZY | RTLD_GLOBAL); + if (_glfw.xrender.handle) + break; + } + _glfw.xrender.errorBase = 0; + _glfw.xrender.eventBase = 0; + _glfw.xrender.major = 0; + _glfw.xrender.minor = 0; + if (_glfw.xrender.handle) do { + int errorBase, eventBase, major, minor; + _glfw.xrender.QueryExtension = + dlsym(_glfw.xrender.handle, "XRenderQueryExtension"); + _glfw.xrender.QueryVersion = + dlsym(_glfw.xrender.handle, "XRenderQueryVersion"); + _glfw.xrender.FindVisualFormat = + dlsym(_glfw.xrender.handle, "XRenderFindVisualFormat"); + + if ( !XRenderQueryExtension(_glfw.x11.display, &errorBase, &eventBase)) { + break; + } + if ( !XRenderQueryVersion(_glfw.x11.display, &major, &minor)) { + break; + } + + _glfw.xrender.errorBase = errorBase; + _glfw.xrender.eventBase = eventBase; + _glfw.xrender.major = major; + _glfw.xrender.minor = minor; + } while(0); + return GLFW_TRUE; } diff --git a/src/x11_platform.h b/src/x11_platform.h index 39eaec056..9890f7034 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -162,10 +162,20 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(Vk #define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.x11.display) #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowX11 x11 -#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11 +#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11 ; _GLFWlibraryXrender xrender #define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorX11 x11 #define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorX11 x11 +// libXrender.so function pointer typedefs +typedef Bool (*PFNXRENDERQUERYEXTENSIONPROC)(Display*,int*,int*); +typedef Status (*PFNXRENDERQUERYVERSIONPROC)(Display*dpy,int*,int*); +typedef XRenderPictFormat* (*PFNXRENDERFINDVISUALFORMATPROC)(Display*,Visual const *); + +// libXrender.so function identifier overlays +#define XRenderQueryExtension _glfw.xrender.QueryExtension +#define XRenderQueryVersion _glfw.xrender.QueryVersion +#define XRenderFindVisualFormat _glfw.xrender.FindVisualFormat + // X11-specific per-window data // @@ -375,6 +385,22 @@ typedef struct _GLFWlibraryX11 } _GLFWlibraryX11; +// Xrender-specific global data +typedef struct _GLFWlibraryXrender +{ + int major, minor; + int eventBase; + int errorBase; + + // dlopen handle for libGL.so.1 + void* handle; + + // Xrender functions (subset required for transparent window) + PFNXRENDERQUERYEXTENSIONPROC QueryExtension; + PFNXRENDERQUERYVERSIONPROC QueryVersion; + PFNXRENDERFINDVISUALFORMATPROC FindVisualFormat; +} _GLFWlibraryXrender; + // X11-specific per-monitor data // typedef struct _GLFWmonitorX11 diff --git a/src/x11_window.c b/src/x11_window.c index 26ea0897f..3addee6ca 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -1905,14 +1905,14 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, { if (!_glfwInitGLX()) return GLFW_FALSE; - if (!_glfwChooseVisualGLX(ctxconfig, fbconfig, &visual, &depth)) + if (!_glfwChooseVisualGLX(wndconfig, ctxconfig, fbconfig, &visual, &depth)) return GLFW_FALSE; } else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) { if (!_glfwInitEGL()) return GLFW_FALSE; - if (!_glfwChooseVisualEGL(ctxconfig, fbconfig, &visual, &depth)) + if (!_glfwChooseVisualEGL(wndconfig, ctxconfig, fbconfig, &visual, &depth)) return GLFW_FALSE; } else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) From 51f0cd3b510b90237236dfc46a9afe61b8713925 Mon Sep 17 00:00:00 2001 From: Christopher Pelloux Date: Tue, 23 Feb 2016 23:40:22 -0500 Subject: [PATCH 19/25] Win32: Implement GLFW_TRANSPARENT This is a squashed extract of several commits, minimally edited to ensure it compiles. Related to #197. Related to #723. --- src/wgl_context.c | 102 +++++++++++++++++++++++++++++++++++++++++-- src/win32_init.c | 2 + src/win32_platform.h | 16 +++++++ 3 files changed, 117 insertions(+), 3 deletions(-) diff --git a/src/wgl_context.c b/src/wgl_context.c index 1f2b12a50..990eb2992 100644 --- a/src/wgl_context.c +++ b/src/wgl_context.c @@ -83,6 +83,20 @@ static int choosePixelFormat(_GLFWwindow* window, { const int n = i + 1; _GLFWfbconfig* u = usableConfigs + usableCount; + PIXELFORMATDESCRIPTOR pfd; + + if (window->transparent) { + if (!DescribePixelFormat(window->context.wgl.dc, + n, + sizeof(PIXELFORMATDESCRIPTOR), + &pfd)) + { + continue; + } + + if (!(pfd.dwFlags & PFD_SUPPORT_COMPOSITION)) + continue; + } if (_glfw.wgl.ARB_pixel_format) { @@ -152,11 +166,9 @@ static int choosePixelFormat(_GLFWwindow* window, } else { - PIXELFORMATDESCRIPTOR pfd; - // Get pixel format attributes through legacy PFDs - if (!DescribePixelFormat(window->context.wgl.dc, + if (!window->transparent && DescribePixelFormat(window->context.wgl.dc, n, sizeof(PIXELFORMATDESCRIPTOR), &pfd)) @@ -203,6 +215,15 @@ static int choosePixelFormat(_GLFWwindow* window, u->handle = n; usableCount++; } + // Reiterate the selection loop without looking for transparency supporting + // formats if no matching pixelformat for a transparent window were found. + if (window->transparent && !usableCount) { + window->transparent = GLFW_FALSE; + free(usableConfigs); + _glfwInputError(GLFW_PLATFORM_ERROR, + "WGL: No pixel format found for transparent window. Ignoring transparency."); + return choosePixelFormat(window, ctxconfig, fbconfig); + } if (!usableCount) { @@ -484,6 +505,75 @@ void _glfwTerminateWGL(void) attribs[index++] = v; \ } +static GLFWbool setupTransparentWindow(_GLFWwindow* window) +{ + if (!isCompositionEnabled) { + _glfwInputError(GLFW_PLATFORM_ERROR, + "WGL: Composition needed for transparent window is disabled"); + } + if (!_glfw_DwmEnableBlurBehindWindow) { + _glfwInputError(GLFW_PLATFORM_ERROR, + "WGL: Unable to load DwmEnableBlurBehindWindow required for transparent window"); + return GLFW_FALSE; + } + + HRESULT hr = S_OK; + HWND handle = window->win32.handle; + + DWM_BLURBEHIND bb = { 0 }; + bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; + bb.hRgnBlur = CreateRectRgn(0, 0, -1, -1); // makes the window transparent + bb.fEnable = TRUE; + hr = _glfw_DwmEnableBlurBehindWindow(handle, &bb); + + if (!SUCCEEDED(hr)) { + _glfwInputError(GLFW_PLATFORM_ERROR, + "WGL: Failed to enable blur behind window required for transparent window"); + return GLFW_FALSE; + } + + // Decorated windows on Windows 8+ don't repaint the transparent background + // leaving a trail behind animations. + // Hack: making the window layered with a transparency color key seems to fix this. + // Normally, when specifying a transparency color key to be used when composing + // the layered window, all pixels painted by the window in this color will be transparent. + // That doesn't seem to be the case anymore on Windows 8+, at least when used with + // DwmEnableBlurBehindWindow + negative region. + if (window->decorated && IsWindows8OrGreater()) + { + long style = GetWindowLong(handle, GWL_EXSTYLE); + if (!style) { + _glfwInputError(GLFW_PLATFORM_ERROR, + "WGL: Failed to retrieve extended styles. GetLastError: %d", + GetLastError()); + return GLFW_FALSE; + } + style |= WS_EX_LAYERED; + if (!SetWindowLongPtr(handle, GWL_EXSTYLE, style)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "WGL: Failed to add layered style. GetLastError: %d", + GetLastError()); + return GLFW_FALSE; + } + if (!SetLayeredWindowAttributes(handle, + // Using a color key not equal to black to fix the trailing issue. + // When set to black, something is making the hit test not resize with the + // window frame. + RGB(0, 193, 48), + 255, + LWA_COLORKEY)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "WGL: Failed to set layered window. GetLastError: %d", + GetLastError()); + return GLFW_FALSE; + } + } + + return GLFW_TRUE; +} + // Create the OpenGL or OpenGL ES context // GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, @@ -713,6 +803,12 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, } } + if (window->transparent) + { + if (!setupTransparentWindow(window)) + window->transparent = GLFW_FALSE; + } + window->context.makeCurrent = makeContextCurrentWGL; window->context.swapBuffers = swapBuffersWGL; window->context.swapInterval = swapIntervalWGL; diff --git a/src/win32_init.c b/src/win32_init.c index 7a8ee2086..619fcfb14 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -131,6 +131,8 @@ static GLFWbool loadLibraries(void) GetProcAddress(_glfw.win32.dwmapi.instance, "DwmIsCompositionEnabled"); _glfw.win32.dwmapi.Flush = (PFN_DwmFlush) GetProcAddress(_glfw.win32.dwmapi.instance, "DwmFlush"); + _glfw.win32.dwmapi.DwmEnableBlurBehindWindow = (DWMENABLEBLURBEHINDWINDOW_T) + GetProcAddress(_glfw.win32.dwmapi.instance, "DwmEnableBlurBehindWindow"); } _glfw.win32.shcore.instance = LoadLibraryA("shcore.dll"); diff --git a/src/win32_platform.h b/src/win32_platform.h index e829799d7..bfed50514 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -122,6 +122,19 @@ typedef enum PROCESS_DPI_AWARENESS } PROCESS_DPI_AWARENESS; #endif /*DPI_ENUMS_DECLARED*/ +#if !defined(_DWMAPI_H_) +#define DWM_BB_ENABLE 0x00000001 +#define DWM_BB_BLURREGION 0x00000002 + +typedef struct +{ + DWORD dwFlags; + BOOL fEnable; + HRGN hRgnBlur; + BOOL fTransitionOnMaximized; +} DWM_BLURBEHIND; +#endif + // HACK: Define versionhelpers.h functions manually as MinGW lacks the header FORCEINLINE BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp) { @@ -203,8 +216,10 @@ typedef BOOL (WINAPI * PFN_ChangeWindowMessageFilterEx)(HWND,UINT,DWORD,PCHANGEF // dwmapi.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*); typedef HRESULT (WINAPI * PFN_DwmFlush)(VOID); +typedef HRESULT(WINAPI * DWMENABLEBLURBEHINDWINDOW_T)(HWND, const DWM_BLURBEHIND*); #define DwmIsCompositionEnabled _glfw.win32.dwmapi.IsCompositionEnabled #define DwmFlush _glfw.win32.dwmapi.Flush +#define _glfw_DwmEnableBlurBehindWindow _glfw.win32.dwmapi.DwmEnableBlurBehindWindow // shcore.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); @@ -310,6 +325,7 @@ typedef struct _GLFWlibraryWin32 HINSTANCE instance; PFN_DwmIsCompositionEnabled IsCompositionEnabled; PFN_DwmFlush Flush; + DWMENABLEBLURBEHINDWINDOW_T DwmEnableBlurBehindWindow; } dwmapi; struct { From ac009a5f5c41f9a78d19cd6cf0c72a0700bafc14 Mon Sep 17 00:00:00 2001 From: Cem Karan Date: Wed, 16 Dec 2015 16:28:20 -0500 Subject: [PATCH 20/25] Cocoa: Implement GLFW_TRANSPARENT This is an extract of a commit, minimally edited to ensure it compiles. Closes #663. Related to #197. --- src/cocoa_window.m | 12 +++++++++++- src/nsgl_context.m | 6 ++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/cocoa_window.m b/src/cocoa_window.m index cd4f3f894..4b5cd3c1f 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -413,7 +413,11 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (BOOL)isOpaque { - return YES; + // Set to NO even if alphaMask is not used; + // The NSView/GLFWContentView does not need to be opaque anyway, + // and to avoid keeping track of alphaMask inside the NSView we + // just return NO here instead. + return NO; } - (BOOL)canBecomeKeyView @@ -1081,6 +1085,12 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, if (wndconfig->ns.retina) [window->ns.view setWantsBestResolutionOpenGLSurface:YES]; + if (window->transparent) + { + [window->ns.object setOpaque:NO]; + [window->ns.object setBackgroundColor:[NSColor clearColor]]; + } + [window->ns.object setContentView:window->ns.view]; [window->ns.object makeFirstResponder:window->ns.view]; [window->ns.object setTitle:[NSString stringWithUTF8String:wndconfig->title]]; diff --git a/src/nsgl_context.m b/src/nsgl_context.m index 8504f0b97..ef247ca7d 100644 --- a/src/nsgl_context.m +++ b/src/nsgl_context.m @@ -296,6 +296,12 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, return GLFW_FALSE; } + if (window->transparent) + { + GLint opaque = 0; + [window->context.nsgl.object setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity]; + } + [window->context.nsgl.object setView:window->ns.view]; window->context.makeCurrent = makeContextCurrentNSGL; From 93e66661d3f087ba38b8e157a9661cf747142bc5 Mon Sep 17 00:00:00 2001 From: Bailey Cosier Date: Tue, 19 Sep 2017 18:27:45 +0200 Subject: [PATCH 21/25] Cleanup This is an extract of a commit, minimally edited to ensure it compiles. Closes #1078. Related to #197. --- docs/window.dox | 4 ++++ include/GLFW/glfw3.h | 7 ++++++- src/cocoa_window.m | 2 +- src/egl_context.c | 4 ++-- src/glx_context.c | 4 ++-- src/internal.h | 3 +-- src/nsgl_context.m | 2 +- src/wgl_context.c | 13 +++++++------ src/window.c | 11 ++++------- 9 files changed, 28 insertions(+), 22 deletions(-) diff --git a/docs/window.dox b/docs/window.dox index d9a837f63..17fa3002b 100644 --- a/docs/window.dox +++ b/docs/window.dox @@ -287,6 +287,10 @@ __GLFW_DOUBLEBUFFER__ specifies whether the framebuffer should be double buffered. You nearly always want to use double buffering. This is a hard constraint. Possible values are `GLFW_TRUE` and `GLFW_FALSE`. +@anchor GLFW_TRANSPARENT_hint +__GLFW_TRANSPARENT__ specifies whether the framebuffer will support transparency +in the background. Possible values are `GLFW_TRUE` and `GLFW_FALSE`. + @subsubsection window_hints_mtr Monitor related hints diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 58b7d7b91..5e9eac6f2 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -787,7 +787,6 @@ extern "C" { * Cursor centering [window hint](@ref GLFW_CENTER_CURSOR_hint). */ #define GLFW_CENTER_CURSOR 0x00020009 -#define GLFW_TRANSPARENT 0x0002000A /*! @brief Framebuffer bit depth hint. * @@ -869,6 +868,12 @@ extern "C" { * Framebuffer double buffering [hint](@ref GLFW_DOUBLEBUFFER). */ #define GLFW_DOUBLEBUFFER 0x00021010 +/*! @brief Framebuffer transparency hint. + * + * Framebuffer transparency [hint](@ref GLFW_TRANSPARENT_hint). + */ +#define GLFW_TRANSPARENT 0x00021011 + /*! @brief Context client API hint and attribute. * * Context client API [hint](@ref GLFW_CLIENT_API_hint) and diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 4b5cd3c1f..5b2736b40 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1085,7 +1085,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, if (wndconfig->ns.retina) [window->ns.view setWantsBestResolutionOpenGLSurface:YES]; - if (window->transparent) + if (_glfw.hints.framebuffer.transparent) { [window->ns.object setOpaque:NO]; [window->ns.object setBackgroundColor:[NSColor clearColor]]; diff --git a/src/egl_context.c b/src/egl_context.c index e5435468c..bba1efb7e 100644 --- a/src/egl_context.c +++ b/src/egl_context.c @@ -493,7 +493,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, if (ctxconfig->share) share = ctxconfig->share->context.egl.handle; - if (!chooseEGLConfig(ctxconfig, fbconfig, &config, window->transparent)) + if (!chooseEGLConfig(ctxconfig, fbconfig, &config, fbconfig->transparent)) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "EGL: Failed to find a suitable EGLConfig"); @@ -738,7 +738,7 @@ GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig, EGLint visualID = 0, count = 0; const long vimask = VisualScreenMask | VisualIDMask; - if (!chooseEGLConfig(ctxconfig, fbconfig, &native, wndconfig->transparent)) + if (!chooseEGLConfig(ctxconfig, fbconfig, &native, fbconfig->transparent)) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "EGL: Failed to find a suitable EGLConfig"); diff --git a/src/glx_context.c b/src/glx_context.c index 9d02a6fab..712e8c78e 100644 --- a/src/glx_context.c +++ b/src/glx_context.c @@ -477,7 +477,7 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, if (ctxconfig->share) share = ctxconfig->share->context.glx.handle; - if (!chooseGLXFBConfig(fbconfig, &native, window->transparent)) + if (!chooseGLXFBConfig(fbconfig, &native, fbconfig->transparent)) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "GLX: Failed to find a suitable GLXFBConfig"); @@ -665,7 +665,7 @@ GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig, GLXFBConfig native; XVisualInfo* result; - if (!chooseGLXFBConfig(fbconfig, &native, wndconfig->transparent)) + if (!chooseGLXFBConfig(fbconfig, &native, fbconfig->transparent)) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "GLX: Failed to find a suitable GLXFBConfig"); diff --git a/src/internal.h b/src/internal.h index 01517ec16..2e4e0150d 100644 --- a/src/internal.h +++ b/src/internal.h @@ -299,7 +299,6 @@ struct _GLFWwndconfig GLFWbool resizable; GLFWbool visible; GLFWbool decorated; - GLFWbool transparent; GLFWbool focused; GLFWbool autoIconify; GLFWbool floating; @@ -360,6 +359,7 @@ struct _GLFWfbconfig int samples; GLFWbool sRGB; GLFWbool doublebuffer; + GLFWbool transparent; uintptr_t handle; }; @@ -403,7 +403,6 @@ struct _GLFWwindow // Window settings and state GLFWbool resizable; GLFWbool decorated; - GLFWbool transparent; GLFWbool autoIconify; GLFWbool floating; GLFWbool shouldClose; diff --git a/src/nsgl_context.m b/src/nsgl_context.m index ef247ca7d..a7cbf00f3 100644 --- a/src/nsgl_context.m +++ b/src/nsgl_context.m @@ -296,7 +296,7 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, return GLFW_FALSE; } - if (window->transparent) + if (fbconfig->transparent) { GLint opaque = 0; [window->context.nsgl.object setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity]; diff --git a/src/wgl_context.c b/src/wgl_context.c index 990eb2992..0834e0a89 100644 --- a/src/wgl_context.c +++ b/src/wgl_context.c @@ -85,7 +85,7 @@ static int choosePixelFormat(_GLFWwindow* window, _GLFWfbconfig* u = usableConfigs + usableCount; PIXELFORMATDESCRIPTOR pfd; - if (window->transparent) { + if (fbconfig->transparent) { if (!DescribePixelFormat(window->context.wgl.dc, n, sizeof(PIXELFORMATDESCRIPTOR), @@ -168,7 +168,7 @@ static int choosePixelFormat(_GLFWwindow* window, { // Get pixel format attributes through legacy PFDs - if (!window->transparent && DescribePixelFormat(window->context.wgl.dc, + if (!fbconfig->transparent && DescribePixelFormat(window->context.wgl.dc, n, sizeof(PIXELFORMATDESCRIPTOR), &pfd)) @@ -217,8 +217,7 @@ static int choosePixelFormat(_GLFWwindow* window, } // Reiterate the selection loop without looking for transparency supporting // formats if no matching pixelformat for a transparent window were found. - if (window->transparent && !usableCount) { - window->transparent = GLFW_FALSE; + if (fbconfig->transparent && !usableCount) { free(usableConfigs); _glfwInputError(GLFW_PLATFORM_ERROR, "WGL: No pixel format found for transparent window. Ignoring transparency."); @@ -803,10 +802,12 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, } } - if (window->transparent) + if (fbconfig->transparent) { if (!setupTransparentWindow(window)) - window->transparent = GLFW_FALSE; + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to setup window as transparent as requested"); + } window->context.makeCurrent = makeContextCurrentWGL; diff --git a/src/window.c b/src/window.c index e8a68b61a..863f0ed23 100644 --- a/src/window.c +++ b/src/window.c @@ -147,6 +147,7 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, fbconfig = _glfw.hints.framebuffer; ctxconfig = _glfw.hints.context; wndconfig = _glfw.hints.window; + fbconfig.transparent = _glfw.hints.framebuffer.transparent ? GLFW_TRUE : GLFW_FALSE; wndconfig.width = width; wndconfig.height = height; @@ -180,7 +181,6 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, window->monitor = (_GLFWmonitor*) monitor; window->resizable = wndconfig.resizable; window->decorated = wndconfig.decorated; - window->transparent = wndconfig.transparent; window->autoIconify = wndconfig.autoIconify; window->floating = wndconfig.floating; window->cursorMode = GLFW_CURSOR_NORMAL; @@ -250,7 +250,6 @@ void glfwDefaultWindowHints(void) _glfw.hints.window.resizable = GLFW_TRUE; _glfw.hints.window.visible = GLFW_TRUE; _glfw.hints.window.decorated = GLFW_TRUE; - _glfw.hints.window.transparent = GLFW_FALSE; _glfw.hints.window.focused = GLFW_TRUE; _glfw.hints.window.autoIconify = GLFW_TRUE; @@ -317,6 +316,9 @@ GLFWAPI void glfwWindowHint(int hint, int value) case GLFW_DOUBLEBUFFER: _glfw.hints.framebuffer.doublebuffer = value ? GLFW_TRUE : GLFW_FALSE; return; + case GLFW_TRANSPARENT: + _glfw.hints.framebuffer.transparent = value ? GLFW_TRUE : GLFW_FALSE; + return; case GLFW_SAMPLES: _glfw.hints.framebuffer.samples = value; return; @@ -329,9 +331,6 @@ GLFWAPI void glfwWindowHint(int hint, int value) case GLFW_DECORATED: _glfw.hints.window.decorated = value ? GLFW_TRUE : GLFW_FALSE; return; - case GLFW_TRANSPARENT: - _glfw.hints.window.transparent = value ? GLFW_TRUE : GLFW_FALSE; - return; case GLFW_FOCUSED: _glfw.hints.window.focused = value ? GLFW_TRUE : GLFW_FALSE; return; @@ -733,8 +732,6 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) return window->resizable; case GLFW_DECORATED: return window->decorated; - case GLFW_TRANSPARENT: - return window->transparent; case GLFW_FLOATING: return window->floating; case GLFW_AUTO_ICONIFY: From 32e78aeb2edb5cb5add36ae575fc914f6c4fc7e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 18 Sep 2017 18:10:57 +0200 Subject: [PATCH 22/25] Add GLFW_TRANSPARENT attribute and documentation This completes support for window framebuffer transparency on Windows, macOS and X11. Note that the hint/attribute may be renamed before release to clarify its relationship to GLFW_OPACITY. Fixes #197. Closes #1079. Related to #663. Related to #715. Related to #723. Related to #1078. --- README.md | 6 +++ docs/compat.dox | 5 ++ docs/news.dox | 7 +++ docs/window.dox | 41 +++++++++++++-- examples/gears.c | 3 +- include/GLFW/glfw3.h | 11 ++-- src/cocoa_window.m | 18 ++++--- src/context.c | 3 ++ src/egl_context.c | 58 +++++++------------- src/glx_context.c | 48 +++++------------ src/internal.h | 1 + src/mir_window.c | 7 +++ src/null_window.c | 5 ++ src/wgl_context.c | 122 ++----------------------------------------- src/win32_init.c | 13 ++++- src/win32_platform.h | 48 ++++++++--------- src/win32_window.c | 86 +++++++++++++++++++++++++++++- src/window.c | 3 +- src/wl_window.c | 7 +++ src/x11_init.c | 72 ++++++++----------------- src/x11_platform.h | 51 +++++++++--------- src/x11_window.c | 25 +++++++++ 22 files changed, 322 insertions(+), 318 deletions(-) diff --git a/README.md b/README.md index dbdafe007..ff753270b 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,8 @@ information on what to include when reporting a bug. functions for accessing X11 primary selection (#894,#1056) - Added headless [OSMesa](http://mesa3d.org/osmesa.html) backend (#850) - Added definition of `GLAPIENTRY` to public header +- Added `GLFW_TRANSPARENT` window hint for enabling window framebuffer + transparency (#197,#663,#715,#723,#1078) - Added `GLFW_CENTER_CURSOR` window hint for controlling cursor centering (#749,#842) - Added `GLFW_JOYSTICK_HAT_BUTTONS` init hint (#889) @@ -289,6 +291,7 @@ skills. - Yaron Cohen-Tal - Omar Cornut - Andrew Corrigan + - Bailey Cosier - Noel Cower - Jason Daly - Jarrod Davis @@ -297,6 +300,7 @@ skills. - Michael Dickens - Роман Донченко - Mario Dorn + - Wolfgang Draxinger - Jonathan Dummer - Ralph Eastwood - Fredrik Ehnbom @@ -322,6 +326,7 @@ skills. - Erik S. V. Jansson - Toni Jovanoski - Arseny Kapoulkine + - Cem Karan - Osman Keskin - Josh Kilmer - Cameron King @@ -363,6 +368,7 @@ skills. - Andri Pálsson - Peoro - Braden Pellett + - Christopher Pelloux - Arturo J. Pérez - Anthony Pesch - Orson Peters diff --git a/docs/compat.dox b/docs/compat.dox index cabe18c33..a27f58f3d 100644 --- a/docs/compat.dox +++ b/docs/compat.dox @@ -80,6 +80,11 @@ GLFW uses the XInput2 extension to provide raw, non-accelerated mouse motion when the cursor is disabled. If the running X server does not support this extension, regular accelerated mouse motion will be used. +GLFW uses both the XRender extension and the compositing manager to support +transparent window framebuffers. If the running X server does not support this +extension or there is no running compositing manager, the `GLFW_TRANSPARENT` +framebuffer hint will have no effect. + @section compat_glx GLX extensions diff --git a/docs/news.dox b/docs/news.dox index 042468f16..86b68c41f 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -85,6 +85,13 @@ be disabled with the @ref GLFW_JOYSTICK_HAT_BUTTONS init hint. @see @ref joystick_hat +@subsection news_33_transparent Support for transparent window framebuffer + +GLFW now supports the creation of windows with transparent framebuffers on +systems with desktop compositing enabled with the @ref GLFW_TRANSPARENT window +hint and attribute. Any window decorations will still be opaque. + + @subsection news_33_centercursor Cursor centering window hint GLFW now supports controlling whether the cursor is centered over newly created diff --git a/docs/window.dox b/docs/window.dox index 17fa3002b..5dbf15bfa 100644 --- a/docs/window.dox +++ b/docs/window.dox @@ -225,6 +225,13 @@ __GLFW_CENTER_CURSOR__ specifies whether the cursor should be centered over newly created full screen windows. Possible values are `GLFW_TRUE` and `GLFW_FALSE`. This hint is ignored for windowed mode windows. +@anchor GLFW_TRANSPARENT_hint +__GLFW_TRANSPARENT__ specifies whether the window framebuffer will be +transparent. If enabled and supported by the system, the window framebuffer +alpha channel will be used to combine the framebuffer with the background. This +does not affect window decorations. Possible values are `GLFW_TRUE` and +`GLFW_FALSE`. + @subsubsection window_hints_fb Framebuffer related hints @@ -287,10 +294,6 @@ __GLFW_DOUBLEBUFFER__ specifies whether the framebuffer should be double buffered. You nearly always want to use double buffering. This is a hard constraint. Possible values are `GLFW_TRUE` and `GLFW_FALSE`. -@anchor GLFW_TRANSPARENT_hint -__GLFW_TRANSPARENT__ specifies whether the framebuffer will support transparency -in the background. Possible values are `GLFW_TRUE` and `GLFW_FALSE`. - @subsubsection window_hints_mtr Monitor related hints @@ -474,6 +477,7 @@ GLFW_AUTO_ICONIFY | `GLFW_TRUE` | `GLFW_TRUE` or `GL GLFW_FLOATING | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` GLFW_MAXIMIZED | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` GLFW_CENTER_CURSOR | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` +GLFW_TRANSPARENT | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` GLFW_RED_BITS | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` GLFW_GREEN_BITS | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` GLFW_BLUE_BITS | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` @@ -1065,6 +1069,30 @@ window contents are saved off-screen, this callback might only be called when the window or framebuffer is resized. +@subsection window_transparency Window transparency + +Window framebuffers can be made transparent on a per-pixel per-frame basis with +the [GLFW_TRANSPARENT](@ref GLFW_TRANSPARENT_hint) window hint. + +@code +glfwWindowHint(GLFW_TRANSPARENT, GLFW_TRUE); +@endcode + +If supported by the system, the window framebuffer will be composited with the +background using the framebuffer per-pixel alpha channel. This requires desktop +compositing to be enabled on the system. It does not affect window decorations. + +You can check whether the window framebuffer was successfully made transparent +with the [GLFW_TRANSPARENT](@ref GLFW_TRANSPARENT_attrib) window attribute. + +@code +if (glfwGetWindowAttrib(window, GLFW_TRANSPARENT)) +{ + // window framebuffer is currently transparent +} +@endcode + + @subsection window_attribs Window attributes Windows have a number of attributes that can be returned using @ref @@ -1134,6 +1162,11 @@ called topmost or always-on-top. This can be set before creation with the [GLFW_FLOATING](@ref GLFW_FLOATING_hint) window hint or after with @ref glfwSetWindowAttrib. +@anchor GLFW_TRANSPARENT_attrib +__GLFW_TRANSPARENT__ indicates whether the specified window has a transparent +framebuffer, i.e. the window contents is composited with the background using +the window framebuffer alpha channel. See @ref window_transparency for details. + @subsubsection window_attribs_ctx Context related attributes diff --git a/examples/gears.c b/examples/gears.c index f7526e05f..f14b300a7 100644 --- a/examples/gears.c +++ b/examples/gears.c @@ -172,7 +172,7 @@ static GLfloat angle = 0.f; /* OpenGL draw function & timing */ static void draw(void) { - glClearColor(0., 0., 0., 0.); + glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); @@ -312,7 +312,6 @@ int main(int argc, char *argv[]) } glfwWindowHint(GLFW_DEPTH_BITS, 16); - glfwWindowHint(GLFW_ALPHA_BITS, 8); glfwWindowHint(GLFW_TRANSPARENT, GLFW_TRUE); window = glfwCreateWindow( 300, 300, "Gears", NULL, NULL ); diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 5e9eac6f2..819d2cd6a 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -787,6 +787,12 @@ extern "C" { * Cursor centering [window hint](@ref GLFW_CENTER_CURSOR_hint). */ #define GLFW_CENTER_CURSOR 0x00020009 +/*! @brief Window framebuffer transparency hint and attribute + * + * Window framebuffer transparency [window hint](@ref GLFW_TRANSPARENT_hint) + * and [window attribute](@ref GLFW_TRANSPARENT_attrib). + */ +#define GLFW_TRANSPARENT 0x0002000A /*! @brief Framebuffer bit depth hint. * @@ -868,11 +874,6 @@ extern "C" { * Framebuffer double buffering [hint](@ref GLFW_DOUBLEBUFFER). */ #define GLFW_DOUBLEBUFFER 0x00021010 -/*! @brief Framebuffer transparency hint. - * - * Framebuffer transparency [hint](@ref GLFW_TRANSPARENT_hint). - */ -#define GLFW_TRANSPARENT 0x00021011 /*! @brief Context client API hint and attribute. * diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 5b2736b40..9d9476623 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -413,11 +413,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (BOOL)isOpaque { - // Set to NO even if alphaMask is not used; - // The NSView/GLFWContentView does not need to be opaque anyway, - // and to avoid keeping track of alphaMask inside the NSView we - // just return NO here instead. - return NO; + return [window->ns.object isOpaque]; } - (BOOL)canBecomeKeyView @@ -1016,7 +1012,8 @@ static GLFWbool initializeAppKit(void) // Create the Cocoa window // static GLFWbool createNativeWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig) + const _GLFWwndconfig* wndconfig, + const _GLFWfbconfig* fbconfig) { window->ns.delegate = [[GLFWWindowDelegate alloc] initWithGlfwWindow:window]; if (window->ns.delegate == nil) @@ -1085,7 +1082,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, if (wndconfig->ns.retina) [window->ns.view setWantsBestResolutionOpenGLSurface:YES]; - if (_glfw.hints.framebuffer.transparent) + if (fbconfig->transparent) { [window->ns.object setOpaque:NO]; [window->ns.object setBackgroundColor:[NSColor clearColor]]; @@ -1114,7 +1111,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!initializeAppKit()) return GLFW_FALSE; - if (!createNativeWindow(window, wndconfig)) + if (!createNativeWindow(window, wndconfig, fbconfig)) return GLFW_FALSE; if (ctxconfig->client != GLFW_NO_API) @@ -1453,6 +1450,11 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return [window->ns.object isZoomed]; } +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + return ![window->ns.object isOpaque] && ![window->ns.view isOpaque]; +} + void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) { [window->ns.object setStyleMask:getStyleMask(window)]; diff --git a/src/context.c b/src/context.c index 1a26356e1..3842f0a37 100644 --- a/src/context.c +++ b/src/context.c @@ -208,6 +208,9 @@ const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired, // not important to us here, so we count them as one missing++; } + + if (desired->transparent != current->transparent) + missing++; } // These polynomials make many small channel size differences matter diff --git a/src/egl_context.c b/src/egl_context.c index bba1efb7e..b2d11a471 100644 --- a/src/egl_context.c +++ b/src/egl_context.c @@ -87,21 +87,13 @@ static int getEGLConfigAttrib(EGLConfig config, int attrib) // static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* desired, - EGLConfig* result, - GLFWbool findTransparent) + EGLConfig* result) { EGLConfig* nativeConfigs; _GLFWfbconfig* usableConfigs; const _GLFWfbconfig* closest; int i, nativeCount, usableCount; -#if defined(_GLFW_X11) - XVisualInfo visualTemplate = {0}; - if ( !(_glfw.xrender.major || _glfw.xrender.minor) ) { - findTransparent = GLFW_FALSE; - } -#endif // _GLFW_X11 - eglGetConfigs(_glfw.egl.display, NULL, 0, &nativeCount); if (!nativeCount) { @@ -115,7 +107,6 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); usableCount = 0; -selectionloop: for (i = 0; i < nativeCount; i++) { const EGLConfig n = nativeConfigs[i]; @@ -130,31 +121,24 @@ selectionloop: continue; #if defined(_GLFW_X11) + XVisualInfo vi = {0}; + // Only consider EGLConfigs with associated Visuals - visualTemplate.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID); - if (!visualTemplate.visualid) + vi.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID); + if (!vi.visualid) continue; - if( findTransparent ) { - int n_vi; - XVisualInfo *visualinfo; - XRenderPictFormat *pictFormat; - - visualinfo = XGetVisualInfo(_glfw.x11.display, VisualIDMask, &visualTemplate, &n_vi); - if (!visualinfo) - continue; - - pictFormat = XRenderFindVisualFormat(_glfw.x11.display, visualinfo->visual); - if( !pictFormat ) { - XFree( visualinfo ); - continue; - } - - if( !pictFormat->direct.alphaMask ) { - XFree( visualinfo ); - continue; - } - XFree( visualinfo ); + if (desired->transparent) + { + int count; + XVisualInfo* vis = XGetVisualInfo(_glfw.x11.display, + VisualIDMask, &vi, + &count); + if (vis) + { + u->transparent = _glfwIsVisualTransparentX11(vis[0].visual); + XFree(vis); + } } #endif // _GLFW_X11 @@ -191,12 +175,6 @@ selectionloop: u->handle = (uintptr_t) n; usableCount++; } - // reiterate the selection loop without looking for transparency supporting - // formats if no matchig FB configs for a transparent window were found. - if( findTransparent && !usableCount ) { - findTransparent = GLFW_FALSE; - goto selectionloop; - } closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); if (closest) @@ -493,7 +471,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, if (ctxconfig->share) share = ctxconfig->share->context.egl.handle; - if (!chooseEGLConfig(ctxconfig, fbconfig, &config, fbconfig->transparent)) + if (!chooseEGLConfig(ctxconfig, fbconfig, &config)) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "EGL: Failed to find a suitable EGLConfig"); @@ -738,7 +716,7 @@ GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig, EGLint visualID = 0, count = 0; const long vimask = VisualScreenMask | VisualIDMask; - if (!chooseEGLConfig(ctxconfig, fbconfig, &native, fbconfig->transparent)) + if (!chooseEGLConfig(ctxconfig, fbconfig, &native)) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "EGL: Failed to find a suitable EGLConfig"); diff --git a/src/glx_context.c b/src/glx_context.c index 712e8c78e..708663a9e 100644 --- a/src/glx_context.c +++ b/src/glx_context.c @@ -47,10 +47,8 @@ static int getGLXFBConfigAttrib(GLXFBConfig fbconfig, int attrib) // Return the GLXFBConfig most closely matching the specified hints // -static GLFWbool chooseGLXFBConfig( - const _GLFWfbconfig* desired, - GLXFBConfig* result, - GLFWbool findTransparent) +static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, + GLXFBConfig* result) { GLXFBConfig* nativeConfigs; _GLFWfbconfig* usableConfigs; @@ -59,10 +57,6 @@ static GLFWbool chooseGLXFBConfig( const char* vendor; GLFWbool trustWindowBit = GLFW_TRUE; - if ( !(_glfw.xrender.major || _glfw.xrender.minor) ) { - findTransparent = GLFW_FALSE; - } - // HACK: This is a (hopefully temporary) workaround for Chromium // (VirtualBox GL) not setting the window bit on any GLXFBConfigs vendor = glXGetClientString(_glfw.x11.display, GLX_VENDOR); @@ -80,7 +74,6 @@ static GLFWbool chooseGLXFBConfig( usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); usableCount = 0; -selectionloop: for (i = 0; i < nativeCount; i++) { const GLXFBConfig n = nativeConfigs[i]; @@ -97,25 +90,14 @@ selectionloop: continue; } - if( findTransparent ) { - XVisualInfo *visualinfo; - XRenderPictFormat *pictFormat; - - visualinfo = glXGetVisualFromFBConfig(_glfw.x11.display, n); - if (!visualinfo) - continue; - - pictFormat = XRenderFindVisualFormat(_glfw.x11.display, visualinfo->visual); - if( !pictFormat ) { - XFree( visualinfo ); - continue; - } - - if( !pictFormat->direct.alphaMask ) { - XFree( visualinfo ); - continue; - } - XFree( visualinfo ); + if (desired->transparent) + { + XVisualInfo* vi = glXGetVisualFromFBConfig(_glfw.x11.display, n); + if (vi) + { + u->transparent = _glfwIsVisualTransparentX11(vi->visual); + XFree(vi); + } } u->redBits = getGLXFBConfigAttrib(n, GLX_RED_SIZE); @@ -147,12 +129,6 @@ selectionloop: u->handle = (uintptr_t) n; usableCount++; } - // reiterate the selection loop without looking for transparency supporting - // formats if no matchig FB configs for a transparent window were found. - if( findTransparent && !usableCount ) { - findTransparent = GLFW_FALSE; - goto selectionloop; - } closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); if (closest) @@ -477,7 +453,7 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, if (ctxconfig->share) share = ctxconfig->share->context.glx.handle; - if (!chooseGLXFBConfig(fbconfig, &native, fbconfig->transparent)) + if (!chooseGLXFBConfig(fbconfig, &native)) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "GLX: Failed to find a suitable GLXFBConfig"); @@ -665,7 +641,7 @@ GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig, GLXFBConfig native; XVisualInfo* result; - if (!chooseGLXFBConfig(fbconfig, &native, fbconfig->transparent)) + if (!chooseGLXFBConfig(fbconfig, &native)) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "GLX: Failed to find a suitable GLXFBConfig"); diff --git a/src/internal.h b/src/internal.h index 2e4e0150d..b9f55da7c 100644 --- a/src/internal.h +++ b/src/internal.h @@ -682,6 +682,7 @@ int _glfwPlatformWindowFocused(_GLFWwindow* window); int _glfwPlatformWindowIconified(_GLFWwindow* window); int _glfwPlatformWindowVisible(_GLFWwindow* window); int _glfwPlatformWindowMaximized(_GLFWwindow* window); +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window); void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled); diff --git a/src/mir_window.c b/src/mir_window.c index 4ed71f971..8b763de10 100644 --- a/src/mir_window.c +++ b/src/mir_window.c @@ -614,6 +614,13 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return mir_window_get_state(window->mir.window) == mir_window_state_maximized; } +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); + return GLFW_FALSE; +} + void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) { _glfwInputError(GLFW_PLATFORM_ERROR, diff --git a/src/null_window.c b/src/null_window.c index 2e627672c..33ff6c333 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -156,6 +156,11 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return GLFW_FALSE; } +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) { } diff --git a/src/wgl_context.c b/src/wgl_context.c index 0834e0a89..d864a47cc 100644 --- a/src/wgl_context.c +++ b/src/wgl_context.c @@ -83,20 +83,6 @@ static int choosePixelFormat(_GLFWwindow* window, { const int n = i + 1; _GLFWfbconfig* u = usableConfigs + usableCount; - PIXELFORMATDESCRIPTOR pfd; - - if (fbconfig->transparent) { - if (!DescribePixelFormat(window->context.wgl.dc, - n, - sizeof(PIXELFORMATDESCRIPTOR), - &pfd)) - { - continue; - } - - if (!(pfd.dwFlags & PFD_SUPPORT_COMPOSITION)) - continue; - } if (_glfw.wgl.ARB_pixel_format) { @@ -168,7 +154,9 @@ static int choosePixelFormat(_GLFWwindow* window, { // Get pixel format attributes through legacy PFDs - if (!fbconfig->transparent && DescribePixelFormat(window->context.wgl.dc, + PIXELFORMATDESCRIPTOR pfd; + + if (!DescribePixelFormat(window->context.wgl.dc, n, sizeof(PIXELFORMATDESCRIPTOR), &pfd)) @@ -215,14 +203,6 @@ static int choosePixelFormat(_GLFWwindow* window, u->handle = n; usableCount++; } - // Reiterate the selection loop without looking for transparency supporting - // formats if no matching pixelformat for a transparent window were found. - if (fbconfig->transparent && !usableCount) { - free(usableConfigs); - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: No pixel format found for transparent window. Ignoring transparency."); - return choosePixelFormat(window, ctxconfig, fbconfig); - } if (!usableCount) { @@ -249,21 +229,6 @@ static int choosePixelFormat(_GLFWwindow* window, return pixelFormat; } -// Returns whether desktop compositing is enabled -// -static GLFWbool isCompositionEnabled(void) -{ - if (_glfw.win32.dwmapi.instance) - { - BOOL enabled; - - if (DwmIsCompositionEnabled(&enabled) == S_OK) - return enabled; - } - - return FALSE; -} - static void makeContextCurrentWGL(_GLFWwindow* window) { if (window) @@ -292,7 +257,7 @@ static void makeContextCurrentWGL(_GLFWwindow* window) static void swapBuffersWGL(_GLFWwindow* window) { // HACK: Use DwmFlush when desktop composition is enabled - if (isCompositionEnabled() && !window->monitor) + if (_glfwIsCompositionEnabledWin32() && !window->monitor) { int count = abs(window->context.wgl.interval); while (count--) @@ -310,7 +275,7 @@ static void swapIntervalWGL(int interval) // HACK: Disable WGL swap interval when desktop composition is enabled to // avoid interfering with DWM vsync - if (isCompositionEnabled() && !window->monitor) + if (_glfwIsCompositionEnabledWin32() && !window->monitor) interval = 0; if (_glfw.wgl.EXT_swap_control) @@ -504,75 +469,6 @@ void _glfwTerminateWGL(void) attribs[index++] = v; \ } -static GLFWbool setupTransparentWindow(_GLFWwindow* window) -{ - if (!isCompositionEnabled) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Composition needed for transparent window is disabled"); - } - if (!_glfw_DwmEnableBlurBehindWindow) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Unable to load DwmEnableBlurBehindWindow required for transparent window"); - return GLFW_FALSE; - } - - HRESULT hr = S_OK; - HWND handle = window->win32.handle; - - DWM_BLURBEHIND bb = { 0 }; - bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; - bb.hRgnBlur = CreateRectRgn(0, 0, -1, -1); // makes the window transparent - bb.fEnable = TRUE; - hr = _glfw_DwmEnableBlurBehindWindow(handle, &bb); - - if (!SUCCEEDED(hr)) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to enable blur behind window required for transparent window"); - return GLFW_FALSE; - } - - // Decorated windows on Windows 8+ don't repaint the transparent background - // leaving a trail behind animations. - // Hack: making the window layered with a transparency color key seems to fix this. - // Normally, when specifying a transparency color key to be used when composing - // the layered window, all pixels painted by the window in this color will be transparent. - // That doesn't seem to be the case anymore on Windows 8+, at least when used with - // DwmEnableBlurBehindWindow + negative region. - if (window->decorated && IsWindows8OrGreater()) - { - long style = GetWindowLong(handle, GWL_EXSTYLE); - if (!style) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to retrieve extended styles. GetLastError: %d", - GetLastError()); - return GLFW_FALSE; - } - style |= WS_EX_LAYERED; - if (!SetWindowLongPtr(handle, GWL_EXSTYLE, style)) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to add layered style. GetLastError: %d", - GetLastError()); - return GLFW_FALSE; - } - if (!SetLayeredWindowAttributes(handle, - // Using a color key not equal to black to fix the trailing issue. - // When set to black, something is making the hit test not resize with the - // window frame. - RGB(0, 193, 48), - 255, - LWA_COLORKEY)) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to set layered window. GetLastError: %d", - GetLastError()); - return GLFW_FALSE; - } - } - - return GLFW_TRUE; -} - // Create the OpenGL or OpenGL ES context // GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, @@ -802,14 +698,6 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, } } - if (fbconfig->transparent) - { - if (!setupTransparentWindow(window)) - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "WGL: Failed to setup window as transparent as requested"); - - } - window->context.makeCurrent = makeContextCurrentWGL; window->context.swapBuffers = swapBuffersWGL; window->context.swapInterval = swapIntervalWGL; diff --git a/src/win32_init.c b/src/win32_init.c index 619fcfb14..0531ff13e 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -62,6 +62,17 @@ BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) #endif // _GLFW_BUILD_DLL +// HACK: Define versionhelpers.h functions manually as MinGW lacks the header +BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp) +{ + OSVERSIONINFOEXW osvi = { sizeof(osvi), major, minor, 0, 0, {0}, sp }; + DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR; + ULONGLONG cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL); + cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL); + cond = VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); + return VerifyVersionInfoW(&osvi, mask, cond); +} + // Load necessary libraries (DLLs) // static GLFWbool loadLibraries(void) @@ -131,7 +142,7 @@ static GLFWbool loadLibraries(void) GetProcAddress(_glfw.win32.dwmapi.instance, "DwmIsCompositionEnabled"); _glfw.win32.dwmapi.Flush = (PFN_DwmFlush) GetProcAddress(_glfw.win32.dwmapi.instance, "DwmFlush"); - _glfw.win32.dwmapi.DwmEnableBlurBehindWindow = (DWMENABLEBLURBEHINDWINDOW_T) + _glfw.win32.dwmapi.EnableBlurBehindWindow = (PFN_DwmEnableBlurBehindWindow) GetProcAddress(_glfw.win32.dwmapi.instance, "DwmEnableBlurBehindWindow"); } diff --git a/src/win32_platform.h b/src/win32_platform.h index bfed50514..acbb5ca2a 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -100,6 +100,9 @@ #ifndef DISPLAY_DEVICE_ACTIVE #define DISPLAY_DEVICE_ACTIVE 0x00000001 #endif +#ifndef _WIN32_WINNT_WINBLUE + #define _WIN32_WINNT_WINBLUE 0x0602 +#endif #if WINVER < 0x0601 typedef struct tagCHANGEFILTERSTRUCT @@ -113,6 +116,18 @@ typedef struct tagCHANGEFILTERSTRUCT #endif #endif /*Windows 7*/ +#if WINVER < 0x0600 +#define DWM_BB_ENABLE 0x00000001 +#define DWM_BB_BLURREGION 0x00000002 +typedef struct +{ + DWORD dwFlags; + BOOL fEnable; + HRGN hRgnBlur; + BOOL fTransitionOnMaximized; +} DWM_BLURBEHIND; +#endif /*Windows Vista*/ + #ifndef DPI_ENUMS_DECLARED typedef enum PROCESS_DPI_AWARENESS { @@ -122,30 +137,8 @@ typedef enum PROCESS_DPI_AWARENESS } PROCESS_DPI_AWARENESS; #endif /*DPI_ENUMS_DECLARED*/ -#if !defined(_DWMAPI_H_) -#define DWM_BB_ENABLE 0x00000001 -#define DWM_BB_BLURREGION 0x00000002 - -typedef struct -{ - DWORD dwFlags; - BOOL fEnable; - HRGN hRgnBlur; - BOOL fTransitionOnMaximized; -} DWM_BLURBEHIND; -#endif - // HACK: Define versionhelpers.h functions manually as MinGW lacks the header -FORCEINLINE BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp) -{ - OSVERSIONINFOEXW osvi = { sizeof(osvi), major, minor, 0, 0, {0}, sp }; - DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR; - ULONGLONG cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL); - cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL); - cond = VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); - return VerifyVersionInfoW(&osvi, mask, cond); -} - +BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp); #define IsWindowsVistaOrGreater() \ IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), \ LOBYTE(_WIN32_WINNT_VISTA), 0) @@ -216,10 +209,10 @@ typedef BOOL (WINAPI * PFN_ChangeWindowMessageFilterEx)(HWND,UINT,DWORD,PCHANGEF // dwmapi.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*); typedef HRESULT (WINAPI * PFN_DwmFlush)(VOID); -typedef HRESULT(WINAPI * DWMENABLEBLURBEHINDWINDOW_T)(HWND, const DWM_BLURBEHIND*); +typedef HRESULT(WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND,const DWM_BLURBEHIND*); #define DwmIsCompositionEnabled _glfw.win32.dwmapi.IsCompositionEnabled #define DwmFlush _glfw.win32.dwmapi.Flush -#define _glfw_DwmEnableBlurBehindWindow _glfw.win32.dwmapi.DwmEnableBlurBehindWindow +#define DwmEnableBlurBehindWindow _glfw.win32.dwmapi.EnableBlurBehindWindow // shcore.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); @@ -274,6 +267,8 @@ typedef struct _GLFWwindowWin32 GLFWbool frameAction; GLFWbool iconified; GLFWbool maximized; + // Whether to enable framebuffer transparency on DWM + GLFWbool transparent; // The last received cursor position, regardless of source int lastCursorPosX, lastCursorPosY; @@ -325,7 +320,7 @@ typedef struct _GLFWlibraryWin32 HINSTANCE instance; PFN_DwmIsCompositionEnabled IsCompositionEnabled; PFN_DwmFlush Flush; - DWMENABLEBLURBEHINDWINDOW_T DwmEnableBlurBehindWindow; + PFN_DwmEnableBlurBehindWindow EnableBlurBehindWindow; } dwmapi; struct { @@ -388,6 +383,7 @@ typedef struct _GLFWmutexWin32 GLFWbool _glfwRegisterWindowClassWin32(void); void _glfwUnregisterWindowClassWin32(void); +GLFWbool _glfwIsCompositionEnabledWin32(void); WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source); char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source); diff --git a/src/win32_window.c b/src/win32_window.c index 9ce9e4361..6bebb19bd 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -306,6 +306,55 @@ static void updateWindowStyles(const _GLFWwindow* window) SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER); } +// Update window framebuffer transparency +// +static void updateFramebufferTransparency(const _GLFWwindow* window) +{ + if (!IsWindowsVistaOrGreater()) + return; + + if (_glfwIsCompositionEnabledWin32()) + { + HRGN region = CreateRectRgn(0, 0, -1, -1); + DWM_BLURBEHIND bb = {0}; + bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; + bb.hRgnBlur = region; + bb.fEnable = TRUE; + + if (SUCCEEDED(DwmEnableBlurBehindWindow(window->win32.handle, &bb))) + { + // Decorated windows don't repaint the transparent background + // leaving a trail behind animations + // HACK: Making the window layered with a transparency color key + // seems to fix this. Normally, when specifying + // a transparency color key to be used when composing the + // layered window, all pixels painted by the window in this + // color will be transparent. That doesn't seem to be the + // case anymore, at least when used with blur behind window + // plus negative region. + LONG style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); + style |= WS_EX_LAYERED; + SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style); + + // Using a color key not equal to black to fix the trailing + // issue. When set to black, something is making the hit test + // not resize with the window frame. + SetLayeredWindowAttributes(window->win32.handle, + RGB(0, 193, 48), 255, LWA_COLORKEY); + } + + DeleteObject(region); + } + else + { + LONG style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); + style &= ~WS_EX_LAYERED; + SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style); + RedrawWindow(window->win32.handle, NULL, NULL, + RDW_ERASE | RDW_INVALIDATE | RDW_FRAME); + } +} + // Translates a GLFW standard cursor to a resource ID // static LPWSTR translateCursorShape(int shape) @@ -916,6 +965,13 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, return TRUE; } + case WM_DWMCOMPOSITIONCHANGED: + { + if (window->win32.transparent) + updateFramebufferTransparency(window); + return 0; + } + case WM_SETCURSOR: { if (LOWORD(lParam) == HTCLIENT) @@ -981,7 +1037,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, // Creates the GLFW window // static int createNativeWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig) + const _GLFWwndconfig* wndconfig, + const _GLFWfbconfig* fbconfig) { int xpos, ypos, fullWidth, fullHeight; WCHAR* wideTitle; @@ -1051,6 +1108,12 @@ static int createNativeWindow(_GLFWwindow* window, DragAcceptFiles(window->win32.handle, TRUE); + if (fbconfig->transparent) + { + updateFramebufferTransparency(window); + window->win32.transparent = GLFW_TRUE; + } + return GLFW_TRUE; } @@ -1102,6 +1165,20 @@ void _glfwUnregisterWindowClassWin32(void) UnregisterClassW(_GLFW_WNDCLASSNAME, GetModuleHandleW(NULL)); } +// Returns whether desktop compositing is enabled +// +GLFWbool _glfwIsCompositionEnabledWin32(void) +{ + if (IsWindowsVistaOrGreater()) + { + BOOL enabled; + if (SUCCEEDED(DwmIsCompositionEnabled(&enabled))) + return enabled; + } + + return FALSE; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// @@ -1112,7 +1189,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { - if (!createNativeWindow(window, wndconfig)) + if (!createNativeWindow(window, wndconfig, fbconfig)) return GLFW_FALSE; if (ctxconfig->client != GLFW_NO_API) @@ -1481,6 +1558,11 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return IsZoomed(window->win32.handle); } +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + return window->win32.transparent && _glfwIsCompositionEnabledWin32(); +} + void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) { updateWindowStyles(window); diff --git a/src/window.c b/src/window.c index 863f0ed23..57845579f 100644 --- a/src/window.c +++ b/src/window.c @@ -147,7 +147,6 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, fbconfig = _glfw.hints.framebuffer; ctxconfig = _glfw.hints.context; wndconfig = _glfw.hints.window; - fbconfig.transparent = _glfw.hints.framebuffer.transparent ? GLFW_TRUE : GLFW_FALSE; wndconfig.width = width; wndconfig.height = height; @@ -728,6 +727,8 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) return _glfwPlatformWindowVisible(window); case GLFW_MAXIMIZED: return _glfwPlatformWindowMaximized(window); + case GLFW_TRANSPARENT: + return _glfwPlatformFramebufferTransparent(window); case GLFW_RESIZABLE: return window->resizable; case GLFW_DECORATED: diff --git a/src/wl_window.c b/src/wl_window.c index 6c974dfd3..caa51d3a9 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -654,6 +654,13 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return window->wl.maximized; } +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Framebuffer transparency attribute not implemented yet"); + return GLFW_FALSE; +} + void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) { // TODO diff --git a/src/x11_init.c b/src/x11_init.c index 195c8ce4f..526d9ca25 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -651,6 +651,29 @@ static GLFWbool initExtensions(void) dlsym(_glfw.x11.x11xcb.handle, "XGetXCBConnection"); } + _glfw.x11.xrender.handle = dlopen("libXrender.so.1", RTLD_LAZY | RTLD_GLOBAL); + if (_glfw.x11.xrender.handle) + { + _glfw.x11.xrender.QueryExtension = (PFN_XRenderQueryExtension) + dlsym(_glfw.x11.xrender.handle, "XRenderQueryExtension"); + _glfw.x11.xrender.QueryVersion = (PFN_XRenderQueryVersion) + dlsym(_glfw.x11.xrender.handle, "XRenderQueryVersion"); + _glfw.x11.xrender.FindVisualFormat = (PFN_XRenderFindVisualFormat) + dlsym(_glfw.x11.xrender.handle, "XRenderFindVisualFormat"); + + if (XRenderQueryExtension(_glfw.x11.display, + &_glfw.x11.xrender.errorBase, + &_glfw.x11.xrender.eventBase)) + { + if (XRenderQueryVersion(_glfw.x11.display, + &_glfw.x11.xrender.major, + &_glfw.x11.xrender.minor)) + { + _glfw.x11.xrender.available = GLFW_TRUE; + } + } + } + // Update the key code LUT // FIXME: We should listen to XkbMapNotify events to track changes to // the keyboard mapping. @@ -717,55 +740,6 @@ static GLFWbool initExtensions(void) _glfw.x11.MOTIF_WM_HINTS = XInternAtom(_glfw.x11.display, "_MOTIF_WM_HINTS", False); - int i; - const char* sonames_xrender[] = - { -#if defined(__CYGWIN__) - "libXrender-1.so", -#else - "libXrender.so.1", - "libXrender.so", -#endif - NULL - }; - - // Xrender support is optional and not a requirement for GLX/EGL - // to work. Xrender is required for selecting a FB config that - // supports a picture format with an alpha mask, which in turn - // is required for transparent windows. I Xrender is not supported - // the GLFW_TRANSPARENT window hint is ignored. - for (i = 0; sonames_xrender[i]; i++) - { - _glfw.xrender.handle = dlopen(sonames_xrender[i], RTLD_LAZY | RTLD_GLOBAL); - if (_glfw.xrender.handle) - break; - } - _glfw.xrender.errorBase = 0; - _glfw.xrender.eventBase = 0; - _glfw.xrender.major = 0; - _glfw.xrender.minor = 0; - if (_glfw.xrender.handle) do { - int errorBase, eventBase, major, minor; - _glfw.xrender.QueryExtension = - dlsym(_glfw.xrender.handle, "XRenderQueryExtension"); - _glfw.xrender.QueryVersion = - dlsym(_glfw.xrender.handle, "XRenderQueryVersion"); - _glfw.xrender.FindVisualFormat = - dlsym(_glfw.xrender.handle, "XRenderFindVisualFormat"); - - if ( !XRenderQueryExtension(_glfw.x11.display, &errorBase, &eventBase)) { - break; - } - if ( !XRenderQueryVersion(_glfw.x11.display, &major, &minor)) { - break; - } - - _glfw.xrender.errorBase = errorBase; - _glfw.xrender.eventBase = eventBase; - _glfw.xrender.major = major; - _glfw.xrender.minor = minor; - } while(0); - return GLFW_TRUE; } diff --git a/src/x11_platform.h b/src/x11_platform.h index 9890f7034..2b1c0c622 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -116,6 +116,13 @@ typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int); #define XIQueryVersion _glfw.x11.xi.QueryVersion #define XISelectEvents _glfw.x11.xi.SelectEvents +typedef Bool (* PFN_XRenderQueryExtension)(Display*,int*,int*); +typedef Status (* PFN_XRenderQueryVersion)(Display*dpy,int*,int*); +typedef XRenderPictFormat* (* PFN_XRenderFindVisualFormat)(Display*,Visual const*); +#define XRenderQueryExtension _glfw.x11.xrender.QueryExtension +#define XRenderQueryVersion _glfw.x11.xrender.QueryVersion +#define XRenderFindVisualFormat _glfw.x11.xrender.FindVisualFormat + typedef VkFlags VkXlibSurfaceCreateFlagsKHR; typedef VkFlags VkXcbSurfaceCreateFlagsKHR; @@ -162,20 +169,10 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(Vk #define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.x11.display) #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowX11 x11 -#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11 ; _GLFWlibraryXrender xrender +#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11 #define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorX11 x11 #define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorX11 x11 -// libXrender.so function pointer typedefs -typedef Bool (*PFNXRENDERQUERYEXTENSIONPROC)(Display*,int*,int*); -typedef Status (*PFNXRENDERQUERYVERSIONPROC)(Display*dpy,int*,int*); -typedef XRenderPictFormat* (*PFNXRENDERFINDVISUALFORMATPROC)(Display*,Visual const *); - -// libXrender.so function identifier overlays -#define XRenderQueryExtension _glfw.xrender.QueryExtension -#define XRenderQueryVersion _glfw.xrender.QueryVersion -#define XRenderFindVisualFormat _glfw.xrender.FindVisualFormat - // X11-specific per-window data // @@ -189,6 +186,9 @@ typedef struct _GLFWwindowX11 GLFWbool iconified; GLFWbool maximized; + // Whether the visual supports framebuffer transparency + GLFWbool transparent; + // Cached position and size used to filter out duplicate events int width, height; int xpos, ypos; @@ -383,24 +383,20 @@ typedef struct _GLFWlibraryX11 PFN_XISelectEvents SelectEvents; } xi; + struct { + GLFWbool available; + void* handle; + int major; + int minor; + int eventBase; + int errorBase; + PFN_XRenderQueryExtension QueryExtension; + PFN_XRenderQueryVersion QueryVersion; + PFN_XRenderFindVisualFormat FindVisualFormat; + } xrender; + } _GLFWlibraryX11; -// Xrender-specific global data -typedef struct _GLFWlibraryXrender -{ - int major, minor; - int eventBase; - int errorBase; - - // dlopen handle for libGL.so.1 - void* handle; - - // Xrender functions (subset required for transparent window) - PFNXRENDERQUERYEXTENSIONPROC QueryExtension; - PFNXRENDERQUERYVERSIONPROC QueryVersion; - PFNXRENDERFINDVISUALFORMATPROC FindVisualFormat; -} _GLFWlibraryXrender; - // X11-specific per-monitor data // typedef struct _GLFWmonitorX11 @@ -434,6 +430,7 @@ unsigned long _glfwGetWindowPropertyX11(Window window, Atom property, Atom type, unsigned char** value); +GLFWbool _glfwIsVisualTransparentX11(Visual* visual); void _glfwGrabErrorHandlerX11(void); void _glfwReleaseErrorHandlerX11(void); diff --git a/src/x11_window.c b/src/x11_window.c index 3addee6ca..cf5483b0d 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -364,6 +364,7 @@ static void updateWindowMode(_GLFWwindow* window) } // Enable compositor bypass + if (!window->x11.transparent) { const unsigned long value = 1; @@ -402,6 +403,7 @@ static void updateWindowMode(_GLFWwindow* window) } // Disable compositor bypass + if (!window->x11.transparent) { XDeleteProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_BYPASS_COMPOSITOR); @@ -577,6 +579,8 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, visual, AllocNone); + window->x11.transparent = _glfwIsVisualTransparentX11(visual); + // Create the actual window { XSetWindowAttributes wa; @@ -1838,6 +1842,15 @@ unsigned long _glfwGetWindowPropertyX11(Window window, return itemCount; } +GLFWbool _glfwIsVisualTransparentX11(Visual* visual) +{ + if (!_glfw.x11.xrender.available) + return GLFW_FALSE; + + XRenderPictFormat* pf = XRenderFindVisualFormat(_glfw.x11.display, visual); + return pf && pf->direct.alphaMask; +} + // Push contents of our selection to clipboard manager // void _glfwPushSelectionToManagerX11(void) @@ -2422,6 +2435,18 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return maximized; } +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + if (!window->x11.transparent) + return GLFW_FALSE; + + // Check whether a compositing manager is running + char name[32]; + snprintf(name, sizeof(name), "_NET_WM_CM_S%u", _glfw.x11.screen); + const Atom selection = XInternAtom(_glfw.x11.display, name, False); + return XGetSelectionOwner(_glfw.x11.display, selection) != None; +} + void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) { int width, height; From 5d0d30db38e6a7dd648ea58eac6129bfdc4c9c6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 28 Sep 2017 17:32:15 +0200 Subject: [PATCH 23/25] Cleanup --- src/win32_window.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/win32_window.c b/src/win32_window.c index 6bebb19bd..490a75d2c 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -332,9 +332,9 @@ static void updateFramebufferTransparency(const _GLFWwindow* window) // color will be transparent. That doesn't seem to be the // case anymore, at least when used with blur behind window // plus negative region. - LONG style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); - style |= WS_EX_LAYERED; - SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style); + LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); + exStyle |= WS_EX_LAYERED; + SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle); // Using a color key not equal to black to fix the trailing // issue. When set to black, something is making the hit test @@ -347,9 +347,9 @@ static void updateFramebufferTransparency(const _GLFWwindow* window) } else { - LONG style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); - style &= ~WS_EX_LAYERED; - SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style); + LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); + exStyle &= ~WS_EX_LAYERED; + SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle); RedrawWindow(window->win32.handle, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME); } From da68ec56c3e00f290ed2407eb9624792f9c46b34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 27 Sep 2017 23:01:14 +0200 Subject: [PATCH 24/25] Cocoa: Start using instancetype The first tiny step towards using more modern Objective-C. --- src/cocoa_window.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 9d9476623..c8efb0d76 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -228,13 +228,13 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; _GLFWwindow* window; } -- (id)initWithGlfwWindow:(_GLFWwindow *)initWindow; +- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow; @end @implementation GLFWWindowDelegate -- (id)initWithGlfwWindow:(_GLFWwindow *)initWindow +- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow { self = [super init]; if (self != nil) @@ -381,13 +381,13 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; NSMutableAttributedString* markedText; } -- (id)initWithGlfwWindow:(_GLFWwindow *)initWindow; +- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow; @end @implementation GLFWContentView -- (id)initWithGlfwWindow:(_GLFWwindow *)initWindow +- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow { self = [super init]; if (self != nil) From 5aeb37d1b89a566d70c86c72aec1f11ce5b15c3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 28 Sep 2017 18:44:04 +0200 Subject: [PATCH 25/25] Cocoa: Fix window title being lost when untitled The window title was lost and could not be updated while the window did not have NSWindowStyleMaskTitled set. Fixes #1082. --- README.md | 1 + src/cocoa_window.m | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ff753270b..01b159c93 100644 --- a/README.md +++ b/README.md @@ -237,6 +237,7 @@ information on what to include when reporting a bug. - [Cocoa] Bugfix: A hidden or disabled cursor would become visible when a user notification was shown (#971,#1028) - [Cocoa] Bugfix: Some characters did not repeat due to Press and Hold (#1010) +- [Cocoa] Bugfix: Window title was lost when full screen or undecorated (#1082) - [WGL] Added support for `WGL_EXT_colorspace` for OpenGL ES contexts - [WGL] Added support for `WGL_ARB_create_context_no_error` - [GLX] Added support for `GLX_ARB_create_context_no_error` diff --git a/src/cocoa_window.m b/src/cocoa_window.m index c8efb0d76..0a3bcd540 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1182,7 +1182,11 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char *title) { - [window->ns.object setTitle:[NSString stringWithUTF8String:title]]; + NSString* string = [NSString stringWithUTF8String:title]; + [window->ns.object setTitle:string]; + // HACK: Set the miniwindow title explicitly as setTitle: doesn't update it + // if the window lacks NSWindowStyleMaskTitled + [window->ns.object setMiniwindowTitle:string]; } void _glfwPlatformSetWindowIcon(_GLFWwindow* window, @@ -1427,6 +1431,9 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, [window->ns.object setLevel:NSNormalWindowLevel]; [window->ns.object setHasShadow:YES]; + // HACK: Clearing NSWindowStyleMaskTitled resets and disables the window + // title property but the miniwindow title property is unaffected + [window->ns.object setTitle:[window->ns.object miniwindowTitle]]; } }