diff --git a/CMakeLists.txt b/CMakeLists.txt index 825fc453..ac011c6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -159,6 +159,10 @@ if (_GLFW_X11) # Set up library and include paths list(APPEND glfw_INCLUDE_DIRS "${X11_X11_INCLUDE_PATH}") + if (NOT X11_X11_INCLUDE_PATH) + message(FATAL_ERROR "X111 headers not found; install libx11 development package") + endif() + # Check for XRandR (modern resolution switching and gamma control) if (NOT X11_Xrandr_INCLUDE_PATH) message(FATAL_ERROR "RandR headers not found; install libxrandr development package") @@ -188,6 +192,8 @@ if (_GLFW_X11) if (NOT X11_Xshape_INCLUDE_PATH) message(FATAL_ERROR "X Shape headers not found; install libxext development package") endif() + + list(APPEND glfw_LIBRARIES ${X11_X11_LIB}) endif() #-------------------------------------------------------------------- diff --git a/README.md b/README.md index 3e529076..fdf64a8e 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,7 @@ information on what to include when reporting a bug. values to select ANGLE backend (#1380) - Added `GLFW_X11_XCB_VULKAN_SURFACE` init hint for selecting X11 Vulkan surface extension (#1793) + - Added `glfwGetWindowTitle` function for GLFWwindow for querying window titles - Made joystick subsystem initialize at first use (#1284,#1646) - Made `GLFW_DOUBLEBUFFER` a read-only window attribute - Updated the minimum required CMake version to 3.1 diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 7728dad1..97e69e19 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -2895,6 +2895,28 @@ GLFWAPI int glfwWindowShouldClose(GLFWwindow* window); */ GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value); +/*! @brief Retrieves the title of the specified window. + * + * This function gets the window title, encoded as UTF-8, of the specified + * window. + * + * @param[in] window The window to query. + * @return A copy of the UTF-8 encoded window title, or NULL if an error has occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark Do not forget to free the returned char* when you are done with it. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_title + * @sa @ref glfwSetWindowTitle + * + * @ingroup window + */ +GLFWAPI char* glfwGetWindowTitle(GLFWwindow* window); + /*! @brief Sets the title of the specified window. * * This function sets the window title, encoded as UTF-8, of the specified @@ -2912,6 +2934,7 @@ GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value); * @thread_safety This function must only be called from the main thread. * * @sa @ref window_title + * @sa @ref glfwGetWindowTitle * * @since Added in version 1.0. * @glfw3 Added window handle parameter. diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 5b8396b4..4cfe6a94 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -977,6 +977,11 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) } // autoreleasepool } +char* _glfwPlatformGetWindowTitle(_GLFWwindow* window) +{ + return NULL; +} + void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) { @autoreleasepool { diff --git a/src/internal.h b/src/internal.h index efa7e5b8..3bcbd040 100644 --- a/src/internal.h +++ b/src/internal.h @@ -650,6 +650,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); void _glfwPlatformDestroyWindow(_GLFWwindow* window); +char* _glfwPlatformGetWindowTitle(_GLFWwindow* window); void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title); void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count, const GLFWimage* images); diff --git a/src/null_window.c b/src/null_window.c index b81ab601..bca9d62f 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -150,6 +150,11 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) window->context.destroy(window); } +char* _glfwPlatformGetWindowTitle(_GLFWwindow* window) +{ + return NULL; +} + void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) { } diff --git a/src/win32_window.c b/src/win32_window.c index 13126292..9ac90939 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -1462,6 +1462,44 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) DestroyIcon(window->win32.smallIcon); } +char* _glfwPlatformGetWindowTitle(_GLFWwindow* window) +{ + int count; + SetLastError(0); + + count = GetWindowTextLengthW(window->win32.handle); + if(count == 0) + { + int error; + SetLastError(0); + error = GetLastError(); + + if(error != 0) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Querying window title failed"); + return NULL; + } + else + return calloc(1, sizeof(char)); // single \0 + + } + else + { + WCHAR* wideTitle; + char* title; + count += 1; // the \0 + + wideTitle = calloc(count, sizeof(WCHAR)); + GetWindowTextW(window->win32.handle, wideTitle, count); + + title = _glfwCreateUTF8FromWideStringWin32(wideTitle); + if(!title) + return calloc(1, sizeof(char)); // single \0 + + return title; + } +} + void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) { WCHAR* wideTitle = _glfwCreateWideStringFromUTF8Win32(title); diff --git a/src/window.c b/src/window.c index 518b27fd..e312342e 100644 --- a/src/window.c +++ b/src/window.c @@ -501,6 +501,15 @@ GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* handle, int value) window->shouldClose = value; } +GLFWAPI char* glfwGetWindowTitle(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return _glfwPlatformGetWindowTitle(window); +} + GLFWAPI void glfwSetWindowTitle(GLFWwindow* handle, const char* title) { _GLFWwindow* window = (_GLFWwindow*) handle; diff --git a/src/wl_window.c b/src/wl_window.c index f8b50df1..a05159af 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -886,6 +886,13 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) free(window->wl.monitors); } +char* _glfwPlatformGetWindowTitle(_GLFWwindow* window) +{ + if(window->wl.title) + return _glfw_strdup(window->wl.title); + return calloc(1, sizeof(char)); +} + void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) { if (window->wl.title) diff --git a/src/x11_window.c b/src/x11_window.c index d7b85994..228c82cf 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -31,7 +31,8 @@ #include #include - +#include +#include #include #include @@ -2074,6 +2075,60 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) XFlush(_glfw.x11.display); } +char* _glfwPlatformGetWindowTitle(_GLFWwindow* window) +{ + // Uses XGetWMName instead of XFetchName (which occasionally fails for some reason) + XTextProperty textProperty; + int len; + char** charList = NULL; + char* title; + + _glfwGrabErrorHandlerX11(); + if (XGetWMName(_glfw.x11.display, window->x11.handle, &textProperty) == 0) + { + _glfwInputErrorX11(GLFW_PLATFORM_ERROR, "X11: Could not get window title"); + _glfwReleaseErrorHandlerX11(); + return NULL; + } + + int ret = Xutf8TextPropertyToTextList(_glfw.x11.display, &textProperty, &charList, &len); + if (ret != Success) + { + _glfwReleaseErrorHandlerX11(); + if (ret == XNoMemory) + _glfwInputErrorX11(GLFW_PLATFORM_ERROR, "X11: No memory to convert window title to UTF-8"); + else if (ret == XLocaleNotSupported || ret == XConverterNotFound) + _glfwInputErrorX11(GLFW_PLATFORM_ERROR, "X11: Cannot convert window title, unsupported locale"); + + if (textProperty.value != NULL) + XFree(textProperty.value); + + return NULL; + } + + if (len < 1) // empty title + { + _glfwReleaseErrorHandlerX11(); + + if (textProperty.value != NULL) + XFree(textProperty.value); + if (charList != NULL) + XFreeStringList(charList); + return calloc(1, sizeof(char)); + } + + title = _glfw_strdup(charList[0]); + + _glfwReleaseErrorHandlerX11(); + + if (textProperty.value != NULL) + XFree(textProperty.value); + if (charList != NULL) + XFreeStringList(charList); + + return title; +} + void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) { if (_glfw.x11.xlib.utf8) diff --git a/testing/CMakeLists.txt b/testing/CMakeLists.txt new file mode 100644 index 00000000..09a0a8f1 --- /dev/null +++ b/testing/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.18) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +project(test + VERSION 1.0 + LANGUAGES CXX) + +add_executable(test + test.cpp +) + +find_package(OpenGL REQUIRED) + +set(GLFW_BUILD_DOCS OFF) +set(USE_MSVC_RUNTIME_LIBRARY_DLL OFF) +add_subdirectory(D:/src/glfw ${CMAKE_BINARY_DIR}/glfw-build) + +target_include_directories(test PUBLIC D:/src/glfw/include ${OPENGL_INCLUDE_DIRS}) +target_link_libraries(test glfw ${OPENGL_LIBRARIES}) \ No newline at end of file diff --git a/testing/test.cpp b/testing/test.cpp new file mode 100644 index 00000000..e12ef718 --- /dev/null +++ b/testing/test.cpp @@ -0,0 +1,48 @@ +#include + +#include + +void printWindowTitle(GLFWwindow *window) +{ + std::cout << "The window title should be '" << glfwGetWindowTitle(window) << "'.\n"; +} + +void windowShow(GLFWwindow *window) +{ + printWindowTitle(window); + while (!glfwWindowShouldClose(window)) + { + glfwWaitEvents(); + } + glfwSetWindowShouldClose(window, GLFW_FALSE); +} + +int main() +{ + if (!glfwInit()) + { + std::cerr << "Could not initialise glfw.\n"; + return -1; + } + + GLFWwindow *window = glfwCreateWindow(800, 600, "Initial title", NULL, NULL); + windowShow(window); + + glfwSetWindowTitle(window, ""); + windowShow(window); + + glfwSetWindowTitle(window, "Potato's are cool"); + windowShow(window); + + glfwSetWindowTitle(window, u8"😀 😃 😄 😁"); + windowShow(window); + + glfwDestroyWindow(window); + + window = glfwCreateWindow(800, 600, "", NULL, NULL); + windowShow(window); + + glfwTerminate(); + + return 0; +} \ No newline at end of file