diff --git a/.appveyor.yml b/.appveyor.yml index 2f4532eb..a21829a6 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -7,11 +7,6 @@ branches: - master - 3.3-stable skip_tags: true -skip_commits: - files: - - README.md - - LICENSE.md - - docs/* environment: matrix: - GENERATOR: MinGW Makefiles diff --git a/.gitignore b/.gitignore index 5519e80a..7e4c830d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,18 +7,24 @@ _ReSharper* *.dir *.vcxproj* *.sln -.vs/ +.vs +CMakeSettings.json Win32 x64 Debug Release MinSizeRel RelWithDebInfo -*.xcodeproj -bin -bin-int +*.opensdf -# CMake files +# Xcode clutter +GLFW.build +GLFW.xcodeproj + +# macOS clutter +.DS_Store + +# Makefile generator clutter Makefile CMakeCache.txt CMakeFiles @@ -85,3 +91,6 @@ tests/title tests/triangle-vulkan tests/windows +# Hazel-specific +bin +bin-int diff --git a/.travis.yml b/.travis.yml index 9b37ebfd..5f3b7393 100644 --- a/.travis.yml +++ b/.travis.yml @@ -75,12 +75,14 @@ matrix: env: - BUILD_SHARED_LIBS=ON - CFLAGS=-Werror + - MACOSX_DEPLOYMENT_TARGET=10.8 - os: osx sudo: false name: "Cocoa static library" env: - BUILD_SHARED_LIBS=OFF - CFLAGS=-Werror + - MACOSX_DEPLOYMENT_TARGET=10.8 script: - if grep -Inr '\s$' src include docs tests examples CMake *.md .gitattributes .gitignore; then echo Trailing whitespace found, aborting; diff --git a/CMake/MacOSXBundleInfo.plist.in b/CMake/Info.plist.in similarity index 100% rename from CMake/MacOSXBundleInfo.plist.in rename to CMake/Info.plist.in diff --git a/src/glfw3.pc.in b/CMake/glfw3.pc.in similarity index 100% rename from src/glfw3.pc.in rename to CMake/glfw3.pc.in diff --git a/CMake/glfw3Config.cmake.in b/CMake/glfw3Config.cmake.in new file mode 100644 index 00000000..4a13a88b --- /dev/null +++ b/CMake/glfw3Config.cmake.in @@ -0,0 +1,3 @@ +include(CMakeFindDependencyMacro) +find_dependency(Threads) +include("${CMAKE_CURRENT_LIST_DIR}/glfw3Targets.cmake") diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c7ac819..80dbc13a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.1) project(GLFW VERSION 3.4.0 LANGUAGES C) @@ -37,10 +37,6 @@ cmake_dependent_option(GLFW_USE_WAYLAND "Use Wayland for window creation" OFF cmake_dependent_option(USE_MSVC_RUNTIME_LIBRARY_DLL "Use MSVC runtime library DLL" ON "MSVC" OFF) -if (BUILD_SHARED_LIBS) - set(_GLFW_BUILD_DLL 1) -endif() - if (BUILD_SHARED_LIBS AND UNIX) # On Unix-like systems, shared libraries can use the soname system. set(GLFW_LIB_NAME glfw) @@ -75,7 +71,7 @@ if (MSVC) include(CheckIncludeFile) check_include_file(dinput.h DINPUT_H_FOUND) if (NOT DINPUT_H_FOUND) - message(FATAL_ERROR "DirectX 9 SDK not found") + message(FATAL_ERROR "DirectX 9 headers not found; install DirectX 9 SDK") endif() # Workaround for VS 2008 not shipping with stdint.h list(APPEND glfw_INCLUDE_DIRS "${GLFW_SOURCE_DIR}/deps/vs2008") @@ -199,42 +195,33 @@ if (_GLFW_X11) find_package(X11 REQUIRED) - list(APPEND glfw_PKG_DEPS "x11") - # Set up library and include paths list(APPEND glfw_INCLUDE_DIRS "${X11_X11_INCLUDE_PATH}") - list(APPEND glfw_LIBRARIES "${X11_X11_LIB}" "${CMAKE_THREAD_LIBS_INIT}") # Check for XRandR (modern resolution switching and gamma control) if (NOT X11_Xrandr_INCLUDE_PATH) - message(FATAL_ERROR "The RandR headers were not found") + message(FATAL_ERROR "RandR headers not found; install libxrandr development package") endif() # Check for Xinerama (legacy multi-monitor support) if (NOT X11_Xinerama_INCLUDE_PATH) - message(FATAL_ERROR "The Xinerama headers were not found") + message(FATAL_ERROR "Xinerama headers not found; install libxinerama development package") endif() # Check for Xkb (X keyboard extension) if (NOT X11_Xkb_INCLUDE_PATH) - message(FATAL_ERROR "The X keyboard extension headers were not found") + message(FATAL_ERROR "XKB headers not found; install X11 development package") endif() # Check for Xcursor (cursor creation from RGBA images) if (NOT X11_Xcursor_INCLUDE_PATH) - message(FATAL_ERROR "The Xcursor headers were not found") + message(FATAL_ERROR "Xcursor headers not found; install libxcursor development package") endif() # Check for XInput (modern HID input) if (NOT X11_Xi_INCLUDE_PATH) - message(FATAL_ERROR "The XInput headers were not found") + message(FATAL_ERROR "XInput headers not found; install libxi development package") endif() - - list(APPEND glfw_INCLUDE_DIRS "${X11_Xrandr_INCLUDE_PATH}" - "${X11_Xinerama_INCLUDE_PATH}" - "${X11_Xkb_INCLUDE_PATH}" - "${X11_Xcursor_INCLUDE_PATH}" - "${X11_Xi_INCLUDE_PATH}") endif() #-------------------------------------------------------------------- @@ -248,10 +235,10 @@ if (_GLFW_WAYLAND) find_package(WaylandScanner REQUIRED) find_package(WaylandProtocols 1.15 REQUIRED) - list(APPEND glfw_PKG_DEPS "wayland-egl") + list(APPEND glfw_PKG_DEPS "wayland-client") list(APPEND glfw_INCLUDE_DIRS "${Wayland_INCLUDE_DIRS}") - list(APPEND glfw_LIBRARIES "${Wayland_LIBRARIES}" "${CMAKE_THREAD_LIBS_INIT}") + list(APPEND glfw_LIBRARIES "${Wayland_LIBRARIES}") find_package(XKBCommon REQUIRED) list(APPEND glfw_INCLUDE_DIRS "${XKBCOMMON_INCLUDE_DIRS}") @@ -275,7 +262,6 @@ endif() #-------------------------------------------------------------------- if (_GLFW_OSMESA) find_package(OSMesa REQUIRED) - list(APPEND glfw_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}") endif() #-------------------------------------------------------------------- @@ -286,11 +272,10 @@ if (_GLFW_COCOA) list(APPEND glfw_LIBRARIES "-framework Cocoa" "-framework IOKit" - "-framework CoreFoundation" - "-framework CoreVideo") + "-framework CoreFoundation") set(glfw_PKG_DEPS "") - set(glfw_PKG_LIBS "-framework Cocoa -framework IOKit -framework CoreFoundation -framework CoreVideo") + set(glfw_PKG_LIBS "-framework Cocoa -framework IOKit -framework CoreFoundation") endif() #-------------------------------------------------------------------- @@ -317,7 +302,7 @@ include(CMakePackageConfigHelpers) set(GLFW_CONFIG_PATH "${CMAKE_INSTALL_LIBDIR}/cmake/glfw3") -configure_package_config_file(src/glfw3Config.cmake.in +configure_package_config_file(CMake/glfw3Config.cmake.in src/glfw3Config.cmake INSTALL_DESTINATION "${GLFW_CONFIG_PATH}" NO_CHECK_REQUIRED_COMPONENTS_MACRO) @@ -326,9 +311,7 @@ write_basic_package_version_file(src/glfw3ConfigVersion.cmake VERSION ${GLFW_VERSION} COMPATIBILITY SameMajorVersion) -configure_file(src/glfw_config.h.in src/glfw_config.h @ONLY) - -configure_file(src/glfw3.pc.in src/glfw3.pc @ONLY) +configure_file(CMake/glfw3.pc.in src/glfw3.pc @ONLY) #-------------------------------------------------------------------- # Add subdirectories @@ -365,6 +348,11 @@ if (GLFW_INSTALL) install(FILES "${GLFW_BINARY_DIR}/src/glfw3.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + if (DOXYGEN_FOUND AND GLFW_BUILD_DOCS) + install(DIRECTORY "${GLFW_BINARY_DIR}/docs/html" + DESTINATION "${CMAKE_INSTALL_DOCDIR}") + endif() + # Only generate this target if no higher-level project already has if (NOT TARGET uninstall) configure_file(CMake/cmake_uninstall.cmake.in diff --git a/README.md b/README.md index daa1be9c..c30e8a47 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ application development. It provides a simple, platform-independent API for creating windows, contexts and surfaces, reading input, handling events, etc. GLFW natively supports Windows, macOS and Linux and other Unix-like systems. On -Linux both X11 and Wayland is supported. +Linux both X11 and Wayland are supported. GLFW is licensed under the [zlib/libpng license](http://www.glfw.org/license.html). @@ -85,10 +85,11 @@ in the documentation for more information. ## Dependencies -GLFW itself depends only on the headers and libraries for your window system. +GLFW itself needs only CMake 3.1 or later and the headers and libraries for your +OS and window system. The (experimental) Wayland backend also depends on the `extra-cmake-modules` -package, which is used to generated Wayland protocol headers. +package, which is used to generate Wayland protocol headers. The examples and test programs depend on a number of tiny libraries. These are located in the `deps/` directory. @@ -123,29 +124,70 @@ information on what to include when reporting a bug. - Added `GLFW_RESIZE_EW_CURSOR` alias for `GLFW_HRESIZE_CURSOR` (#427) - Added `GLFW_RESIZE_NS_CURSOR` alias for `GLFW_VRESIZE_CURSOR` (#427) - Added `GLFW_POINTING_HAND_CURSOR` alias for `GLFW_HAND_CURSOR` (#427) + - Updated the minimum required CMake version to 3.1 - Disabled tests and examples by default when built as a CMake subdirectory - Bugfix: The CMake config-file package used an absolute path and was not relocatable (#1470) - Bugfix: Video modes with a duplicate screen area were discarded (#1555,#1556) - Bugfix: Compiling with -Wextra-semi caused warnings (#1440) + - Bugfix: Built-in mappings failed because some OEMs re-used VID/PID (#1583) - [Win32] Added the `GLFW_WIN32_KEYBOARD_MENU` window hint for enabling access to the window menu + - [Win32] Added a version info resource to the GLFW DLL - [Win32] Bugfix: `GLFW_INCLUDE_VULKAN` plus `VK_USE_PLATFORM_WIN32_KHR` caused symbol redefinition (#1524) - [Win32] Bugfix: The cursor position event was emitted before its cursor enter event (#1490) - [Win32] Bugfix: The window hint `GLFW_MAXIMIZED` did not move or resize the window (#1499) + - [Win32] Bugfix: Disabled cursor mode interfered with some non-client actions + - [Win32] Bugfix: Super key was not released after Win+V hotkey (#1622) + - [Win32] Bugfix: `glfwGetKeyName` could access out of bounds and return an + invalid pointer + - [Win32] Bugfix: Some synthetic key events were reported as `GLFW_KEY_UNKNOWN` + (#1623) + - [Cocoa] Added support for `VK_EXT_metal_surface` (#1619) + - [Cocoa] Added locating the Vulkan loader at runtime in an application bundle + - [Cocoa] Moved main menu creation to GLFW initialization time (#1649) + - [Cocoa] Removed dependency on the CoreVideo framework - [Cocoa] Bugfix: `glfwSetWindowSize` used a bottom-left anchor point (#1553) - [Cocoa] Bugfix: Window remained on screen after destruction until event poll (#1412) + - [Cocoa] Bugfix: Event processing before window creation would assert (#1543) + - [Cocoa] Bugfix: Undecorated windows could not be iconified on recent macOS + - [Cocoa] Bugfix: Touching event queue from secondary thread before main thread + would abort (#1649) - [X11] Bugfix: The CMake files did not check for the XInput headers (#1480) - [X11] Bugfix: Key names were not updated when the keyboard layout changed (#1462,#1528) - [X11] Bugfix: Decorations could not be enabled after window creation (#1566) - [X11] Bugfix: Content scale fallback value could be inconsistent (#1578) + - [X11] Bugfix: `glfwMaximizeWindow` had no effect on hidden windows + - [X11] Bugfix: Clearing `GLFW_FLOATING` on a hidden window caused invalid read + - [X11] Bugfix: Changing `GLFW_FLOATING` on a hidden window could silently fail + - [X11] Bugfix: Disabled cursor mode was interrupted by indicator windows + - [X11] Bugfix: Monitor physical dimensions could be reported as zero mm + - [X11] Bugfix: Window position events were not emitted during resizing (#1613) + - [X11] Bugfix: `glfwFocusWindow` could terminate on older WMs or without a WM + - [X11] Bugfix: Querying a disconnected monitor could segfault (#1602) + - [X11] Bugfix: IME input of CJK was broken for "C" locale (#1587,#1636) + - [X11] Bugfix: Termination would segfault if the IM had been destroyed + - [X11] Bugfix: Any IM started after initialization would not be detected + - [X11] Bugfix: Xlib errors caused by other parts of the application could be + reported as GLFW errors + - [X11] Bugfix: A handle race condition could cause a `BadWindow` error (#1633) + - [X11] Bugfix: XKB path used keysyms instead of physical locations for + non-printable keys (#1598) + - [X11] Bugfix: Function keys were mapped to `GLFW_KEY_UNKNOWN` for some layout + combinaitons (#1598) + - [Wayland] Removed support for `wl_shell` (#1443) - [Wayland] Bugfix: The `GLFW_HAND_CURSOR` shape used the wrong image (#1432) + - [Wayland] Bugfix: `CLOCK_MONOTONIC` was not correctly enabled + - [POSIX] Bugfix: `CLOCK_MONOTONIC` was not correctly tested for or enabled - [NSGL] Removed enforcement of forward-compatible flag for core contexts + - [NSGL] Bugfix: `GLFW_COCOA_RETINA_FRAMEBUFFER` had no effect on newer + macOS versions (#1442) + - [NSGL] Bugfix: Workaround for swap interval on 10.14 broke on 10.12 (#1483) ## Contact @@ -218,6 +260,7 @@ skills. - GeO4d - Marcus Geelnard - Charles Giessen + - Ryan C. Gordon - Stephen Gowen - Kovid Goyal - Eloi Marín Gratacós @@ -238,6 +281,7 @@ skills. - Cem Karan - Osman Keskin - Josh Kilmer + - Byunghoon Kim - Cameron King - Peter Knut - Christoph Kubisch @@ -245,6 +289,7 @@ skills. - Rokas Kupstys - Konstantin Käfer - Eric Larson + - Francis Lecavalier - Robin Leffmann - Glenn Lewis - Shane Liesegang @@ -253,6 +298,7 @@ skills. - Eyal Lotem - Aaron Loucks - Luflosi + - lukect - Tristam MacDonald - Hans Mackowiak - Дмитри Малышев @@ -298,6 +344,7 @@ skills. - Alexandre Pretyman - Pablo Prietz - przemekmirek + - pthom - Guillaume Racicot - Philip Rideout - Eddie Ringle @@ -312,6 +359,7 @@ skills. - Matt Sealey - Steve Sexton - Arkady Shapkin + - Ali Sherief - Yoshiki Shibukawa - Dmitri Shuralyov - Daniel Skorupski @@ -328,6 +376,7 @@ skills. - Paul Sultana - Nathan Sweet - TTK-Bandit + - Jared Tiala - Sergey Tikhomirov - Arthur Tombs - Ioannis Tsakpinis diff --git a/deps/linmath.h b/deps/linmath.h index 9c2e2a0a..c4c0e24e 100644 --- a/deps/linmath.h +++ b/deps/linmath.h @@ -3,31 +3,40 @@ #include -#ifdef _MSC_VER -#define inline __inline +/* 2020-03-02 Camilla Löwy + * - Added inclusion of string.h for memcpy + * - Replaced tan and acos with tanf and acosf + * - Replaced double constants with float equivalents + */ +#include + +#ifdef LINMATH_NO_INLINE +#define LINMATH_H_FUNC static +#else +#define LINMATH_H_FUNC static inline #endif #define LINMATH_H_DEFINE_VEC(n) \ typedef float vec##n[n]; \ -static inline void vec##n##_add(vec##n r, vec##n const a, vec##n const b) \ +LINMATH_H_FUNC void vec##n##_add(vec##n r, vec##n const a, vec##n const b) \ { \ int i; \ for(i=0; ib[i] ? a[i] : b[i]; \ } LINMATH_H_DEFINE_VEC(2) LINMATH_H_DEFINE_VEC(3) LINMATH_H_DEFINE_VEC(4) -static inline void vec3_mul_cross(vec3 r, vec3 const a, vec3 const b) +LINMATH_H_FUNC void vec3_mul_cross(vec3 r, vec3 const a, vec3 const b) { r[0] = a[1]*b[2] - a[2]*b[1]; r[1] = a[2]*b[0] - a[0]*b[2]; r[2] = a[0]*b[1] - a[1]*b[0]; } -static inline void vec3_reflect(vec3 r, vec3 const v, vec3 const n) +LINMATH_H_FUNC void vec3_reflect(vec3 r, vec3 const v, vec3 const n) { float p = 2.f*vec3_mul_inner(v, n); int i; @@ -64,7 +85,7 @@ static inline void vec3_reflect(vec3 r, vec3 const v, vec3 const n) r[i] = v[i] - p*n[i]; } -static inline void vec4_mul_cross(vec4 r, vec4 a, vec4 b) +LINMATH_H_FUNC void vec4_mul_cross(vec4 r, vec4 a, vec4 b) { r[0] = a[1]*b[2] - a[2]*b[1]; r[1] = a[2]*b[0] - a[0]*b[2]; @@ -72,7 +93,7 @@ static inline void vec4_mul_cross(vec4 r, vec4 a, vec4 b) r[3] = 1.f; } -static inline void vec4_reflect(vec4 r, vec4 v, vec4 n) +LINMATH_H_FUNC void vec4_reflect(vec4 r, vec4 v, vec4 n) { float p = 2.f*vec4_mul_inner(v, n); int i; @@ -81,58 +102,58 @@ static inline void vec4_reflect(vec4 r, vec4 v, vec4 n) } typedef vec4 mat4x4[4]; -static inline void mat4x4_identity(mat4x4 M) +LINMATH_H_FUNC void mat4x4_identity(mat4x4 M) { int i, j; for(i=0; i<4; ++i) for(j=0; j<4; ++j) M[i][j] = i==j ? 1.f : 0.f; } -static inline void mat4x4_dup(mat4x4 M, mat4x4 N) +LINMATH_H_FUNC void mat4x4_dup(mat4x4 M, mat4x4 N) { int i, j; for(i=0; i<4; ++i) for(j=0; j<4; ++j) M[i][j] = N[i][j]; } -static inline void mat4x4_row(vec4 r, mat4x4 M, int i) +LINMATH_H_FUNC void mat4x4_row(vec4 r, mat4x4 M, int i) { int k; for(k=0; k<4; ++k) r[k] = M[k][i]; } -static inline void mat4x4_col(vec4 r, mat4x4 M, int i) +LINMATH_H_FUNC void mat4x4_col(vec4 r, mat4x4 M, int i) { int k; for(k=0; k<4; ++k) r[k] = M[i][k]; } -static inline void mat4x4_transpose(mat4x4 M, mat4x4 N) +LINMATH_H_FUNC void mat4x4_transpose(mat4x4 M, mat4x4 N) { int i, j; for(j=0; j<4; ++j) for(i=0; i<4; ++i) M[i][j] = N[j][i]; } -static inline void mat4x4_add(mat4x4 M, mat4x4 a, mat4x4 b) +LINMATH_H_FUNC void mat4x4_add(mat4x4 M, mat4x4 a, mat4x4 b) { int i; for(i=0; i<4; ++i) vec4_add(M[i], a[i], b[i]); } -static inline void mat4x4_sub(mat4x4 M, mat4x4 a, mat4x4 b) +LINMATH_H_FUNC void mat4x4_sub(mat4x4 M, mat4x4 a, mat4x4 b) { int i; for(i=0; i<4; ++i) vec4_sub(M[i], a[i], b[i]); } -static inline void mat4x4_scale(mat4x4 M, mat4x4 a, float k) +LINMATH_H_FUNC void mat4x4_scale(mat4x4 M, mat4x4 a, float k) { int i; for(i=0; i<4; ++i) vec4_scale(M[i], a[i], k); } -static inline void mat4x4_scale_aniso(mat4x4 M, mat4x4 a, float x, float y, float z) +LINMATH_H_FUNC void mat4x4_scale_aniso(mat4x4 M, mat4x4 a, float x, float y, float z) { int i; vec4_scale(M[0], a[0], x); @@ -142,7 +163,7 @@ static inline void mat4x4_scale_aniso(mat4x4 M, mat4x4 a, float x, float y, floa M[3][i] = a[3][i]; } } -static inline void mat4x4_mul(mat4x4 M, mat4x4 a, mat4x4 b) +LINMATH_H_FUNC void mat4x4_mul(mat4x4 M, mat4x4 a, mat4x4 b) { mat4x4 temp; int k, r, c; @@ -153,7 +174,7 @@ static inline void mat4x4_mul(mat4x4 M, mat4x4 a, mat4x4 b) } mat4x4_dup(M, temp); } -static inline void mat4x4_mul_vec4(vec4 r, mat4x4 M, vec4 v) +LINMATH_H_FUNC void mat4x4_mul_vec4(vec4 r, mat4x4 M, vec4 v) { int i, j; for(j=0; j<4; ++j) { @@ -162,14 +183,14 @@ static inline void mat4x4_mul_vec4(vec4 r, mat4x4 M, vec4 v) r[j] += M[i][j] * v[i]; } } -static inline void mat4x4_translate(mat4x4 T, float x, float y, float z) +LINMATH_H_FUNC void mat4x4_translate(mat4x4 T, float x, float y, float z) { mat4x4_identity(T); T[3][0] = x; T[3][1] = y; T[3][2] = z; } -static inline void mat4x4_translate_in_place(mat4x4 M, float x, float y, float z) +LINMATH_H_FUNC void mat4x4_translate_in_place(mat4x4 M, float x, float y, float z) { vec4 t = {x, y, z, 0}; vec4 r; @@ -179,33 +200,32 @@ static inline void mat4x4_translate_in_place(mat4x4 M, float x, float y, float z M[3][i] += vec4_mul_inner(r, t); } } -static inline void mat4x4_from_vec3_mul_outer(mat4x4 M, vec3 a, vec3 b) +LINMATH_H_FUNC void mat4x4_from_vec3_mul_outer(mat4x4 M, vec3 a, vec3 b) { int i, j; for(i=0; i<4; ++i) for(j=0; j<4; ++j) M[i][j] = i<3 && j<3 ? a[i] * b[j] : 0.f; } -static inline void mat4x4_rotate(mat4x4 R, mat4x4 M, float x, float y, float z, float angle) +LINMATH_H_FUNC void mat4x4_rotate(mat4x4 R, mat4x4 M, float x, float y, float z, float angle) { float s = sinf(angle); float c = cosf(angle); vec3 u = {x, y, z}; if(vec3_len(u) > 1e-4) { - mat4x4 T, C, S = {{0}}; - vec3_norm(u, u); + mat4x4 T; mat4x4_from_vec3_mul_outer(T, u, u); - S[1][2] = u[0]; - S[2][1] = -u[0]; - S[2][0] = u[1]; - S[0][2] = -u[1]; - S[0][1] = u[2]; - S[1][0] = -u[2]; - + mat4x4 S = { + { 0, u[2], -u[1], 0}, + {-u[2], 0, u[0], 0}, + { u[1], -u[0], 0, 0}, + { 0, 0, 0, 0} + }; mat4x4_scale(S, S, s); + mat4x4 C; mat4x4_identity(C); mat4x4_sub(C, C, T); @@ -214,13 +234,13 @@ static inline void mat4x4_rotate(mat4x4 R, mat4x4 M, float x, float y, float z, mat4x4_add(T, T, C); mat4x4_add(T, T, S); - T[3][3] = 1.; + T[3][3] = 1.; mat4x4_mul(R, M, T); } else { mat4x4_dup(R, M); } } -static inline void mat4x4_rotate_X(mat4x4 Q, mat4x4 M, float angle) +LINMATH_H_FUNC void mat4x4_rotate_X(mat4x4 Q, mat4x4 M, float angle) { float s = sinf(angle); float c = cosf(angle); @@ -232,7 +252,7 @@ static inline void mat4x4_rotate_X(mat4x4 Q, mat4x4 M, float angle) }; mat4x4_mul(Q, M, R); } -static inline void mat4x4_rotate_Y(mat4x4 Q, mat4x4 M, float angle) +LINMATH_H_FUNC void mat4x4_rotate_Y(mat4x4 Q, mat4x4 M, float angle) { float s = sinf(angle); float c = cosf(angle); @@ -244,7 +264,7 @@ static inline void mat4x4_rotate_Y(mat4x4 Q, mat4x4 M, float angle) }; mat4x4_mul(Q, M, R); } -static inline void mat4x4_rotate_Z(mat4x4 Q, mat4x4 M, float angle) +LINMATH_H_FUNC void mat4x4_rotate_Z(mat4x4 Q, mat4x4 M, float angle) { float s = sinf(angle); float c = cosf(angle); @@ -256,9 +276,8 @@ static inline void mat4x4_rotate_Z(mat4x4 Q, mat4x4 M, float angle) }; mat4x4_mul(Q, M, R); } -static inline void mat4x4_invert(mat4x4 T, mat4x4 M) +LINMATH_H_FUNC void mat4x4_invert(mat4x4 T, mat4x4 M) { - float idet; float s[6]; float c[6]; s[0] = M[0][0]*M[1][1] - M[1][0]*M[0][1]; @@ -274,10 +293,10 @@ static inline void mat4x4_invert(mat4x4 T, mat4x4 M) c[3] = M[2][1]*M[3][2] - M[3][1]*M[2][2]; c[4] = M[2][1]*M[3][3] - M[3][1]*M[2][3]; c[5] = M[2][2]*M[3][3] - M[3][2]*M[2][3]; - + /* Assumes it is invertible */ - idet = 1.0f/( s[0]*c[5]-s[1]*c[4]+s[2]*c[3]+s[3]*c[2]-s[4]*c[1]+s[5]*c[0] ); - + float idet = 1.0f/( s[0]*c[5]-s[1]*c[4]+s[2]*c[3]+s[3]*c[2]-s[4]*c[1]+s[5]*c[0] ); + T[0][0] = ( M[1][1] * c[5] - M[1][2] * c[4] + M[1][3] * c[3]) * idet; T[0][1] = (-M[0][1] * c[5] + M[0][2] * c[4] - M[0][3] * c[3]) * idet; T[0][2] = ( M[3][1] * s[5] - M[3][2] * s[4] + M[3][3] * s[3]) * idet; @@ -298,35 +317,34 @@ static inline void mat4x4_invert(mat4x4 T, mat4x4 M) T[3][2] = (-M[3][0] * s[3] + M[3][1] * s[1] - M[3][2] * s[0]) * idet; T[3][3] = ( M[2][0] * s[3] - M[2][1] * s[1] + M[2][2] * s[0]) * idet; } -static inline void mat4x4_orthonormalize(mat4x4 R, mat4x4 M) +LINMATH_H_FUNC void mat4x4_orthonormalize(mat4x4 R, mat4x4 M) { + mat4x4_dup(R, M); float s = 1.; vec3 h; - mat4x4_dup(R, M); vec3_norm(R[2], R[2]); - - s = vec3_mul_inner(R[1], R[2]); - vec3_scale(h, R[2], s); - vec3_sub(R[1], R[1], h); - vec3_norm(R[2], R[2]); - + s = vec3_mul_inner(R[1], R[2]); vec3_scale(h, R[2], s); vec3_sub(R[1], R[1], h); vec3_norm(R[1], R[1]); + s = vec3_mul_inner(R[0], R[2]); + vec3_scale(h, R[2], s); + vec3_sub(R[0], R[0], h); + s = vec3_mul_inner(R[0], R[1]); vec3_scale(h, R[1], s); vec3_sub(R[0], R[0], h); vec3_norm(R[0], R[0]); } -static inline void mat4x4_frustum(mat4x4 M, float l, float r, float b, float t, float n, float f) +LINMATH_H_FUNC void mat4x4_frustum(mat4x4 M, float l, float r, float b, float t, float n, float f) { M[0][0] = 2.f*n/(r-l); M[0][1] = M[0][2] = M[0][3] = 0.f; - + M[1][1] = 2.f*n/(t-b); M[1][0] = M[1][2] = M[1][3] = 0.f; @@ -334,11 +352,11 @@ static inline void mat4x4_frustum(mat4x4 M, float l, float r, float b, float t, M[2][1] = (t+b)/(t-b); M[2][2] = -(f+n)/(f-n); M[2][3] = -1.f; - + M[3][2] = -2.f*(f*n)/(f-n); M[3][0] = M[3][1] = M[3][3] = 0.f; } -static inline void mat4x4_ortho(mat4x4 M, float l, float r, float b, float t, float n, float f) +LINMATH_H_FUNC void mat4x4_ortho(mat4x4 M, float l, float r, float b, float t, float n, float f) { M[0][0] = 2.f/(r-l); M[0][1] = M[0][2] = M[0][3] = 0.f; @@ -348,17 +366,17 @@ static inline void mat4x4_ortho(mat4x4 M, float l, float r, float b, float t, fl M[2][2] = -2.f/(f-n); M[2][0] = M[2][1] = M[2][3] = 0.f; - + M[3][0] = -(r+l)/(r-l); M[3][1] = -(t+b)/(t-b); M[3][2] = -(f+n)/(f-n); M[3][3] = 1.f; } -static inline void mat4x4_perspective(mat4x4 m, float y_fov, float aspect, float n, float f) +LINMATH_H_FUNC void mat4x4_perspective(mat4x4 m, float y_fov, float aspect, float n, float f) { /* NOTE: Degrees are an unhandy unit to work with. * linmath.h uses radians for everything! */ - float const a = 1.f / (float) tan(y_fov / 2.f); + float const a = 1.f / tanf(y_fov / 2.f); m[0][0] = a / aspect; m[0][1] = 0.f; @@ -380,7 +398,7 @@ static inline void mat4x4_perspective(mat4x4 m, float y_fov, float aspect, float m[3][2] = -((2.f * f * n) / (f - n)); m[3][3] = 0.f; } -static inline void mat4x4_look_at(mat4x4 m, vec3 eye, vec3 center, vec3 up) +LINMATH_H_FUNC void mat4x4_look_at(mat4x4 m, vec3 eye, vec3 center, vec3 up) { /* Adapted from Android's OpenGL Matrix.java. */ /* See the OpenGL GLUT documentation for gluLookAt for a description */ @@ -389,15 +407,14 @@ static inline void mat4x4_look_at(mat4x4 m, vec3 eye, vec3 center, vec3 up) /* TODO: The negation of of can be spared by swapping the order of * operands in the following cross products in the right way. */ vec3 f; + vec3_sub(f, center, eye); + vec3_norm(f, f); + vec3 s; - vec3 t; - - vec3_sub(f, center, eye); - vec3_norm(f, f); - vec3_mul_cross(s, f, up); vec3_norm(s, s); + vec3 t; vec3_mul_cross(t, s, f); m[0][0] = s[0]; @@ -424,24 +441,24 @@ static inline void mat4x4_look_at(mat4x4 m, vec3 eye, vec3 center, vec3 up) } typedef float quat[4]; -static inline void quat_identity(quat q) +LINMATH_H_FUNC void quat_identity(quat q) { q[0] = q[1] = q[2] = 0.f; q[3] = 1.f; } -static inline void quat_add(quat r, quat a, quat b) +LINMATH_H_FUNC void quat_add(quat r, quat a, quat b) { int i; for(i=0; i<4; ++i) r[i] = a[i] + b[i]; } -static inline void quat_sub(quat r, quat a, quat b) +LINMATH_H_FUNC void quat_sub(quat r, quat a, quat b) { int i; for(i=0; i<4; ++i) r[i] = a[i] - b[i]; } -static inline void quat_mul(quat r, quat p, quat q) +LINMATH_H_FUNC void quat_mul(quat r, quat p, quat q) { vec3 w; vec3_mul_cross(r, p, q); @@ -451,13 +468,13 @@ static inline void quat_mul(quat r, quat p, quat q) vec3_add(r, r, w); r[3] = p[3]*q[3] - vec3_mul_inner(p, q); } -static inline void quat_scale(quat r, quat v, float s) +LINMATH_H_FUNC void quat_scale(quat r, quat v, float s) { int i; for(i=0; i<4; ++i) r[i] = v[i] * s; } -static inline float quat_inner_product(quat a, quat b) +LINMATH_H_FUNC float quat_inner_product(quat a, quat b) { float p = 0.f; int i; @@ -465,42 +482,43 @@ static inline float quat_inner_product(quat a, quat b) p += b[i]*a[i]; return p; } -static inline void quat_conj(quat r, quat q) +LINMATH_H_FUNC void quat_conj(quat r, quat q) { int i; for(i=0; i<3; ++i) r[i] = -q[i]; r[3] = q[3]; } -static inline void quat_rotate(quat r, float angle, vec3 axis) { - int i; +LINMATH_H_FUNC void quat_rotate(quat r, float angle, vec3 axis) { vec3 v; vec3_scale(v, axis, sinf(angle / 2)); + int i; for(i=0; i<3; ++i) r[i] = v[i]; r[3] = cosf(angle / 2); } #define quat_norm vec4_norm -static inline void quat_mul_vec3(vec3 r, quat q, vec3 v) +LINMATH_H_FUNC void quat_mul_vec3(vec3 r, quat q, vec3 v) { /* * Method by Fabian 'ryg' Giessen (of Farbrausch) t = 2 * cross(q.xyz, v) v' = v + q.w * t + cross(q.xyz, t) */ - vec3 t = {q[0], q[1], q[2]}; + vec3 t; + vec3 q_xyz = {q[0], q[1], q[2]}; vec3 u = {q[0], q[1], q[2]}; - vec3_mul_cross(t, t, v); + vec3_mul_cross(t, q_xyz, v); vec3_scale(t, t, 2); - vec3_mul_cross(u, u, t); + vec3_mul_cross(u, q_xyz, t); vec3_scale(t, t, q[3]); vec3_add(r, v, t); vec3_add(r, r, u); } -static inline void mat4x4_from_quat(mat4x4 M, quat q) +LINMATH_H_FUNC void mat4x4_from_quat(mat4x4 M, quat q) { float a = q[3]; float b = q[0]; @@ -510,7 +528,7 @@ static inline void mat4x4_from_quat(mat4x4 M, quat q) float b2 = b*b; float c2 = c*c; float d2 = d*d; - + M[0][0] = a2 + b2 - c2 - d2; M[0][1] = 2.f*(b*c + a*d); M[0][2] = 2.f*(b*d - a*c); @@ -530,7 +548,7 @@ static inline void mat4x4_from_quat(mat4x4 M, quat q) M[3][3] = 1.f; } -static inline void mat4x4o_mul_quat(mat4x4 R, mat4x4 M, quat q) +LINMATH_H_FUNC void mat4x4o_mul_quat(mat4x4 R, mat4x4 M, quat q) { /* XXX: The way this is written only works for othogonal matrices. */ /* TODO: Take care of non-orthogonal case. */ @@ -541,7 +559,7 @@ static inline void mat4x4o_mul_quat(mat4x4 R, mat4x4 M, quat q) R[3][0] = R[3][1] = R[3][2] = 0.f; R[3][3] = 1.f; } -static inline void quat_from_mat4x4(quat q, mat4x4 M) +LINMATH_H_FUNC void quat_from_mat4x4(quat q, mat4x4 M) { float r=0.f; int i; @@ -557,7 +575,7 @@ static inline void quat_from_mat4x4(quat q, mat4x4 M) p = &perm[i]; } - r = (float) sqrt(1.f + M[p[0]][p[0]] - M[p[1]][p[1]] - M[p[2]][p[2]] ); + r = sqrtf(1.f + M[p[0]][p[0]] - M[p[1]][p[1]] - M[p[2]][p[2]] ); if(r < 1e-6) { q[0] = 1.f; @@ -571,4 +589,33 @@ static inline void quat_from_mat4x4(quat q, mat4x4 M) q[3] = (M[p[2]][p[1]] - M[p[1]][p[2]])/(2.f*r); } +LINMATH_H_FUNC void mat4x4_arcball(mat4x4 R, mat4x4 M, vec2 _a, vec2 _b, float s) +{ + vec2 a; memcpy(a, _a, sizeof(a)); + vec2 b; memcpy(b, _b, sizeof(b)); + + float z_a = 0.; + float z_b = 0.; + + if(vec2_len(a) < 1.f) { + z_a = sqrtf(1.f - vec2_mul_inner(a, a)); + } else { + vec2_norm(a, a); + } + + if(vec2_len(b) < 1.f) { + z_b = sqrtf(1.f - vec2_mul_inner(b, b)); + } else { + vec2_norm(b, b); + } + + vec3 a_ = {a[0], a[1], z_a}; + vec3 b_ = {b[0], b[1], z_b}; + + vec3 c_; + vec3_mul_cross(c_, a_, b_); + + float const angle = acosf(vec3_mul_inner(a_, b_)) * s; + mat4x4_rotate(R, M, c_[0], c_[1], c_[2], angle); +} #endif diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index df9e2147..2347858e 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -30,3 +30,5 @@ add_custom_target(docs ALL "${DOXYGEN_EXECUTABLE}" WORKING_DIRECTORY "${GLFW_BINARY_DIR}/docs" COMMENT "Generating HTML documentation" VERBATIM) +set_target_properties(docs PROPERTIES FOLDER "GLFW3") + diff --git a/docs/build.dox b/docs/build.dox index 127c9f06..03d0fd7d 100644 --- a/docs/build.dox +++ b/docs/build.dox @@ -78,6 +78,11 @@ compiler that the GLFW functions are defined in a DLL. The following macros control which OpenGL or OpenGL ES API header is included. Only one of these may be defined at a time. +@note GLFW does not provide any of the API headers mentioned below. They are +provided by your development environment or your OpenGL, OpenGL ES or Vulkan +SDK, and most of them can be downloaded from the +[Khronos Registry](https://www.khronos.org/registry/). + @anchor GLFW_INCLUDE_GLCOREARB __GLFW_INCLUDE_GLCOREARB__ makes the GLFW header include the modern `GL/glcorearb.h` header (`OpenGL/gl3.h` on macOS) instead of the regular OpenGL @@ -129,10 +134,6 @@ header selected above. This should only be used with the standard OpenGL header and only for compatibility with legacy code. GLU has been deprecated and should not be used in new code. -@note GLFW does not provide any of the API headers mentioned above. They must -be provided by your development environment or your OpenGL, OpenGL ES or Vulkan -SDK. - @note None of these macros may be defined during the compilation of GLFW itself. If your build includes GLFW and you define any these in your build files, make sure they are not applied to the GLFW sources. @@ -166,16 +167,11 @@ must also explicitly link with `gdi32`. Other toolchains including MinGW-w64 include it in the set of default libraries along with other dependencies like `user32` and `kernel32`. -If you are using GLU, you must also link with `glu32`. - The link library for the GLFW DLL is named `glfw3dll`. When compiling an application that uses the DLL version of GLFW, you need to define the @ref GLFW_DLL macro _before_ any inclusion of the GLFW header. This can be done either with a compiler switch or by defining it in your source code. -An application using the GLFW DLL does not need to link against any of its -dependencies, but you still have to link against `glu32` if it uses GLU. - @subsection build_link_cmake_source With CMake and GLFW source @@ -187,51 +183,38 @@ With a few changes to your `CMakeLists.txt` you can have the GLFW source tree built along with your application. Add the root directory of the GLFW source tree to your project. This will add -the `glfw` target and the necessary cache variables to your project. +the `glfw` target to your project. @code{.cmake} add_subdirectory(path/to/glfw) @endcode -Once GLFW has been added to the project, link against it with the `glfw` target. -This adds all link-time dependencies of GLFW as it is currently configured, -the include directory for the GLFW header and, when applicable, the @ref -GLFW_DLL macro. +Once GLFW has been added, link your application against the `glfw` target. +This adds the GLFW library and its link-time dependencies as it is currently +configured, the include directory for the GLFW header and, when applicable, the +@ref GLFW_DLL macro. @code{.cmake} target_link_libraries(myapp glfw) @endcode -Note that the dependencies do not include OpenGL or GLU, as GLFW loads any -OpenGL, OpenGL ES or Vulkan libraries it needs at runtime and does not use GLU. -If your application calls OpenGL directly, instead of using a modern -[extension loader library](@ref context_glext_auto) you can find it by requiring -the OpenGL package. +Note that the `glfw` target does not depend on OpenGL, as GLFW loads any OpenGL, +OpenGL ES or Vulkan libraries it needs at runtime. If your application calls +OpenGL directly, instead of using a modern +[extension loader library](@ref context_glext_auto), use the OpenGL CMake +package. @code{.cmake} find_package(OpenGL REQUIRED) @endcode -If OpenGL is found, the `OPENGL_FOUND` variable is true and the -`OPENGL_INCLUDE_DIR` and `OPENGL_gl_LIBRARY` cache variables can be used. +If OpenGL is found, the `OpenGL::GL` target is added to your project, containing +library and include directory paths. Link against this like any other library. @code{.cmake} -target_include_directories(myapp PUBLIC ${OPENGL_INCLUDE_DIR}) -target_link_libraries(myapp ${OPENGL_gl_LIBRARY}) +target_link_libraries(myapp OpenGL::GL) @endcode -The OpenGL CMake package also looks for GLU. If GLU is found, the -`OPENGL_GLU_FOUND` variable is true and the `OPENGL_INCLUDE_DIR` and -`OPENGL_glu_LIBRARY` cache variables can be used. - -@code{.cmake} -target_link_libraries(myapp ${OPENGL_glu_LIBRARY}) -@endcode - -@note GLU has been deprecated and should not be used in new code, but some -legacy code requires it. See the [section on GLU](@ref moving_glu) in the -transition guide for suggested replacements. - @subsection build_link_cmake_package With CMake and installed GLFW binaries @@ -247,44 +230,30 @@ find_package(glfw3 3.4 REQUIRED) @endcode Once GLFW has been added to the project, link against it with the `glfw` target. -This adds all link-time dependencies of GLFW as it is currently configured, -the include directory for the GLFW header and, when applicable, the @ref -GLFW_DLL macro. +This adds the GLFW library and its link-time dependencies, the include directory +for the GLFW header and, when applicable, the @ref GLFW_DLL macro. @code{.cmake} target_link_libraries(myapp glfw) @endcode -Note that the dependencies do not include OpenGL or GLU, as GLFW loads any -OpenGL, OpenGL ES or Vulkan libraries it needs at runtime and does not use GLU. -If your application calls OpenGL directly, instead of using a modern -[extension loader library](@ref context_glext_auto) you can find it by requiring -the OpenGL package. +Note that the `glfw` target does not depend on OpenGL, as GLFW loads any OpenGL, +OpenGL ES or Vulkan libraries it needs at runtime. If your application calls +OpenGL directly, instead of using a modern +[extension loader library](@ref context_glext_auto), use the OpenGL CMake +package. @code{.cmake} find_package(OpenGL REQUIRED) @endcode -If OpenGL is found, the `OPENGL_FOUND` variable is true and the -`OPENGL_INCLUDE_DIR` and `OPENGL_gl_LIBRARY` cache variables can be used. +If OpenGL is found, the `OpenGL::GL` target is added to your project, containing +library and include directory paths. Link against this like any other library. @code{.cmake} -target_include_directories(myapp PUBLIC ${OPENGL_INCLUDE_DIR}) -target_link_libraries(myapp ${OPENGL_gl_LIBRARY}) +target_link_libraries(myapp OpenGL::GL) @endcode -The OpenGL CMake package also looks for GLU. If GLU is found, the -`OPENGL_GLU_FOUND` variable is true and the `OPENGL_INCLUDE_DIR` and -`OPENGL_glu_LIBRARY` cache variables can be used. - -@code{.cmake} -target_link_libraries(myapp ${OPENGL_glu_LIBRARY}) -@endcode - -@note GLU has been deprecated and should not be used in new code, but some -legacy code requires it. See the [section on GLU](@ref moving_glu) in the -transition guide for suggested replacements. - @subsection build_link_pkgconfig With makefiles and pkg-config on Unix @@ -299,42 +268,31 @@ A typical compile and link command-line when using the static version of the GLFW library may look like this: @code{.sh} -cc `pkg-config --cflags glfw3` -o myprog myprog.c `pkg-config --static --libs glfw3` +cc $(pkg-config --cflags glfw3) -o myprog myprog.c $(pkg-config --static --libs glfw3) @endcode If you are using the shared version of the GLFW library, omit the `--static` flag. @code{.sh} -cc `pkg-config --cflags glfw3` -o myprog myprog.c `pkg-config --libs glfw3` +cc $(pkg-config --cflags glfw3) -o myprog myprog.c $(pkg-config --libs glfw3) @endcode You can also use the `glfw3.pc` file without installing it first, by using the `PKG_CONFIG_PATH` environment variable. @code{.sh} -env PKG_CONFIG_PATH=path/to/glfw/src cc `pkg-config --cflags glfw3` -o myprog myprog.c `pkg-config --libs glfw3` +env PKG_CONFIG_PATH=path/to/glfw/src cc $(pkg-config --cflags glfw3) -o myprog myprog.c $(pkg-config --libs glfw3) @endcode -The dependencies do not include OpenGL or GLU, as GLFW loads any OpenGL, OpenGL -ES or Vulkan libraries it needs at runtime and does not use GLU. On macOS, GLU -is built into the OpenGL framework, so if you need GLU you don't need to do -anything extra. If you need GLU and are using Linux or BSD, you should add the -`glu` pkg-config package. +The dependencies do not include OpenGL, as GLFW loads any OpenGL, OpenGL ES or +Vulkan libraries it needs at runtime. If your application calls OpenGL +directly, instead of using a modern +[extension loader library](@ref context_glext_auto), you should add the `gl` +pkg-config package. @code{.sh} -cc `pkg-config --cflags glfw3 glu` -o myprog myprog.c `pkg-config --libs glfw3 glu` -@endcode - -@note GLU has been deprecated and should not be used in new code, but some -legacy code requires it. See the [section on GLU](@ref moving_glu) in the -transition guide for suggested replacements. - -If you are using the static version of the GLFW library, make sure you don't -link statically against GLU. - -@code{.sh} -cc `pkg-config --cflags glfw3 glu` -o myprog myprog.c `pkg-config --static --libs glfw3` `pkg-config --libs glu` +cc $(pkg-config --cflags glfw3 gl) -o myprog myprog.c $(pkg-config --libs glfw3 gl) @endcode @@ -344,8 +302,8 @@ If you are using the dynamic library version of GLFW, add it to the project dependencies. If you are using the static library version of GLFW, add it and the Cocoa, -OpenGL, IOKit and CoreVideo frameworks to the project as dependencies. They can -all be found in `/System/Library/Frameworks`. +OpenGL and IOKit frameworks to the project as dependencies. They can all be +found in `/System/Library/Frameworks`. @subsection build_link_osx With command-line on macOS @@ -359,7 +317,7 @@ the `-l` and `-framework` switches. If you are using the dynamic GLFW library, which is named `libglfw.3.dylib`, do: @code{.sh} -cc -o myprog myprog.c -lglfw -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo +cc -o myprog myprog.c -lglfw -framework Cocoa -framework OpenGL -framework IOKit @endcode If you are using the static library, named `libglfw3.a`, substitute `-lglfw3` @@ -368,9 +326,7 @@ for `-lglfw`. Note that you do not add the `.framework` extension to a framework when linking against it from the command-line. -The OpenGL framework contains both the OpenGL and GLU APIs, so there is nothing -special to do when using GLU. Also note that even though your machine may have -`libGL`-style OpenGL libraries, they are for use with the X Window System and -will _not_ work with the macOS native version of GLFW. +@note Your machine may have `libGL.*.dylib` style OpenGL library, but that is +for the X Window System and will not work with the macOS native version of GLFW. */ diff --git a/docs/compat.dox b/docs/compat.dox index c437e350..8aba6698 100644 --- a/docs/compat.dox +++ b/docs/compat.dox @@ -111,7 +111,7 @@ has been configured in the compositor. GLFW uses the [xdg-shell protocol](https://cgit.freedesktop.org/wayland/wayland-protocols/tree/stable/xdg-shell/xdg-shell.xml) to provide better window management. This protocol is part of -wayland-protocols 1.12, and mandatory at build time. +wayland-protocols 1.12, and is mandatory for GLFW to display a window. GLFW uses the [relative pointer protocol](https://cgit.freedesktop.org/wayland/wayland-protocols/tree/unstable/relative-pointer/relative-pointer-unstable-v1.xml) @@ -268,10 +268,10 @@ surfaces on Microsoft Windows. If any of these extensions are not available, @ref glfwGetRequiredInstanceExtensions will return an empty list and window surface creation will fail. -GLFW uses the `VK_KHR_surface` and `VK_MVK_macos_surface` extensions to create -surfaces on macOS. If any of these extensions are not available, @ref -glfwGetRequiredInstanceExtensions will return an empty list and window surface -creation will fail. +GLFW uses the `VK_KHR_surface` and either the `VK_MVK_macos_surface` or +`VK_EXT_metal_surface` extensions to create surfaces on macOS. If any of these +extensions are not available, @ref glfwGetRequiredInstanceExtensions will +return an empty list and window surface creation will fail. GLFW uses the `VK_KHR_surface` and either the `VK_KHR_xlib_surface` or `VK_KHR_xcb_surface` extensions to create surfaces on X11. If `VK_KHR_surface` diff --git a/docs/context.dox b/docs/context.dox index 69b8fa7f..dd952122 100644 --- a/docs/context.dox +++ b/docs/context.dox @@ -84,10 +84,6 @@ objects are recommended for rendering with such contexts. You should still [process events](@ref events) as long as you have at least one window, even if none of them are visible. -@macos The first time a window is created the menu bar is created. This is not -desirable for example when writing a command-line only application. Menu bar -creation can be disabled with the @ref GLFW_COCOA_MENUBAR init hint. - @subsection context_less Windows without contexts diff --git a/docs/intro.dox b/docs/intro.dox index a72b620e..36e33525 100644 --- a/docs/intro.dox +++ b/docs/intro.dox @@ -62,6 +62,11 @@ before the application exits. Modern systems are very good at freeing resources allocated by programs that exit, but GLFW sometimes has to change global system settings and these might not be restored without termination. +@macos When the library is initialized the main menu and dock icon are created. +These are not desirable for a command-line only program. The creation of the +main menu and dock icon can be disabled with the @ref GLFW_COCOA_MENUBAR init +hint. + @subsection init_hints Initialization hints @@ -97,9 +102,9 @@ the application to the `Contents/Resources` subdirectory of the application's bundle, if present. Set this with @ref glfwInitHint. @anchor GLFW_COCOA_MENUBAR_hint -__GLFW_COCOA_MENUBAR__ specifies whether to create a basic menu bar, either from -a nib or manually, when the first window is created, which is when AppKit is -initialized. Set this with @ref glfwInitHint. +__GLFW_COCOA_MENUBAR__ specifies whether to create the menu bar and dock icon +when GLFW is initialized. This applies whether the menu bar is created from +a nib or manually by GLFW. Set this with @ref glfwInitHint. @subsubsection init_hints_values Supported and default values diff --git a/docs/news.dox b/docs/news.dox index f18537af..4dd72795 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -52,6 +52,19 @@ add_subdirectory(path/to/glfw) @endcode +@subsubsection initmenu_34 macOS main menu now created at initialization + +GLFW now creates the main menu and completes the initialization of NSApplication +during initialization. Programs that do not want a main menu can disable it +with the [GLFW_COCOA_MENUBAR](@ref GLFW_COCOA_MENUBAR_hint) init hint. + + +@subsubsection corevideo_34 CoreVideo dependency has been removed + +GLFW no longer depends on the CoreVideo framework on macOS and it no longer +needs to be specified during compilation or linking. + + @subsection deprecations_34 Deprecations in version 3.4 @subsection removals_34 Removals in 3.4 diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 597c1e13..867f5bcf 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,7 +1,7 @@ link_libraries(glfw) -include_directories(${glfw_INCLUDE_DIRS} "${GLFW_SOURCE_DIR}/deps") +include_directories("${GLFW_SOURCE_DIR}/deps") if (MATH_LIBRARY) link_libraries("${MATH_LIBRARY}") @@ -11,27 +11,10 @@ if (MSVC) add_definitions(-D_CRT_SECURE_NO_WARNINGS) endif() -if (GLFW_USE_OSMESA) - add_definitions(-DUSE_NATIVE_OSMESA) -endif() - if (WIN32) set(ICON glfw.rc) elseif (APPLE) set(ICON glfw.icns) - set_source_files_properties(glfw.icns PROPERTIES - MACOSX_PACKAGE_LOCATION "Resources") -endif() - -if (${CMAKE_VERSION} VERSION_EQUAL "3.1.0" OR - ${CMAKE_VERSION} VERSION_GREATER "3.1.0") - set(CMAKE_C_STANDARD 99) -else() - # Remove this fallback when removing support for CMake version less than 3.1 - add_compile_options("$<$:-std=c99>" - "$<$:-std=c99>" - "$<$:-std=c99>") - endif() set(GLAD_GL "${GLFW_SOURCE_DIR}/deps/glad/gl.h" @@ -51,20 +34,26 @@ add_executable(splitview WIN32 MACOSX_BUNDLE splitview.c ${ICON} ${GLAD_GL}) add_executable(triangle-opengl WIN32 MACOSX_BUNDLE triangle-opengl.c ${ICON} ${GLAD_GL}) add_executable(wave WIN32 MACOSX_BUNDLE wave.c ${ICON} ${GLAD_GL}) -target_link_libraries(particles "${CMAKE_THREAD_LIBS_INIT}") +target_link_libraries(particles Threads::Threads) if (RT_LIBRARY) target_link_libraries(particles "${RT_LIBRARY}") endif() -set(WINDOWS_BINARIES boing gears heightmap particles sharing splitview triangle-opengl wave) +set(GUI_ONLY_BINARIES boing gears heightmap particles sharing splitview + triangle-opengl wave) set(CONSOLE_BINARIES offscreen) -set_target_properties(${WINDOWS_BINARIES} ${CONSOLE_BINARIES} PROPERTIES +set_target_properties(${GUI_ONLY_BINARIES} ${CONSOLE_BINARIES} PROPERTIES + C_STANDARD 99 FOLDER "GLFW3/Examples") +if (GLFW_USE_OSMESA) + target_compile_definitions(offscreen PRIVATE USE_NATIVE_OSMESA) +endif() + if (MSVC) # Tell MSVC to use main instead of WinMain for Windows subsystem executables - set_target_properties(${WINDOWS_BINARIES} PROPERTIES + set_target_properties(${GUI_ONLY_BINARIES} PROPERTIES LINK_FLAGS "/ENTRY:mainCRTStartup") endif() @@ -78,11 +67,12 @@ if (APPLE) set_target_properties(splitview PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "SplitView") set_target_properties(wave PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Wave") - set_target_properties(${WINDOWS_BINARIES} PROPERTIES - RESOURCE glfw.icns + set_source_files_properties(glfw.icns PROPERTIES + MACOSX_PACKAGE_LOCATION "Resources") + set_target_properties(${GUI_ONLY_BINARIES} PROPERTIES MACOSX_BUNDLE_SHORT_VERSION_STRING ${GLFW_VERSION} MACOSX_BUNDLE_LONG_VERSION_STRING ${GLFW_VERSION} MACOSX_BUNDLE_ICON_FILE glfw.icns - MACOSX_BUNDLE_INFO_PLIST "${GLFW_SOURCE_DIR}/CMake/MacOSXBundleInfo.plist.in") + MACOSX_BUNDLE_INFO_PLIST "${GLFW_SOURCE_DIR}/CMake/Info.plist.in") endif() diff --git a/examples/offscreen.c b/examples/offscreen.c index eca37c48..16b8f3c9 100644 --- a/examples/offscreen.c +++ b/examples/offscreen.c @@ -148,6 +148,7 @@ int main(void) glUseProgram(program); glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp); glDrawArrays(GL_TRIANGLES, 0, 3); + glFinish(); #if USE_NATIVE_OSMESA glfwGetOSMesaColorBuffer(window, &width, &height, NULL, (void**) &buffer); diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 8b5b87dd..e5b9b6c6 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -1831,6 +1831,18 @@ typedef struct GLFWgamepadstate * bundle, if present. This can be disabled with the @ref * GLFW_COCOA_CHDIR_RESOURCES init hint. * + * @remark @macos This function will create the main menu and dock icon for the + * application. If GLFW finds a `MainMenu.nib` it is loaded and assumed to + * contain a menu bar. Otherwise a minimal menu bar is created manually with + * common commands like Hide, Quit and About. The About entry opens a minimal + * about dialog with information from the application's bundle. The menu bar + * and dock icon can be disabled entirely with the @ref GLFW_COCOA_MENUBAR init + * hint. + * + * @remark @x11 This function will set the `LC_CTYPE` category of the + * application locale according to the current environment if that category is + * still "C". This is because the "C" locale breaks Unicode text input. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref intro_init @@ -2670,13 +2682,6 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value); * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) * in the Mac Developer Library. * - * @remark @macos The first time a window is created the menu bar is created. - * If GLFW finds a `MainMenu.nib` it is loaded and assumed to contain a menu - * bar. Otherwise a minimal menu bar is created manually with common commands - * like Hide, Quit and About. The About entry opens a minimal about dialog - * with information from the application's bundle. Menu bar creation can be - * disabled entirely with the @ref GLFW_COCOA_MENUBAR init hint. - * * @remark @macos On OS X 10.10 and later the window frame will not be rendered * at full resolution on Retina displays unless the * [GLFW_COCOA_RETINA_FRAMEBUFFER](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint) @@ -2685,7 +2690,7 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value); * [High Resolution Guidelines for OS X](https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html) * in the Mac Developer Library. The GLFW test and example programs use * a custom `Info.plist` template for this, which can be found as - * `CMake/MacOSXBundleInfo.plist.in` in the source tree. + * `CMake/Info.plist.in` in the source tree. * * @remark @macos When activating frame autosaving with * [GLFW_COCOA_FRAME_NAME](@ref GLFW_COCOA_FRAME_NAME_hint), the specified @@ -5776,8 +5781,9 @@ GLFWAPI int glfwVulkanSupported(void); * returned array, as it is an error to specify an extension more than once in * the `VkInstanceCreateInfo` struct. * - * @remark @macos This function currently only supports the - * `VK_MVK_macos_surface` extension from MoltenVK. + * @remark @macos This function currently supports either the + * `VK_MVK_macos_surface` extension from MoltenVK or `VK_EXT_metal_surface` + * extension. * * @pointer_lifetime The returned array is allocated and freed by GLFW. You * should not free it yourself. It is guaranteed to be valid only until the diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6cbeed6d..3fb2a54e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,118 +1,96 @@ -set(common_HEADERS internal.h mappings.h - "${GLFW_BINARY_DIR}/src/glfw_config.h" - "${GLFW_SOURCE_DIR}/include/GLFW/glfw3.h" - "${GLFW_SOURCE_DIR}/include/GLFW/glfw3native.h") -set(common_SOURCES context.c init.c input.c monitor.c vulkan.c window.c) +add_library(glfw "${GLFW_SOURCE_DIR}/include/GLFW/glfw3.h" + "${GLFW_SOURCE_DIR}/include/GLFW/glfw3native.h" + internal.h mappings.h context.c init.c input.c monitor.c + vulkan.c window.c) if (_GLFW_COCOA) - set(glfw_HEADERS ${common_HEADERS} cocoa_platform.h cocoa_joystick.h - posix_thread.h nsgl_context.h egl_context.h osmesa_context.h) - set(glfw_SOURCES ${common_SOURCES} cocoa_init.m cocoa_joystick.m - cocoa_monitor.m cocoa_window.m cocoa_time.c posix_thread.c - nsgl_context.m egl_context.c osmesa_context.c) + target_sources(glfw PRIVATE cocoa_platform.h cocoa_joystick.h posix_thread.h + nsgl_context.h egl_context.h osmesa_context.h + cocoa_init.m cocoa_joystick.m cocoa_monitor.m + cocoa_window.m cocoa_time.c posix_thread.c + nsgl_context.m egl_context.c osmesa_context.c) elseif (_GLFW_WIN32) - set(glfw_HEADERS ${common_HEADERS} win32_platform.h win32_joystick.h - wgl_context.h egl_context.h osmesa_context.h) - set(glfw_SOURCES ${common_SOURCES} win32_init.c win32_joystick.c - win32_monitor.c win32_time.c win32_thread.c win32_window.c - wgl_context.c egl_context.c osmesa_context.c) + target_sources(glfw PRIVATE win32_platform.h win32_joystick.h wgl_context.h + egl_context.h osmesa_context.h win32_init.c + win32_joystick.c win32_monitor.c win32_time.c + win32_thread.c win32_window.c wgl_context.c + egl_context.c osmesa_context.c) elseif (_GLFW_X11) - set(glfw_HEADERS ${common_HEADERS} x11_platform.h xkb_unicode.h posix_time.h - posix_thread.h glx_context.h egl_context.h osmesa_context.h) - set(glfw_SOURCES ${common_SOURCES} x11_init.c x11_monitor.c x11_window.c - xkb_unicode.c posix_time.c posix_thread.c glx_context.c - egl_context.c osmesa_context.c) + target_sources(glfw PRIVATE x11_platform.h xkb_unicode.h posix_time.h + posix_thread.h glx_context.h egl_context.h + osmesa_context.h x11_init.c x11_monitor.c + x11_window.c xkb_unicode.c posix_time.c + posix_thread.c glx_context.c egl_context.c + osmesa_context.c) elseif (_GLFW_WAYLAND) - set(glfw_HEADERS ${common_HEADERS} wl_platform.h - posix_time.h posix_thread.h xkb_unicode.h egl_context.h - osmesa_context.h) - set(glfw_SOURCES ${common_SOURCES} wl_init.c wl_monitor.c wl_window.c - posix_time.c posix_thread.c xkb_unicode.c - egl_context.c osmesa_context.c) - - ecm_add_wayland_client_protocol(glfw_SOURCES - PROTOCOL - "${WAYLAND_PROTOCOLS_PKGDATADIR}/stable/xdg-shell/xdg-shell.xml" - BASENAME xdg-shell) - ecm_add_wayland_client_protocol(glfw_SOURCES - PROTOCOL - "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" - BASENAME xdg-decoration) - ecm_add_wayland_client_protocol(glfw_SOURCES - PROTOCOL - "${WAYLAND_PROTOCOLS_PKGDATADIR}/stable/viewporter/viewporter.xml" - BASENAME viewporter) - ecm_add_wayland_client_protocol(glfw_SOURCES - PROTOCOL - "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/relative-pointer/relative-pointer-unstable-v1.xml" - BASENAME relative-pointer-unstable-v1) - ecm_add_wayland_client_protocol(glfw_SOURCES - PROTOCOL - "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml" - BASENAME pointer-constraints-unstable-v1) - ecm_add_wayland_client_protocol(glfw_SOURCES - PROTOCOL - "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml" - BASENAME idle-inhibit-unstable-v1) + target_sources(glfw PRIVATE wl_platform.h posix_time.h posix_thread.h + xkb_unicode.h egl_context.h osmesa_context.h + wl_init.c wl_monitor.c wl_window.c posix_time.c + posix_thread.c xkb_unicode.c egl_context.c + osmesa_context.c) elseif (_GLFW_OSMESA) - set(glfw_HEADERS ${common_HEADERS} null_platform.h null_joystick.h - posix_time.h posix_thread.h osmesa_context.h) - set(glfw_SOURCES ${common_SOURCES} null_init.c null_monitor.c null_window.c - null_joystick.c posix_time.c posix_thread.c osmesa_context.c) + target_sources(glfw PRIVATE null_platform.h null_joystick.h posix_time.h + posix_thread.h osmesa_context.h null_init.c + null_monitor.c null_window.c null_joystick.c + posix_time.c posix_thread.c osmesa_context.c) endif() if (_GLFW_X11 OR _GLFW_WAYLAND) if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") - set(glfw_HEADERS ${glfw_HEADERS} linux_joystick.h) - set(glfw_SOURCES ${glfw_SOURCES} linux_joystick.c) + target_sources(glfw PRIVATE linux_joystick.h linux_joystick.c) else() - set(glfw_HEADERS ${glfw_HEADERS} null_joystick.h) - set(glfw_SOURCES ${glfw_SOURCES} null_joystick.c) + target_sources(glfw PRIVATE null_joystick.h null_joystick.c) endif() endif() -if (APPLE) - # For some reason, CMake doesn't know about .m - set_source_files_properties(${glfw_SOURCES} PROPERTIES LANGUAGE C) +if (_GLFW_WAYLAND) + ecm_add_wayland_client_protocol(GLFW_WAYLAND_PROTOCOL_SOURCES + PROTOCOL + "${WAYLAND_PROTOCOLS_PKGDATADIR}/stable/xdg-shell/xdg-shell.xml" + BASENAME xdg-shell) + ecm_add_wayland_client_protocol(GLFW_WAYLAND_PROTOCOL_SOURCES + PROTOCOL + "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" + BASENAME xdg-decoration) + ecm_add_wayland_client_protocol(GLFW_WAYLAND_PROTOCOL_SOURCES + PROTOCOL + "${WAYLAND_PROTOCOLS_PKGDATADIR}/stable/viewporter/viewporter.xml" + BASENAME viewporter) + ecm_add_wayland_client_protocol(GLFW_WAYLAND_PROTOCOL_SOURCES + PROTOCOL + "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/relative-pointer/relative-pointer-unstable-v1.xml" + BASENAME relative-pointer-unstable-v1) + ecm_add_wayland_client_protocol(GLFW_WAYLAND_PROTOCOL_SOURCES + PROTOCOL + "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml" + BASENAME pointer-constraints-unstable-v1) + ecm_add_wayland_client_protocol(GLFW_WAYLAND_PROTOCOL_SOURCES + PROTOCOL + "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml" + BASENAME idle-inhibit-unstable-v1) + target_sources(glfw PRIVATE ${GLFW_WAYLAND_PROTOCOL_SOURCES}) endif() -# Make GCC and Clang warn about declarations that VS 2010 and 2012 won't accept -# for all source files that VS will build -if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR - "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR - "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang") - - if (WIN32) - set(windows_SOURCES ${glfw_SOURCES}) - else() - set(windows_SOURCES ${common_SOURCES}) - endif() - set_source_files_properties(${windows_SOURCES} PROPERTIES - COMPILE_FLAGS -Wdeclaration-after-statement) +if (WIN32 AND BUILD_SHARED_LIBS) + configure_file(glfw.rc.in glfw.rc @ONLY) + target_sources(glfw PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/glfw.rc") endif() -add_library(glfw ${glfw_SOURCES} ${glfw_HEADERS}) +configure_file(glfw_config.h.in glfw_config.h @ONLY) +target_compile_definitions(glfw PRIVATE _GLFW_USE_CONFIG_H) +target_sources(glfw PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/glfw_config.h") + set_target_properties(glfw PROPERTIES OUTPUT_NAME ${GLFW_LIB_NAME} VERSION ${GLFW_VERSION_MAJOR}.${GLFW_VERSION_MINOR} SOVERSION ${GLFW_VERSION_MAJOR} POSITION_INDEPENDENT_CODE ON + C_STANDARD 99 + C_EXTENSIONS OFF + DEFINE_SYMBOL _GLFW_BUILD_DLL FOLDER "GLFW3") -if (${CMAKE_VERSION} VERSION_EQUAL "3.1.0" OR - ${CMAKE_VERSION} VERSION_GREATER "3.1.0") - - set_target_properties(glfw PROPERTIES C_STANDARD 99) -else() - # Remove this fallback when removing support for CMake version less than 3.1 - target_compile_options(glfw PRIVATE - "$<$:-std=c99>" - "$<$:-std=c99>" - "$<$:-std=c99>") -endif() - -target_compile_definitions(glfw PRIVATE _GLFW_USE_CONFIG_H) target_include_directories(glfw PUBLIC "$" "$") @@ -120,26 +98,50 @@ target_include_directories(glfw PRIVATE "${GLFW_SOURCE_DIR}/src" "${GLFW_BINARY_DIR}/src" ${glfw_INCLUDE_DIRS}) +target_link_libraries(glfw PRIVATE Threads::Threads ${glfw_LIBRARIES}) + +if (APPLE) + # For some reason CMake didn't know about .m until version 3.16 + set_source_files_properties(cocoa_init.m cocoa_joystick.m cocoa_monitor.m + cocoa_window.m nsgl_context.m PROPERTIES + LANGUAGE C) +endif() + +if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR + "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR + "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang") + + # Make GCC and Clang warn about declarations that VS 2010 and 2012 won't + # accept for all source files that VS will build + set_source_files_properties(context.c init.c input.c monitor.c vulkan.c + window.c win32_init.c win32_joystick.c + win32_monitor.c win32_time.c win32_thread.c + win32_window.c wgl_context.c egl_context.c + osmesa_context.c PROPERTIES + COMPILE_FLAGS -Wdeclaration-after-statement) + + # Enable a reasonable set of warnings (no, -Wextra is not reasonable) + target_compile_options(glfw PRIVATE "-Wall") +endif() + +if (WIN32) + target_compile_definitions(glfw PRIVATE _UNICODE) +endif() # HACK: When building on MinGW, WINVER and UNICODE need to be defined before # the inclusion of stddef.h (by glfw3.h), which is itself included before # win32_platform.h. We define them here until a saner solution can be found # NOTE: MinGW-w64 and Visual C++ do /not/ need this hack. -target_compile_definitions(glfw PRIVATE - "$<$:UNICODE;WINVER=0x0501>") - -# Enable a reasonable set of warnings (no, -Wextra is not reasonable) -target_compile_options(glfw PRIVATE - "$<$:-Wall>" - "$<$:-Wall>" - "$<$:-Wall>") +if (MINGW) + target_compile_definitions(glfw PRIVATE UNICODE WINVER=0x0501) +endif() if (BUILD_SHARED_LIBS) if (WIN32) if (MINGW) # Remove the dependency on the shared version of libgcc # NOTE: MinGW-w64 has the correct default but MinGW needs this - target_link_options(glfw PRIVATE "-static-libgcc") + target_link_libraries(glfw PRIVATE "-static-libgcc") # Remove the lib prefix on the DLL (but not the import library) set_target_properties(glfw PROPERTIES PREFIX "") @@ -151,11 +153,8 @@ if (BUILD_SHARED_LIBS) set_target_properties(glfw PROPERTIES IMPORT_SUFFIX "dll.lib") endif() - target_compile_definitions(glfw INTERFACE GLFW_DLL) + target_compile_definitions(glfw INTERFACE GLFW_DLL) elseif (APPLE) - # Add -fno-common to work around a bug in Apple's GCC - target_compile_options(glfw PRIVATE "-fno-common") - set_target_properties(glfw PROPERTIES INSTALL_NAME_DIR "${CMAKE_INSTALL_LIBDIR}") endif() @@ -164,10 +163,6 @@ if (BUILD_SHARED_LIBS) # Hide symbols not explicitly tagged for export from the shared library target_compile_options(glfw PRIVATE "-fvisibility=hidden") endif() - - target_link_libraries(glfw PRIVATE ${glfw_LIBRARIES}) -else() - target_link_libraries(glfw INTERFACE ${glfw_LIBRARIES}) endif() if (MSVC) diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 01c1b319..434e5beb 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -431,9 +431,8 @@ static GLFWbool initializeTIS(void) // In case we are unbundled, make us a proper UI application [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - // Menu bar setup must go between sharedApplication above and - // finishLaunching below, in order to properly emulate the behavior - // of NSApplicationMain + // Menu bar setup must go between sharedApplication and finishLaunching + // in order to properly emulate the behavior of NSApplicationMain if ([[NSBundle mainBundle] pathForResource:@"MainMenu" ofType:@"nib"]) { @@ -448,9 +447,8 @@ static GLFWbool initializeTIS(void) - (void)applicationDidFinishLaunching:(NSNotification *)notification { - [NSApp stop:nil]; - _glfwPlatformPostEmptyEvent(); + [NSApp stop:nil]; } - (void)applicationDidHide:(NSNotification *)notification @@ -464,6 +462,32 @@ static GLFWbool initializeTIS(void) @end // GLFWApplicationDelegate +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +void* _glfwLoadLocalVulkanLoaderNS(void) +{ + CFBundleRef bundle = CFBundleGetMainBundle(); + if (!bundle) + return NULL; + + CFURLRef url = + CFBundleCopyAuxiliaryExecutableURL(bundle, CFSTR("libvulkan.1.dylib")); + if (!url) + return NULL; + + char path[PATH_MAX]; + void* handle = NULL; + + if (CFURLGetFileSystemRepresentation(url, true, (UInt8*) path, sizeof(path) - 1)) + handle = _glfw_dlopen(path); + + CFRelease(url); + return handle; +} + + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// @@ -478,9 +502,6 @@ int _glfwPlatformInit(void) toTarget:_glfw.ns.helper withObject:nil]; - if (NSApp) - _glfw.ns.finishedLaunching = GLFW_TRUE; - [NSApplication sharedApplication]; _glfw.ns.delegate = [[GLFWApplicationDelegate alloc] init]; @@ -533,6 +554,10 @@ int _glfwPlatformInit(void) _glfwInitJoysticksNS(); _glfwPollMonitorsNS(); + + if (![[NSRunningApplication currentApplication] isFinishedLaunching]) + [NSApp run]; + return GLFW_TRUE; } // autoreleasepool diff --git a/src/cocoa_joystick.m b/src/cocoa_joystick.m index b98f9f67..88636a87 100644 --- a/src/cocoa_joystick.m +++ b/src/cocoa_joystick.m @@ -331,7 +331,7 @@ void _glfwInitJoysticksNS(void) return; } - for (int i = 0; i < sizeof(usages) / sizeof(long); i++) + for (size_t i = 0; i < sizeof(usages) / sizeof(long); i++) { const long page = kHIDPage_GenericDesktop; diff --git a/src/cocoa_monitor.m b/src/cocoa_monitor.m index 9ef2cdc9..42f2dce2 100644 --- a/src/cocoa_monitor.m +++ b/src/cocoa_monitor.m @@ -144,7 +144,7 @@ static GLFWbool modeIsGood(CGDisplayModeRef mode) // Convert Core Graphics display mode to GLFW video mode // static GLFWvidmode vidmodeFromCGDisplayMode(CGDisplayModeRef mode, - CVDisplayLinkRef link) + double fallbackRefreshRate) { GLFWvidmode result; result.width = (int) CGDisplayModeGetWidth(mode); @@ -152,11 +152,7 @@ static GLFWvidmode vidmodeFromCGDisplayMode(CGDisplayModeRef mode, result.refreshRate = (int) round(CGDisplayModeGetRefreshRate(mode)); if (result.refreshRate == 0) - { - const CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link); - if (!(time.flags & kCVTimeIsIndefinite)) - result.refreshRate = (int) (time.timeScale / (double) time.timeValue); - } + result.refreshRate = (int) round(fallbackRefreshRate); #if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100 CFStringRef format = CGDisplayModeCopyPixelEncoding(mode); @@ -238,6 +234,68 @@ static GLFWbool refreshMonitorScreen(_GLFWmonitor* monitor) return GLFW_FALSE; } +// Returns the display refresh rate queried from the I/O registry +// +static double getFallbackRefreshRate(CGDirectDisplayID displayID) +{ + double refreshRate = 60.0; + + io_iterator_t it; + io_service_t service; + + if (IOServiceGetMatchingServices(kIOMasterPortDefault, + IOServiceMatching("IOFramebuffer"), + &it) != 0) + { + return refreshRate; + } + + while ((service = IOIteratorNext(it)) != 0) + { + const CFNumberRef indexRef = + IORegistryEntryCreateCFProperty(service, + CFSTR("IOFramebufferOpenGLIndex"), + kCFAllocatorDefault, + kNilOptions); + if (!indexRef) + continue; + + uint32_t index = 0; + CFNumberGetValue(indexRef, kCFNumberIntType, &index); + CFRelease(indexRef); + + if (CGOpenGLDisplayMaskToDisplayID(1 << index) != displayID) + continue; + + const CFNumberRef clockRef = + IORegistryEntryCreateCFProperty(service, + CFSTR("IOFBCurrentPixelClock"), + kCFAllocatorDefault, + kNilOptions); + const CFNumberRef countRef = + IORegistryEntryCreateCFProperty(service, + CFSTR("IOFBCurrentPixelCount"), + kCFAllocatorDefault, + kNilOptions); + if (!clockRef || !countRef) + break; + + uint32_t clock = 0, count = 0; + CFNumberGetValue(clockRef, kCFNumberIntType, &clock); + CFNumberGetValue(countRef, kCFNumberIntType, &count); + CFRelease(clockRef); + CFRelease(countRef); + + if (clock > 0 && count > 0) + refreshRate = clock / (double) count; + + break; + } + + IOObjectRelease(it); + return refreshRate; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// @@ -294,6 +352,11 @@ void _glfwPollMonitorsNS(void) free(name); + CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displays[i]); + if (CGDisplayModeGetRefreshRate(mode) == 0.0) + monitor->ns.fallbackRefreshRate = getFallbackRefreshRate(displays[i]); + CGDisplayModeRelease(mode); + _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST); } @@ -318,9 +381,6 @@ void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired) if (_glfwCompareVideoModes(¤t, best) == 0) return; - CVDisplayLinkRef link; - CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link); - CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL); const CFIndex count = CFArrayGetCount(modes); CGDisplayModeRef native = NULL; @@ -331,7 +391,8 @@ void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired) if (!modeIsGood(dm)) continue; - const GLFWvidmode mode = vidmodeFromCGDisplayMode(dm, link); + const GLFWvidmode mode = + vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate); if (_glfwCompareVideoModes(best, &mode) == 0) { native = dm; @@ -350,7 +411,6 @@ void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired) } CFRelease(modes); - CVDisplayLinkRelease(link); } // Restore the previously saved (original) video mode @@ -440,9 +500,6 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) *count = 0; - CVDisplayLinkRef link; - CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link); - CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL); const CFIndex found = CFArrayGetCount(modes); GLFWvidmode* result = calloc(found, sizeof(GLFWvidmode)); @@ -453,7 +510,8 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) if (!modeIsGood(dm)) continue; - const GLFWvidmode mode = vidmodeFromCGDisplayMode(dm, link); + const GLFWvidmode mode = + vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate); CFIndex j; for (j = 0; j < *count; j++) @@ -471,7 +529,6 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) } CFRelease(modes); - CVDisplayLinkRelease(link); return result; } // autoreleasepool @@ -481,15 +538,10 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode *mode) { @autoreleasepool { - CVDisplayLinkRef link; - CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link); - CGDisplayModeRef native = CGDisplayCopyDisplayMode(monitor->ns.displayID); - *mode = vidmodeFromCGDisplayMode(native, link); + *mode = vidmodeFromCGDisplayMode(native, monitor->ns.fallbackRefreshRate); CGDisplayModeRelease(native); - CVDisplayLinkRelease(link); - } // autoreleasepool } diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index 21d83bcc..90714341 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -28,8 +28,6 @@ #include #include -#include -#include // NOTE: All of NSGL was deprecated in the 10.14 SDK // This disables the pointless warnings for every symbol we use @@ -63,6 +61,7 @@ typedef void* id; #endif typedef VkFlags VkMacOSSurfaceCreateFlagsMVK; +typedef VkFlags VkMetalSurfaceCreateFlagsEXT; typedef struct VkMacOSSurfaceCreateInfoMVK { @@ -72,7 +71,16 @@ typedef struct VkMacOSSurfaceCreateInfoMVK const void* pView; } VkMacOSSurfaceCreateInfoMVK; +typedef struct VkMetalSurfaceCreateInfoEXT +{ + VkStructureType sType; + const void* pNext; + VkMetalSurfaceCreateFlagsEXT flags; + const void* pLayer; +} VkMetalSurfaceCreateInfoEXT; + typedef VkResult (APIENTRY *PFN_vkCreateMacOSSurfaceMVK)(VkInstance,const VkMacOSSurfaceCreateInfoMVK*,const VkAllocationCallbacks*,VkSurfaceKHR*); +typedef VkResult (APIENTRY *PFN_vkCreateMetalSurfaceEXT)(VkInstance,const VkMetalSurfaceCreateInfoEXT*,const VkAllocationCallbacks*,VkSurfaceKHR*); #include "posix_thread.h" #include "cocoa_joystick.h" @@ -133,7 +141,6 @@ typedef struct _GLFWlibraryNS { CGEventSourceRef eventSource; id delegate; - GLFWbool finishedLaunching; GLFWbool cursorHidden; TISInputSourceRef inputSource; IOHIDManagerRef hidManager; @@ -170,6 +177,7 @@ typedef struct _GLFWmonitorNS CGDisplayModeRef previousMode; uint32_t unitNumber; id screen; + double fallbackRefreshRate; } _GLFWmonitorNS; @@ -198,3 +206,5 @@ void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor); float _glfwTransformYNS(float y); +void* _glfwLoadLocalVulkanLoaderNS(void); + diff --git a/src/cocoa_window.m b/src/cocoa_window.m index bfec8b7b..c50bf21a 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -322,12 +322,6 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; _glfwInputWindowFocus(window, GLFW_FALSE); } -- (void)windowDidChangeScreen:(NSNotification *)notification -{ - if (window->context.source == GLFW_NATIVE_CONTEXT_API) - _glfwUpdateDisplayLinkDisplayNSGL(window); -} - @end @@ -890,12 +884,6 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, { @autoreleasepool { - if (!_glfw.ns.finishedLaunching) - { - [NSApp run]; - _glfw.ns.finishedLaunching = GLFW_TRUE; - } - if (!createNativeWindow(window, wndconfig, fbconfig)) return GLFW_FALSE; @@ -967,13 +955,14 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) } // autoreleasepool } -void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char *title) +void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) { @autoreleasepool { - [window->ns.object setTitle:@(title)]; + NSString* string = @(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:@(title)]; + [window->ns.object setMiniwindowTitle:string]; } // autoreleasepool } @@ -1512,6 +1501,13 @@ const char* _glfwPlatformGetScancodeName(int scancode) { @autoreleasepool { + if (scancode < 0 || scancode > 0xff || + _glfw.ns.keycodes[scancode] == GLFW_KEY_UNKNOWN) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode"); + return NULL; + } + const int key = _glfw.ns.keycodes[scancode]; UInt32 deadKeyState = 0; @@ -1607,13 +1603,13 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) // HACK: Try to use a private message if (shape == GLFW_RESIZE_EW_CURSOR) - cursorSelector = @selector(_windowResizeEastWestCursor); + cursorSelector = NSSelectorFromString(@"_windowResizeEastWestCursor"); else if (shape == GLFW_RESIZE_NS_CURSOR) - cursorSelector = @selector(_windowResizeNorthSouthCursor); + cursorSelector = NSSelectorFromString(@"_windowResizeNorthSouthCursor"); else if (shape == GLFW_RESIZE_NWSE_CURSOR) - cursorSelector = @selector(_windowResizeNorthWestSouthEastCursor); + cursorSelector = NSSelectorFromString(@"_windowResizeNorthWestSouthEastCursor"); else if (shape == GLFW_RESIZE_NESW_CURSOR) - cursorSelector = @selector(_windowResizeNorthEastSouthWestCursor); + cursorSelector = NSSelectorFromString(@"_windowResizeNorthEastSouthWestCursor"); if (cursorSelector && [NSCursor respondsToSelector:cursorSelector]) { @@ -1711,11 +1707,16 @@ const char* _glfwPlatformGetClipboardString(void) void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) { - if (!_glfw.vk.KHR_surface || !_glfw.vk.MVK_macos_surface) - return; - - extensions[0] = "VK_KHR_surface"; - extensions[1] = "VK_MVK_macos_surface"; + if (_glfw.vk.KHR_surface && _glfw.vk.EXT_metal_surface) + { + extensions[0] = "VK_KHR_surface"; + extensions[1] = "VK_EXT_metal_surface"; + } + else if (_glfw.vk.KHR_surface && _glfw.vk.MVK_macos_surface) + { + extensions[0] = "VK_KHR_surface"; + extensions[1] = "VK_MVK_macos_surface"; + } } int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, @@ -1733,19 +1734,6 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, @autoreleasepool { #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101100 - VkResult err; - VkMacOSSurfaceCreateInfoMVK sci; - PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK; - - vkCreateMacOSSurfaceMVK = (PFN_vkCreateMacOSSurfaceMVK) - vkGetInstanceProcAddr(instance, "vkCreateMacOSSurfaceMVK"); - if (!vkCreateMacOSSurfaceMVK) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "Cocoa: Vulkan instance missing VK_MVK_macos_surface extension"); - return VK_ERROR_EXTENSION_NOT_PRESENT; - } - // HACK: Dynamically load Core Animation to avoid adding an extra // dependency for the majority who don't use MoltenVK NSBundle* bundle = [NSBundle bundleWithPath:@"/System/Library/Frameworks/QuartzCore.framework"]; @@ -1771,11 +1759,49 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, [window->ns.view setLayer:window->ns.layer]; [window->ns.view setWantsLayer:YES]; - memset(&sci, 0, sizeof(sci)); - sci.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; - sci.pView = window->ns.view; + VkResult err; + + if (_glfw.vk.EXT_metal_surface) + { + VkMetalSurfaceCreateInfoEXT sci; + + PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT; + vkCreateMetalSurfaceEXT = (PFN_vkCreateMetalSurfaceEXT) + vkGetInstanceProcAddr(instance, "vkCreateMetalSurfaceEXT"); + if (!vkCreateMetalSurfaceEXT) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Cocoa: Vulkan instance missing VK_EXT_metal_surface extension"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + memset(&sci, 0, sizeof(sci)); + sci.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT; + sci.pLayer = window->ns.layer; + + err = vkCreateMetalSurfaceEXT(instance, &sci, allocator, surface); + } + else + { + VkMacOSSurfaceCreateInfoMVK sci; + + PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK; + vkCreateMacOSSurfaceMVK = (PFN_vkCreateMacOSSurfaceMVK) + vkGetInstanceProcAddr(instance, "vkCreateMacOSSurfaceMVK"); + if (!vkCreateMacOSSurfaceMVK) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Cocoa: Vulkan instance missing VK_MVK_macos_surface extension"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + memset(&sci, 0, sizeof(sci)); + sci.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; + sci.pView = window->ns.view; + + err = vkCreateMacOSSurfaceMVK(instance, &sci, allocator, surface); + } - err = vkCreateMacOSSurfaceMVK(instance, &sci, allocator, surface); if (err) { _glfwInputError(GLFW_PLATFORM_ERROR, diff --git a/src/egl_context.c b/src/egl_context.c index 6a33396f..706a7922 100644 --- a/src/egl_context.c +++ b/src/egl_context.c @@ -123,23 +123,24 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, continue; #if defined(_GLFW_X11) - XVisualInfo vi = {0}; - - // Only consider EGLConfigs with associated Visuals - vi.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID); - if (!vi.visualid) - continue; - - if (desired->transparent) { - int count; - XVisualInfo* vis = XGetVisualInfo(_glfw.x11.display, - VisualIDMask, &vi, - &count); - if (vis) + XVisualInfo vi = {0}; + + // Only consider EGLConfigs with associated Visuals + vi.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID); + if (!vi.visualid) + continue; + + if (desired->transparent) { - u->transparent = _glfwIsVisualTransparentX11(vis[0].visual); - XFree(vis); + int count; + XVisualInfo* vis = + XGetVisualInfo(_glfw.x11.display, VisualIDMask, &vi, &count); + if (vis) + { + u->transparent = _glfwIsVisualTransparentX11(vis[0].visual); + XFree(vis); + } } } #endif // _GLFW_X11 diff --git a/src/glfw.rc.in b/src/glfw.rc.in new file mode 100644 index 00000000..ac3460a7 --- /dev/null +++ b/src/glfw.rc.in @@ -0,0 +1,30 @@ + +#include + +VS_VERSION_INFO VERSIONINFO +FILEVERSION @GLFW_VERSION_MAJOR@,@GLFW_VERSION_MINOR@,@GLFW_VERSION_PATCH@,0 +PRODUCTVERSION @GLFW_VERSION_MAJOR@,@GLFW_VERSION_MINOR@,@GLFW_VERSION_PATCH@,0 +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +FILEFLAGS 0 +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE 0 +{ + BLOCK "StringFileInfo" + { + BLOCK "040904B0" + { + VALUE "CompanyName", "GLFW" + VALUE "FileDescription", "GLFW @GLFW_VERSION@ DLL" + VALUE "FileVersion", "@GLFW_VERSION@" + VALUE "OriginalFilename", "glfw3.dll" + VALUE "ProductName", "GLFW" + VALUE "ProductVersion", "@GLFW_VERSION@" + } + } + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x409, 1200 + } +} + diff --git a/src/glfw3Config.cmake.in b/src/glfw3Config.cmake.in deleted file mode 100644 index 1fa200e2..00000000 --- a/src/glfw3Config.cmake.in +++ /dev/null @@ -1 +0,0 @@ -include("${CMAKE_CURRENT_LIST_DIR}/glfw3Targets.cmake") diff --git a/src/glfw_config.h.in b/src/glfw_config.h.in index 018952d2..f4876da2 100644 --- a/src/glfw_config.h.in +++ b/src/glfw_config.h.in @@ -45,8 +45,6 @@ // Define this to 1 if building GLFW for OSMesa #cmakedefine _GLFW_OSMESA -// Define this to 1 if building as a shared library / dynamic library / DLL -#cmakedefine _GLFW_BUILD_DLL // Define this to 1 to use Vulkan loader linked statically into application #cmakedefine _GLFW_VULKAN_STATIC diff --git a/src/internal.h b/src/internal.h index 4c75c9b1..6d7587c8 100644 --- a/src/internal.h +++ b/src/internal.h @@ -128,6 +128,7 @@ typedef enum VkStructureType VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000, VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000, VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000123000, + VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT = 1000217000, VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF } VkStructureType; @@ -559,6 +560,7 @@ struct _GLFWlibrary GLFWbool KHR_win32_surface; #elif defined(_GLFW_COCOA) GLFWbool MVK_macos_surface; + GLFWbool EXT_metal_surface; #elif defined(_GLFW_X11) GLFWbool KHR_xlib_surface; GLFWbool KHR_xcb_surface; diff --git a/src/nsgl_context.h b/src/nsgl_context.h index edd958e1..9c31436c 100644 --- a/src/nsgl_context.h +++ b/src/nsgl_context.h @@ -44,10 +44,6 @@ typedef struct _GLFWcontextNSGL { id pixelFormat; id object; - CVDisplayLinkRef displayLink; - atomic_int swapInterval; - int swapIntervalsPassed; - id swapIntervalCond; } _GLFWcontextNSGL; @@ -67,5 +63,4 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); void _glfwDestroyContextNSGL(_GLFWwindow* window); -void _glfwUpdateDisplayLinkDisplayNSGL(_GLFWwindow* window); diff --git a/src/nsgl_context.m b/src/nsgl_context.m index e455d412..e011fa5e 100644 --- a/src/nsgl_context.m +++ b/src/nsgl_context.m @@ -28,29 +28,8 @@ #include "internal.h" -// Display link callback for manual swap interval implementation -// This is based on a similar workaround added to SDL2 -// -static CVReturn displayLinkCallback(CVDisplayLinkRef displayLink, - const CVTimeStamp* now, - const CVTimeStamp* outputTime, - CVOptionFlags flagsIn, - CVOptionFlags* flagsOut, - void* userInfo) -{ - _GLFWwindow* window = (_GLFWwindow *) userInfo; - - const int interval = atomic_load(&window->context.nsgl.swapInterval); - if (interval > 0) - { - [window->context.nsgl.swapIntervalCond lock]; - window->context.nsgl.swapIntervalsPassed++; - [window->context.nsgl.swapIntervalCond signal]; - [window->context.nsgl.swapIntervalCond unlock]; - } - - return kCVReturnSuccess; -} +#include +#include static void makeContextCurrentNSGL(_GLFWwindow* window) { @@ -70,19 +49,28 @@ static void swapBuffersNSGL(_GLFWwindow* window) { @autoreleasepool { - const int interval = atomic_load(&window->context.nsgl.swapInterval); - if (interval > 0) + // HACK: Simulate vsync with usleep as NSGL swap interval does not apply to + // windows with a non-visible occlusion state + if (!([window->ns.object occlusionState] & NSWindowOcclusionStateVisible)) { - [window->context.nsgl.swapIntervalCond lock]; - do + int interval = 0; + [window->context.nsgl.object getValues:&interval + forParameter:NSOpenGLContextParameterSwapInterval]; + + if (interval > 0) { - [window->context.nsgl.swapIntervalCond wait]; - } while (window->context.nsgl.swapIntervalsPassed % interval != 0); - window->context.nsgl.swapIntervalsPassed = 0; - [window->context.nsgl.swapIntervalCond unlock]; + const double framerate = 60.0; + const uint64_t frequency = _glfwPlatformGetTimerFrequency(); + const uint64_t value = _glfwPlatformGetTimerValue(); + + const double elapsed = value / (double) frequency; + const double period = 1.0 / framerate; + const double delay = period - fmod(elapsed, period); + + usleep(floorl(delay * 1e6)); + } } - // ARP appears to be unnecessary, but this is future-proof [window->context.nsgl.object flushBuffer]; } // autoreleasepool @@ -91,11 +79,14 @@ static void swapBuffersNSGL(_GLFWwindow* window) static void swapIntervalNSGL(int interval) { @autoreleasepool { + _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); - atomic_store(&window->context.nsgl.swapInterval, interval); - [window->context.nsgl.swapIntervalCond lock]; - window->context.nsgl.swapIntervalsPassed = 0; - [window->context.nsgl.swapIntervalCond unlock]; + if (window) + { + [window->context.nsgl.object setValues:&interval + forParameter:NSOpenGLContextParameterSwapInterval]; + } + } // autoreleasepool } @@ -123,17 +114,6 @@ static void destroyContextNSGL(_GLFWwindow* window) { @autoreleasepool { - if (window->context.nsgl.displayLink) - { - if (CVDisplayLinkIsRunning(window->context.nsgl.displayLink)) - CVDisplayLinkStop(window->context.nsgl.displayLink); - - CVDisplayLinkRelease(window->context.nsgl.displayLink); - } - - [window->context.nsgl.swapIntervalCond release]; - window->context.nsgl.swapIntervalCond = nil; - [window->context.nsgl.pixelFormat release]; window->context.nsgl.pixelFormat = nil; @@ -354,17 +334,10 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, forParameter:NSOpenGLContextParameterSurfaceOpacity]; } - if (window->ns.retina) - [window->ns.view setWantsBestResolutionOpenGLSurface:YES]; - - GLint interval = 0; - [window->context.nsgl.object setValues:&interval - forParameter:NSOpenGLContextParameterSwapInterval]; + [window->ns.view setWantsBestResolutionOpenGLSurface:window->ns.retina]; [window->context.nsgl.object setView:window->ns.view]; - window->context.nsgl.swapIntervalCond = [NSCondition new]; - window->context.makeCurrent = makeContextCurrentNSGL; window->context.swapBuffers = swapBuffersNSGL; window->context.swapInterval = swapIntervalNSGL; @@ -372,26 +345,9 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, window->context.getProcAddress = getProcAddressNSGL; window->context.destroy = destroyContextNSGL; - CVDisplayLinkCreateWithActiveCGDisplays(&window->context.nsgl.displayLink); - CVDisplayLinkSetOutputCallback(window->context.nsgl.displayLink, - &displayLinkCallback, - window); - CVDisplayLinkStart(window->context.nsgl.displayLink); - - _glfwUpdateDisplayLinkDisplayNSGL(window); return GLFW_TRUE; } -void _glfwUpdateDisplayLinkDisplayNSGL(_GLFWwindow* window) -{ - CGDirectDisplayID displayID = - [[[window->ns.object screen] deviceDescription][@"NSScreenNumber"] unsignedIntValue]; - if (!displayID) - return; - - CVDisplayLinkSetCurrentCGDisplay(window->context.nsgl.displayLink, displayID); -} - ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// diff --git a/src/posix_time.c b/src/posix_time.c index 301cb958..ae3d5c78 100644 --- a/src/posix_time.c +++ b/src/posix_time.c @@ -27,8 +27,11 @@ // It is fine to use C99 in this file because it will not be built with VS //======================================================================== +#define _POSIX_C_SOURCE 199309L + #include "internal.h" +#include #include #include @@ -41,7 +44,7 @@ // void _glfwInitTimerPOSIX(void) { -#if defined(CLOCK_MONOTONIC) +#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) @@ -64,7 +67,7 @@ void _glfwInitTimerPOSIX(void) uint64_t _glfwPlatformGetTimerValue(void) { -#if defined(CLOCK_MONOTONIC) +#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) if (_glfw.timer.posix.monotonic) { struct timespec ts; diff --git a/src/vulkan.c b/src/vulkan.c index 6fd0af2c..b5340520 100644 --- a/src/vulkan.c +++ b/src/vulkan.c @@ -57,6 +57,8 @@ GLFWbool _glfwInitVulkan(int mode) _glfw.vk.handle = _glfw_dlopen("vulkan-1.dll"); #elif defined(_GLFW_COCOA) _glfw.vk.handle = _glfw_dlopen("libvulkan.1.dylib"); + if (!_glfw.vk.handle) + _glfw.vk.handle = _glfwLoadLocalVulkanLoaderNS(); #else _glfw.vk.handle = _glfw_dlopen("libvulkan.so.1"); #endif @@ -130,6 +132,8 @@ GLFWbool _glfwInitVulkan(int mode) #elif defined(_GLFW_COCOA) else if (strcmp(ep[i].extensionName, "VK_MVK_macos_surface") == 0) _glfw.vk.MVK_macos_surface = GLFW_TRUE; + else if (strcmp(ep[i].extensionName, "VK_EXT_metal_surface") == 0) + _glfw.vk.EXT_metal_surface = GLFW_TRUE; #elif defined(_GLFW_X11) else if (strcmp(ep[i].extensionName, "VK_KHR_xlib_surface") == 0) _glfw.vk.KHR_xlib_surface = GLFW_TRUE; diff --git a/src/wgl_context.h b/src/wgl_context.h index df983e91..2cf7e4e5 100644 --- a/src/wgl_context.h +++ b/src/wgl_context.h @@ -104,10 +104,6 @@ typedef BOOL (WINAPI * PFN_wglShareLists)(HGLRC,HGLRC); #define wglMakeCurrent _glfw.wgl.MakeCurrent #define wglShareLists _glfw.wgl.ShareLists -#define _GLFW_RECREATION_NOT_NEEDED 0 -#define _GLFW_RECREATION_REQUIRED 1 -#define _GLFW_RECREATION_IMPOSSIBLE 2 - #define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextWGL wgl #define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryWGL wgl diff --git a/src/win32_monitor.c b/src/win32_monitor.c index 3067b65e..5f91c579 100644 --- a/src/win32_monitor.c +++ b/src/win32_monitor.c @@ -33,6 +33,7 @@ #include #include #include +#include // Callback for EnumDisplayMonitors in createMonitor diff --git a/src/win32_window.c b/src/win32_window.c index 3a50c542..0ae0998a 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -36,8 +36,6 @@ #include #include -#define _GLFW_KEY_INVALID -2 - // Returns the window style for the specified window // static DWORD getWindowStyle(const _GLFWwindow* window) @@ -448,77 +446,6 @@ static int getKeyMods(void) return mods; } -// Retrieves and translates modifier keys -// -static int getAsyncKeyMods(void) -{ - int mods = 0; - - if (GetAsyncKeyState(VK_SHIFT) & 0x8000) - mods |= GLFW_MOD_SHIFT; - if (GetAsyncKeyState(VK_CONTROL) & 0x8000) - mods |= GLFW_MOD_CONTROL; - if (GetAsyncKeyState(VK_MENU) & 0x8000) - mods |= GLFW_MOD_ALT; - if ((GetAsyncKeyState(VK_LWIN) | GetAsyncKeyState(VK_RWIN)) & 0x8000) - mods |= GLFW_MOD_SUPER; - if (GetAsyncKeyState(VK_CAPITAL) & 1) - mods |= GLFW_MOD_CAPS_LOCK; - if (GetAsyncKeyState(VK_NUMLOCK) & 1) - mods |= GLFW_MOD_NUM_LOCK; - - return mods; -} - -// Translates a Windows key to the corresponding GLFW key -// -static int translateKey(WPARAM wParam, LPARAM lParam) -{ - // The Ctrl keys require special handling - if (wParam == VK_CONTROL) - { - MSG next; - DWORD time; - - // Right side keys have the extended key bit set - if (HIWORD(lParam) & KF_EXTENDED) - return GLFW_KEY_RIGHT_CONTROL; - - // HACK: Alt Gr sends Left Ctrl and then Right Alt in close sequence - // We only want the Right Alt message, so if the next message is - // Right Alt we ignore this (synthetic) Left Ctrl message - time = GetMessageTime(); - - if (PeekMessageW(&next, NULL, 0, 0, PM_NOREMOVE)) - { - if (next.message == WM_KEYDOWN || - next.message == WM_SYSKEYDOWN || - next.message == WM_KEYUP || - next.message == WM_SYSKEYUP) - { - if (next.wParam == VK_MENU && - (HIWORD(next.lParam) & KF_EXTENDED) && - next.time == time) - { - // Next message is Right Alt down so discard this - return _GLFW_KEY_INVALID; - } - } - } - - return GLFW_KEY_LEFT_CONTROL; - } - - if (wParam == VK_PROCESSKEY) - { - // IME notifies that keys have been filtered by setting the virtual - // key-code to VK_PROCESSKEY - return _GLFW_KEY_INVALID; - } - - return _glfw.win32.keycodes[HIWORD(lParam) & 0x1FF]; -} - static void fitToMonitor(_GLFWwindow* window) { MONITORINFO mi = { sizeof(mi) }; @@ -748,13 +675,64 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_KEYUP: case WM_SYSKEYUP: { - const int key = translateKey(wParam, lParam); - const int scancode = (HIWORD(lParam) & 0x1ff); + int key, scancode; const int action = (HIWORD(lParam) & KF_UP) ? GLFW_RELEASE : GLFW_PRESS; const int mods = getKeyMods(); - if (key == _GLFW_KEY_INVALID) + scancode = (HIWORD(lParam) & (KF_EXTENDED | 0xff)); + if (!scancode) + { + // NOTE: Some synthetic key messages have a scancode of zero + // HACK: Map the virtual key back to a usable scancode + scancode = MapVirtualKeyW((UINT) wParam, MAPVK_VK_TO_VSC); + } + + key = _glfw.win32.keycodes[scancode]; + + // The Ctrl keys require special handling + if (wParam == VK_CONTROL) + { + if (HIWORD(lParam) & KF_EXTENDED) + { + // Right side keys have the extended key bit set + key = GLFW_KEY_RIGHT_CONTROL; + } + else + { + // NOTE: Alt Gr sends Left Ctrl followed by Right Alt + // HACK: We only want one event for Alt Gr, so if we detect + // this sequence we discard this Left Ctrl message now + // and later report Right Alt normally + MSG next; + const DWORD time = GetMessageTime(); + + if (PeekMessageW(&next, NULL, 0, 0, PM_NOREMOVE)) + { + if (next.message == WM_KEYDOWN || + next.message == WM_SYSKEYDOWN || + next.message == WM_KEYUP || + next.message == WM_SYSKEYUP) + { + if (next.wParam == VK_MENU && + (HIWORD(next.lParam) & KF_EXTENDED) && + next.time == time) + { + // Next message is Right Alt down so discard this + break; + } + } + } + + // This is a regular Left Ctrl message + key = GLFW_KEY_LEFT_CONTROL; + } + } + else if (wParam == VK_PROCESSKEY) + { + // IME notifies that keys have been filtered by setting the + // virtual key-code to VK_PROCESSKEY break; + } if (action == GLFW_RELEASE && wParam == VK_SHIFT) { @@ -1934,30 +1912,40 @@ void _glfwPlatformPollEvents(void) } } + // HACK: Release modifier keys that the system did not emit KEYUP for + // NOTE: Shift keys on Windows tend to "stick" when both are pressed as + // no key up message is generated by the first key release + // NOTE: Windows key is not reported as released by the Win+V hotkey + // Other Win hotkeys are handled implicitly by _glfwInputWindowFocus + // because they change the input focus + // NOTE: The other half of this is in the WM_*KEY* handler in windowProc handle = GetActiveWindow(); if (handle) { - // NOTE: Shift keys on Windows tend to "stick" when both are pressed as - // no key up message is generated by the first key release - // The other half of this is in the handling of WM_KEYUP - // HACK: Query actual key state and synthesize release events as needed window = GetPropW(handle, L"GLFW"); if (window) { - const GLFWbool lshift = (GetAsyncKeyState(VK_LSHIFT) & 0x8000) != 0; - const GLFWbool rshift = (GetAsyncKeyState(VK_RSHIFT) & 0x8000) != 0; + int i; + const int keys[4][2] = + { + { VK_LSHIFT, GLFW_KEY_LEFT_SHIFT }, + { VK_RSHIFT, GLFW_KEY_RIGHT_SHIFT }, + { VK_LWIN, GLFW_KEY_LEFT_SUPER }, + { VK_RWIN, GLFW_KEY_RIGHT_SUPER } + }; - if (!lshift && window->keys[GLFW_KEY_LEFT_SHIFT] == GLFW_PRESS) + for (i = 0; i < 4; i++) { - const int mods = getAsyncKeyMods(); - const int scancode = _glfw.win32.scancodes[GLFW_KEY_LEFT_SHIFT]; - _glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, scancode, GLFW_RELEASE, mods); - } - else if (!rshift && window->keys[GLFW_KEY_RIGHT_SHIFT] == GLFW_PRESS) - { - const int mods = getAsyncKeyMods(); - const int scancode = _glfw.win32.scancodes[GLFW_KEY_RIGHT_SHIFT]; - _glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, scancode, GLFW_RELEASE, mods); + const int vk = keys[i][0]; + const int key = keys[i][1]; + const int scancode = _glfw.win32.scancodes[key]; + + if ((GetKeyState(vk) & 0x8000)) + continue; + if (window->keys[key] != GLFW_PRESS) + continue; + + _glfwInputKey(window, key, scancode, GLFW_RELEASE, getKeyMods()); } } } @@ -2039,6 +2027,13 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) const char* _glfwPlatformGetScancodeName(int scancode) { + if (scancode < 0 || scancode > (KF_EXTENDED | 0xff) || + _glfw.win32.keycodes[scancode] == GLFW_KEY_UNKNOWN) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode"); + return NULL; + } + return _glfw.win32.keynames[_glfw.win32.keycodes[scancode]]; } diff --git a/src/wl_init.c b/src/wl_init.c index 9e692f0e..558ff8a8 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -26,6 +26,8 @@ // It is fine to use C99 in this file because it will not be built with VS //======================================================================== +#define _POSIX_C_SOURCE 199309L + #include "internal.h" #include @@ -38,6 +40,7 @@ #include #include #include +#include #include @@ -125,6 +128,7 @@ static void pointerHandleLeave(void* data, _glfw.wl.serial = serial; _glfw.wl.pointerFocus = NULL; _glfwInputCursorEnter(window, GLFW_FALSE); + _glfw.wl.cursorPreviousName = NULL; } static void setCursor(_GLFWwindow* window, const char* name) @@ -196,6 +200,7 @@ static void pointerHandleMotion(void* data, window->wl.cursorPosX = x; window->wl.cursorPosY = y; _glfwInputCursorPos(window, x, y); + _glfw.wl.cursorPreviousName = NULL; return; case topDecoration: if (y < _GLFW_DECORATION_WIDTH) diff --git a/src/x11_init.c b/src/x11_init.c index d44d4e2a..e5bd578d 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -29,33 +29,23 @@ #include "internal.h" -#include - #include #include #include #include #include +#include -// Translate an X11 key code to a GLFW key code. +// Translate the X11 KeySyms for a key to a GLFW key code +// NOTE: This is only used as a fallback, in case the XKB method fails +// It is layout-dependent and will fail partially on most non-US layouts // -static int translateKeyCode(int scancode) +static int translateKeySyms(const KeySym* keysyms, int width) { - int keySym; - - // Valid key code range is [8,255], according to the Xlib manual - if (scancode < 8 || scancode > 255) - return GLFW_KEY_UNKNOWN; - - if (_glfw.x11.xkb.available) + if (width > 1) { - // Try secondary keysym, for numeric keypad keys - // Note: This way we always force "NumLock = ON", which is intentional - // since the returned key code should correspond to a physical - // location. - keySym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, _glfw.x11.xkb.group, 1); - switch (keySym) + switch (keysyms[1]) { case XK_KP_0: return GLFW_KEY_KP_0; case XK_KP_1: return GLFW_KEY_KP_1; @@ -73,22 +63,9 @@ static int translateKeyCode(int scancode) case XK_KP_Enter: return GLFW_KEY_KP_ENTER; default: break; } - - // Now try primary keysym for function keys (non-printable keys) - // These should not depend on the current keyboard layout - keySym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, _glfw.x11.xkb.group, 0); - } - else - { - int dummy; - KeySym* keySyms; - - keySyms = XGetKeyboardMapping(_glfw.x11.display, scancode, 1, &dummy); - keySym = keySyms[0]; - XFree(keySyms); } - switch (keySym) + switch (keysyms[0]) { case XK_Escape: return GLFW_KEY_ESCAPE; case XK_Tab: return GLFW_KEY_TAB; @@ -232,7 +209,7 @@ static int translateKeyCode(int scancode) // static void createKeyTables(void) { - int scancode, key; + int scancode, scancodeMin, scancodeMax; memset(_glfw.x11.keycodes, -1, sizeof(_glfw.x11.keycodes)); memset(_glfw.x11.scancodes, -1, sizeof(_glfw.x11.scancodes)); @@ -242,89 +219,217 @@ static void createKeyTables(void) // 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); - XkbGetNames(_glfw.x11.display, XkbKeyNamesMask, desc); + XkbGetNames(_glfw.x11.display, XkbKeyNamesMask | XkbKeyAliasesMask, desc); + + scancodeMin = desc->min_key_code; + scancodeMax = desc->max_key_code; + + const struct + { + int key; + char* name; + } keymap[] = + { + { GLFW_KEY_GRAVE_ACCENT, "TLDE" }, + { GLFW_KEY_1, "AE01" }, + { GLFW_KEY_2, "AE02" }, + { GLFW_KEY_3, "AE03" }, + { GLFW_KEY_4, "AE04" }, + { GLFW_KEY_5, "AE05" }, + { GLFW_KEY_6, "AE06" }, + { GLFW_KEY_7, "AE07" }, + { GLFW_KEY_8, "AE08" }, + { GLFW_KEY_9, "AE09" }, + { GLFW_KEY_0, "AE10" }, + { GLFW_KEY_MINUS, "AE11" }, + { GLFW_KEY_EQUAL, "AE12" }, + { GLFW_KEY_Q, "AD01" }, + { GLFW_KEY_W, "AD02" }, + { GLFW_KEY_E, "AD03" }, + { GLFW_KEY_R, "AD04" }, + { GLFW_KEY_T, "AD05" }, + { GLFW_KEY_Y, "AD06" }, + { GLFW_KEY_U, "AD07" }, + { GLFW_KEY_I, "AD08" }, + { GLFW_KEY_O, "AD09" }, + { GLFW_KEY_P, "AD10" }, + { GLFW_KEY_LEFT_BRACKET, "AD11" }, + { GLFW_KEY_RIGHT_BRACKET, "AD12" }, + { GLFW_KEY_A, "AC01" }, + { GLFW_KEY_S, "AC02" }, + { GLFW_KEY_D, "AC03" }, + { GLFW_KEY_F, "AC04" }, + { GLFW_KEY_G, "AC05" }, + { GLFW_KEY_H, "AC06" }, + { GLFW_KEY_J, "AC07" }, + { GLFW_KEY_K, "AC08" }, + { GLFW_KEY_L, "AC09" }, + { GLFW_KEY_SEMICOLON, "AC10" }, + { GLFW_KEY_APOSTROPHE, "AC11" }, + { GLFW_KEY_Z, "AB01" }, + { GLFW_KEY_X, "AB02" }, + { GLFW_KEY_C, "AB03" }, + { GLFW_KEY_V, "AB04" }, + { GLFW_KEY_B, "AB05" }, + { GLFW_KEY_N, "AB06" }, + { GLFW_KEY_M, "AB07" }, + { GLFW_KEY_COMMA, "AB08" }, + { GLFW_KEY_PERIOD, "AB09" }, + { GLFW_KEY_SLASH, "AB10" }, + { GLFW_KEY_BACKSLASH, "BKSL" }, + { GLFW_KEY_WORLD_1, "LSGT" }, + { GLFW_KEY_SPACE, "SPCE" }, + { GLFW_KEY_ESCAPE, "ESC" }, + { GLFW_KEY_ENTER, "RTRN" }, + { GLFW_KEY_TAB, "TAB" }, + { GLFW_KEY_BACKSPACE, "BKSP" }, + { GLFW_KEY_INSERT, "INS" }, + { GLFW_KEY_DELETE, "DELE" }, + { GLFW_KEY_RIGHT, "RGHT" }, + { GLFW_KEY_LEFT, "LEFT" }, + { GLFW_KEY_DOWN, "DOWN" }, + { GLFW_KEY_UP, "UP" }, + { GLFW_KEY_PAGE_UP, "PGUP" }, + { GLFW_KEY_PAGE_DOWN, "PGDN" }, + { GLFW_KEY_HOME, "HOME" }, + { GLFW_KEY_END, "END" }, + { GLFW_KEY_CAPS_LOCK, "CAPS" }, + { GLFW_KEY_SCROLL_LOCK, "SCLK" }, + { GLFW_KEY_NUM_LOCK, "NMLK" }, + { GLFW_KEY_PRINT_SCREEN, "PRSC" }, + { GLFW_KEY_PAUSE, "PAUS" }, + { GLFW_KEY_F1, "FK01" }, + { GLFW_KEY_F2, "FK02" }, + { GLFW_KEY_F3, "FK03" }, + { GLFW_KEY_F4, "FK04" }, + { GLFW_KEY_F5, "FK05" }, + { GLFW_KEY_F6, "FK06" }, + { GLFW_KEY_F7, "FK07" }, + { GLFW_KEY_F8, "FK08" }, + { GLFW_KEY_F9, "FK09" }, + { GLFW_KEY_F10, "FK10" }, + { GLFW_KEY_F11, "FK11" }, + { GLFW_KEY_F12, "FK12" }, + { GLFW_KEY_F13, "FK13" }, + { GLFW_KEY_F14, "FK14" }, + { GLFW_KEY_F15, "FK15" }, + { GLFW_KEY_F16, "FK16" }, + { GLFW_KEY_F17, "FK17" }, + { GLFW_KEY_F18, "FK18" }, + { GLFW_KEY_F19, "FK19" }, + { GLFW_KEY_F20, "FK20" }, + { GLFW_KEY_F21, "FK21" }, + { GLFW_KEY_F22, "FK22" }, + { GLFW_KEY_F23, "FK23" }, + { GLFW_KEY_F24, "FK24" }, + { GLFW_KEY_F25, "FK25" }, + { GLFW_KEY_KP_0, "KP0" }, + { GLFW_KEY_KP_1, "KP1" }, + { GLFW_KEY_KP_2, "KP2" }, + { GLFW_KEY_KP_3, "KP3" }, + { GLFW_KEY_KP_4, "KP4" }, + { GLFW_KEY_KP_5, "KP5" }, + { GLFW_KEY_KP_6, "KP6" }, + { GLFW_KEY_KP_7, "KP7" }, + { GLFW_KEY_KP_8, "KP8" }, + { GLFW_KEY_KP_9, "KP9" }, + { GLFW_KEY_KP_DECIMAL, "KPDL" }, + { GLFW_KEY_KP_DIVIDE, "KPDV" }, + { GLFW_KEY_KP_MULTIPLY, "KPMU" }, + { GLFW_KEY_KP_SUBTRACT, "KPSU" }, + { GLFW_KEY_KP_ADD, "KPAD" }, + { GLFW_KEY_KP_ENTER, "KPEN" }, + { GLFW_KEY_KP_EQUAL, "KPEQ" }, + { GLFW_KEY_LEFT_SHIFT, "LFSH" }, + { GLFW_KEY_LEFT_CONTROL, "LCTL" }, + { GLFW_KEY_LEFT_ALT, "LALT" }, + { GLFW_KEY_LEFT_SUPER, "LWIN" }, + { GLFW_KEY_RIGHT_SHIFT, "RTSH" }, + { GLFW_KEY_RIGHT_CONTROL, "RCTL" }, + { GLFW_KEY_RIGHT_ALT, "RALT" }, + { GLFW_KEY_RIGHT_ALT, "LVL3" }, + { GLFW_KEY_RIGHT_ALT, "MDSW" }, + { GLFW_KEY_RIGHT_SUPER, "RWIN" }, + { GLFW_KEY_MENU, "MENU" } + }; // Find the X11 key code -> GLFW key code mapping - for (scancode = desc->min_key_code; scancode <= desc->max_key_code; scancode++) + for (scancode = scancodeMin; scancode <= scancodeMax; scancode++) { - memcpy(name, desc->names->keys[scancode].name, XkbKeyNameLength); - name[XkbKeyNameLength] = '\0'; + int key = GLFW_KEY_UNKNOWN; - // Map the key name to a GLFW key code. Note: We only map printable - // keys here, and we use the US keyboard layout. The rest of the - // keys (function keys) are mapped using traditional KeySym - // translations. - if (strcmp(name, "TLDE") == 0) key = GLFW_KEY_GRAVE_ACCENT; - else if (strcmp(name, "AE01") == 0) key = GLFW_KEY_1; - else if (strcmp(name, "AE02") == 0) key = GLFW_KEY_2; - else if (strcmp(name, "AE03") == 0) key = GLFW_KEY_3; - else if (strcmp(name, "AE04") == 0) key = GLFW_KEY_4; - else if (strcmp(name, "AE05") == 0) key = GLFW_KEY_5; - else if (strcmp(name, "AE06") == 0) key = GLFW_KEY_6; - else if (strcmp(name, "AE07") == 0) key = GLFW_KEY_7; - else if (strcmp(name, "AE08") == 0) key = GLFW_KEY_8; - else if (strcmp(name, "AE09") == 0) key = GLFW_KEY_9; - else if (strcmp(name, "AE10") == 0) key = GLFW_KEY_0; - else if (strcmp(name, "AE11") == 0) key = GLFW_KEY_MINUS; - else if (strcmp(name, "AE12") == 0) key = GLFW_KEY_EQUAL; - else if (strcmp(name, "AD01") == 0) key = GLFW_KEY_Q; - else if (strcmp(name, "AD02") == 0) key = GLFW_KEY_W; - else if (strcmp(name, "AD03") == 0) key = GLFW_KEY_E; - else if (strcmp(name, "AD04") == 0) key = GLFW_KEY_R; - else if (strcmp(name, "AD05") == 0) key = GLFW_KEY_T; - else if (strcmp(name, "AD06") == 0) key = GLFW_KEY_Y; - else if (strcmp(name, "AD07") == 0) key = GLFW_KEY_U; - else if (strcmp(name, "AD08") == 0) key = GLFW_KEY_I; - else if (strcmp(name, "AD09") == 0) key = GLFW_KEY_O; - else if (strcmp(name, "AD10") == 0) key = GLFW_KEY_P; - else if (strcmp(name, "AD11") == 0) key = GLFW_KEY_LEFT_BRACKET; - else if (strcmp(name, "AD12") == 0) key = GLFW_KEY_RIGHT_BRACKET; - else if (strcmp(name, "AC01") == 0) key = GLFW_KEY_A; - else if (strcmp(name, "AC02") == 0) key = GLFW_KEY_S; - else if (strcmp(name, "AC03") == 0) key = GLFW_KEY_D; - else if (strcmp(name, "AC04") == 0) key = GLFW_KEY_F; - else if (strcmp(name, "AC05") == 0) key = GLFW_KEY_G; - else if (strcmp(name, "AC06") == 0) key = GLFW_KEY_H; - else if (strcmp(name, "AC07") == 0) key = GLFW_KEY_J; - else if (strcmp(name, "AC08") == 0) key = GLFW_KEY_K; - else if (strcmp(name, "AC09") == 0) key = GLFW_KEY_L; - else if (strcmp(name, "AC10") == 0) key = GLFW_KEY_SEMICOLON; - else if (strcmp(name, "AC11") == 0) key = GLFW_KEY_APOSTROPHE; - else if (strcmp(name, "AB01") == 0) key = GLFW_KEY_Z; - else if (strcmp(name, "AB02") == 0) key = GLFW_KEY_X; - else if (strcmp(name, "AB03") == 0) key = GLFW_KEY_C; - else if (strcmp(name, "AB04") == 0) key = GLFW_KEY_V; - else if (strcmp(name, "AB05") == 0) key = GLFW_KEY_B; - else if (strcmp(name, "AB06") == 0) key = GLFW_KEY_N; - else if (strcmp(name, "AB07") == 0) key = GLFW_KEY_M; - else if (strcmp(name, "AB08") == 0) key = GLFW_KEY_COMMA; - else if (strcmp(name, "AB09") == 0) key = GLFW_KEY_PERIOD; - else if (strcmp(name, "AB10") == 0) key = GLFW_KEY_SLASH; - else if (strcmp(name, "BKSL") == 0) key = GLFW_KEY_BACKSLASH; - else if (strcmp(name, "LSGT") == 0) key = GLFW_KEY_WORLD_1; - else key = GLFW_KEY_UNKNOWN; + // Map the key name to a GLFW key code. Note: We use the US + // keyboard layout. Because function keys aren't mapped correctly + // when using traditional KeySym translations, they are mapped + // here instead. + for (int i = 0; i < sizeof(keymap) / sizeof(keymap[0]); i++) + { + if (strncmp(desc->names->keys[scancode].name, + keymap[i].name, + XkbKeyNameLength) == 0) + { + key = keymap[i].key; + break; + } + } - if ((scancode >= 0) && (scancode < 256)) - _glfw.x11.keycodes[scancode] = key; + // Fall back to key aliases in case the key name did not match + for (int i = 0; i < desc->names->num_key_aliases; i++) + { + if (key != GLFW_KEY_UNKNOWN) + break; + + if (strncmp(desc->names->key_aliases[i].real, + desc->names->keys[scancode].name, + XkbKeyNameLength) != 0) + { + continue; + } + + for (int j = 0; j < sizeof(keymap) / sizeof(keymap[0]); j++) + { + if (strncmp(desc->names->key_aliases[i].alias, + keymap[j].name, + XkbKeyNameLength) == 0) + { + key = keymap[j].key; + break; + } + } + } + + _glfw.x11.keycodes[scancode] = key; } XkbFreeNames(desc, XkbKeyNamesMask, True); XkbFreeKeyboard(desc, 0, True); } + else + XDisplayKeycodes(_glfw.x11.display, &scancodeMin, &scancodeMax); - for (scancode = 0; scancode < 256; scancode++) + int width; + KeySym* keysyms = XGetKeyboardMapping(_glfw.x11.display, + scancodeMin, + scancodeMax - scancodeMin + 1, + &width); + + for (scancode = scancodeMin; scancode <= scancodeMax; scancode++) { // Translate the un-translated key codes using traditional X11 KeySym // lookups if (_glfw.x11.keycodes[scancode] < 0) - _glfw.x11.keycodes[scancode] = translateKeyCode(scancode); + { + const size_t base = (scancode - scancodeMin) * width; + _glfw.x11.keycodes[scancode] = translateKeySyms(&keysyms[base], width); + } // Store the reverse translation for faster key name lookup if (_glfw.x11.keycodes[scancode] > 0) _glfw.x11.scancodes[_glfw.x11.keycodes[scancode]] = scancode; } + + XFree(keysyms); } // Check whether the IM has a usable style @@ -350,6 +455,40 @@ static GLFWbool hasUsableInputMethodStyle(void) return found; } +static void inputMethodDestroyCallback(XIM im, XPointer clientData, XPointer callData) +{ + _glfw.x11.im = NULL; +} + +static void inputMethodInstantiateCallback(Display* display, + XPointer clientData, + XPointer callData) +{ + if (_glfw.x11.im) + return; + + _glfw.x11.im = XOpenIM(_glfw.x11.display, 0, NULL, NULL); + if (_glfw.x11.im) + { + if (!hasUsableInputMethodStyle()) + { + XCloseIM(_glfw.x11.im); + _glfw.x11.im = NULL; + } + } + + if (_glfw.x11.im) + { + XIMCallback callback; + callback.callback = (XIMProc) inputMethodDestroyCallback; + callback.client_data = NULL; + XSetIMValues(_glfw.x11.im, XNDestroyCallback, &callback, NULL); + + for (_GLFWwindow* window = _glfw.windowListHead; window; window = window->next) + _glfwCreateInputContextX11(window); + } +} + // Check whether the specified atom is supported // static Atom getSupportedAtom(Atom* supportedAtoms, @@ -857,6 +996,9 @@ static Window createHelperWindow(void) // static int errorHandler(Display *display, XErrorEvent* event) { + if (_glfw.x11.display != display) + return 0; + _glfw.x11.errorCode = event->error_code; return 0; } @@ -937,15 +1079,224 @@ Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot) int _glfwPlatformInit(void) { -#if !defined(X_HAVE_UTF8_STRING) - // HACK: If the current locale is "C" and the Xlib UTF-8 functions are - // unavailable, apply the environment's locale in the hope that it's - // both available and not "C" - // This is done because the "C" locale breaks wide character input, - // which is what we fall back on when UTF-8 support is missing + // HACK: If the application has left the locale as "C" then both wide + // character text input and explicit UTF-8 input via XIM will break + // This sets the CTYPE part of the current locale from the environment + // in the hope that it is set to something more sane than "C" if (strcmp(setlocale(LC_CTYPE, NULL), "C") == 0) setlocale(LC_CTYPE, ""); + +#if defined(__CYGWIN__) + _glfw.x11.xlib.handle = _glfw_dlopen("libX11-6.so"); +#else + _glfw.x11.xlib.handle = _glfw_dlopen("libX11.so.6"); #endif + if (!_glfw.x11.xlib.handle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to load Xlib"); + return GLFW_FALSE; + } + + _glfw.x11.xlib.AllocClassHint = (PFN_XAllocClassHint) + _glfw_dlsym(_glfw.x11.xlib.handle, "XAllocClassHint"); + _glfw.x11.xlib.AllocSizeHints = (PFN_XAllocSizeHints) + _glfw_dlsym(_glfw.x11.xlib.handle, "XAllocSizeHints"); + _glfw.x11.xlib.AllocWMHints = (PFN_XAllocWMHints) + _glfw_dlsym(_glfw.x11.xlib.handle, "XAllocWMHints"); + _glfw.x11.xlib.ChangeProperty = (PFN_XChangeProperty) + _glfw_dlsym(_glfw.x11.xlib.handle, "XChangeProperty"); + _glfw.x11.xlib.ChangeWindowAttributes = (PFN_XChangeWindowAttributes) + _glfw_dlsym(_glfw.x11.xlib.handle, "XChangeWindowAttributes"); + _glfw.x11.xlib.CheckIfEvent = (PFN_XCheckIfEvent) + _glfw_dlsym(_glfw.x11.xlib.handle, "XCheckIfEvent"); + _glfw.x11.xlib.CheckTypedWindowEvent = (PFN_XCheckTypedWindowEvent) + _glfw_dlsym(_glfw.x11.xlib.handle, "XCheckTypedWindowEvent"); + _glfw.x11.xlib.CloseDisplay = (PFN_XCloseDisplay) + _glfw_dlsym(_glfw.x11.xlib.handle, "XCloseDisplay"); + _glfw.x11.xlib.CloseIM = (PFN_XCloseIM) + _glfw_dlsym(_glfw.x11.xlib.handle, "XCloseIM"); + _glfw.x11.xlib.ConvertSelection = (PFN_XConvertSelection) + _glfw_dlsym(_glfw.x11.xlib.handle, "XConvertSelection"); + _glfw.x11.xlib.CreateColormap = (PFN_XCreateColormap) + _glfw_dlsym(_glfw.x11.xlib.handle, "XCreateColormap"); + _glfw.x11.xlib.CreateFontCursor = (PFN_XCreateFontCursor) + _glfw_dlsym(_glfw.x11.xlib.handle, "XCreateFontCursor"); + _glfw.x11.xlib.CreateIC = (PFN_XCreateIC) + _glfw_dlsym(_glfw.x11.xlib.handle, "XCreateIC"); + _glfw.x11.xlib.CreateWindow = (PFN_XCreateWindow) + _glfw_dlsym(_glfw.x11.xlib.handle, "XCreateWindow"); + _glfw.x11.xlib.DefineCursor = (PFN_XDefineCursor) + _glfw_dlsym(_glfw.x11.xlib.handle, "XDefineCursor"); + _glfw.x11.xlib.DeleteContext = (PFN_XDeleteContext) + _glfw_dlsym(_glfw.x11.xlib.handle, "XDeleteContext"); + _glfw.x11.xlib.DeleteProperty = (PFN_XDeleteProperty) + _glfw_dlsym(_glfw.x11.xlib.handle, "XDeleteProperty"); + _glfw.x11.xlib.DestroyIC = (PFN_XDestroyIC) + _glfw_dlsym(_glfw.x11.xlib.handle, "XDestroyIC"); + _glfw.x11.xlib.DestroyWindow = (PFN_XDestroyWindow) + _glfw_dlsym(_glfw.x11.xlib.handle, "XDestroyWindow"); + _glfw.x11.xlib.DisplayKeycodes = (PFN_XDisplayKeycodes) + _glfw_dlsym(_glfw.x11.xlib.handle, "XDisplayKeycodes"); + _glfw.x11.xlib.EventsQueued = (PFN_XEventsQueued) + _glfw_dlsym(_glfw.x11.xlib.handle, "XEventsQueued"); + _glfw.x11.xlib.FilterEvent = (PFN_XFilterEvent) + _glfw_dlsym(_glfw.x11.xlib.handle, "XFilterEvent"); + _glfw.x11.xlib.FindContext = (PFN_XFindContext) + _glfw_dlsym(_glfw.x11.xlib.handle, "XFindContext"); + _glfw.x11.xlib.Flush = (PFN_XFlush) + _glfw_dlsym(_glfw.x11.xlib.handle, "XFlush"); + _glfw.x11.xlib.Free = (PFN_XFree) + _glfw_dlsym(_glfw.x11.xlib.handle, "XFree"); + _glfw.x11.xlib.FreeColormap = (PFN_XFreeColormap) + _glfw_dlsym(_glfw.x11.xlib.handle, "XFreeColormap"); + _glfw.x11.xlib.FreeCursor = (PFN_XFreeCursor) + _glfw_dlsym(_glfw.x11.xlib.handle, "XFreeCursor"); + _glfw.x11.xlib.FreeEventData = (PFN_XFreeEventData) + _glfw_dlsym(_glfw.x11.xlib.handle, "XFreeEventData"); + _glfw.x11.xlib.GetErrorText = (PFN_XGetErrorText) + _glfw_dlsym(_glfw.x11.xlib.handle, "XGetErrorText"); + _glfw.x11.xlib.GetEventData = (PFN_XGetEventData) + _glfw_dlsym(_glfw.x11.xlib.handle, "XGetEventData"); + _glfw.x11.xlib.GetICValues = (PFN_XGetICValues) + _glfw_dlsym(_glfw.x11.xlib.handle, "XGetICValues"); + _glfw.x11.xlib.GetIMValues = (PFN_XGetIMValues) + _glfw_dlsym(_glfw.x11.xlib.handle, "XGetIMValues"); + _glfw.x11.xlib.GetInputFocus = (PFN_XGetInputFocus) + _glfw_dlsym(_glfw.x11.xlib.handle, "XGetInputFocus"); + _glfw.x11.xlib.GetKeyboardMapping = (PFN_XGetKeyboardMapping) + _glfw_dlsym(_glfw.x11.xlib.handle, "XGetKeyboardMapping"); + _glfw.x11.xlib.GetScreenSaver = (PFN_XGetScreenSaver) + _glfw_dlsym(_glfw.x11.xlib.handle, "XGetScreenSaver"); + _glfw.x11.xlib.GetSelectionOwner = (PFN_XGetSelectionOwner) + _glfw_dlsym(_glfw.x11.xlib.handle, "XGetSelectionOwner"); + _glfw.x11.xlib.GetVisualInfo = (PFN_XGetVisualInfo) + _glfw_dlsym(_glfw.x11.xlib.handle, "XGetVisualInfo"); + _glfw.x11.xlib.GetWMNormalHints = (PFN_XGetWMNormalHints) + _glfw_dlsym(_glfw.x11.xlib.handle, "XGetWMNormalHints"); + _glfw.x11.xlib.GetWindowAttributes = (PFN_XGetWindowAttributes) + _glfw_dlsym(_glfw.x11.xlib.handle, "XGetWindowAttributes"); + _glfw.x11.xlib.GetWindowProperty = (PFN_XGetWindowProperty) + _glfw_dlsym(_glfw.x11.xlib.handle, "XGetWindowProperty"); + _glfw.x11.xlib.GrabPointer = (PFN_XGrabPointer) + _glfw_dlsym(_glfw.x11.xlib.handle, "XGrabPointer"); + _glfw.x11.xlib.IconifyWindow = (PFN_XIconifyWindow) + _glfw_dlsym(_glfw.x11.xlib.handle, "XIconifyWindow"); + _glfw.x11.xlib.InitThreads = (PFN_XInitThreads) + _glfw_dlsym(_glfw.x11.xlib.handle, "XInitThreads"); + _glfw.x11.xlib.InternAtom = (PFN_XInternAtom) + _glfw_dlsym(_glfw.x11.xlib.handle, "XInternAtom"); + _glfw.x11.xlib.LookupString = (PFN_XLookupString) + _glfw_dlsym(_glfw.x11.xlib.handle, "XLookupString"); + _glfw.x11.xlib.MapRaised = (PFN_XMapRaised) + _glfw_dlsym(_glfw.x11.xlib.handle, "XMapRaised"); + _glfw.x11.xlib.MapWindow = (PFN_XMapWindow) + _glfw_dlsym(_glfw.x11.xlib.handle, "XMapWindow"); + _glfw.x11.xlib.MoveResizeWindow = (PFN_XMoveResizeWindow) + _glfw_dlsym(_glfw.x11.xlib.handle, "XMoveResizeWindow"); + _glfw.x11.xlib.MoveWindow = (PFN_XMoveWindow) + _glfw_dlsym(_glfw.x11.xlib.handle, "XMoveWindow"); + _glfw.x11.xlib.NextEvent = (PFN_XNextEvent) + _glfw_dlsym(_glfw.x11.xlib.handle, "XNextEvent"); + _glfw.x11.xlib.OpenDisplay = (PFN_XOpenDisplay) + _glfw_dlsym(_glfw.x11.xlib.handle, "XOpenDisplay"); + _glfw.x11.xlib.OpenIM = (PFN_XOpenIM) + _glfw_dlsym(_glfw.x11.xlib.handle, "XOpenIM"); + _glfw.x11.xlib.PeekEvent = (PFN_XPeekEvent) + _glfw_dlsym(_glfw.x11.xlib.handle, "XPeekEvent"); + _glfw.x11.xlib.Pending = (PFN_XPending) + _glfw_dlsym(_glfw.x11.xlib.handle, "XPending"); + _glfw.x11.xlib.QueryExtension = (PFN_XQueryExtension) + _glfw_dlsym(_glfw.x11.xlib.handle, "XQueryExtension"); + _glfw.x11.xlib.QueryPointer = (PFN_XQueryPointer) + _glfw_dlsym(_glfw.x11.xlib.handle, "XQueryPointer"); + _glfw.x11.xlib.RaiseWindow = (PFN_XRaiseWindow) + _glfw_dlsym(_glfw.x11.xlib.handle, "XRaiseWindow"); + _glfw.x11.xlib.RegisterIMInstantiateCallback = (PFN_XRegisterIMInstantiateCallback) + _glfw_dlsym(_glfw.x11.xlib.handle, "XRegisterIMInstantiateCallback"); + _glfw.x11.xlib.ResizeWindow = (PFN_XResizeWindow) + _glfw_dlsym(_glfw.x11.xlib.handle, "XResizeWindow"); + _glfw.x11.xlib.ResourceManagerString = (PFN_XResourceManagerString) + _glfw_dlsym(_glfw.x11.xlib.handle, "XResourceManagerString"); + _glfw.x11.xlib.SaveContext = (PFN_XSaveContext) + _glfw_dlsym(_glfw.x11.xlib.handle, "XSaveContext"); + _glfw.x11.xlib.SelectInput = (PFN_XSelectInput) + _glfw_dlsym(_glfw.x11.xlib.handle, "XSelectInput"); + _glfw.x11.xlib.SendEvent = (PFN_XSendEvent) + _glfw_dlsym(_glfw.x11.xlib.handle, "XSendEvent"); + _glfw.x11.xlib.SetClassHint = (PFN_XSetClassHint) + _glfw_dlsym(_glfw.x11.xlib.handle, "XSetClassHint"); + _glfw.x11.xlib.SetErrorHandler = (PFN_XSetErrorHandler) + _glfw_dlsym(_glfw.x11.xlib.handle, "XSetErrorHandler"); + _glfw.x11.xlib.SetICFocus = (PFN_XSetICFocus) + _glfw_dlsym(_glfw.x11.xlib.handle, "XSetICFocus"); + _glfw.x11.xlib.SetIMValues = (PFN_XSetIMValues) + _glfw_dlsym(_glfw.x11.xlib.handle, "XSetIMValues"); + _glfw.x11.xlib.SetInputFocus = (PFN_XSetInputFocus) + _glfw_dlsym(_glfw.x11.xlib.handle, "XSetInputFocus"); + _glfw.x11.xlib.SetLocaleModifiers = (PFN_XSetLocaleModifiers) + _glfw_dlsym(_glfw.x11.xlib.handle, "XSetLocaleModifiers"); + _glfw.x11.xlib.SetScreenSaver = (PFN_XSetScreenSaver) + _glfw_dlsym(_glfw.x11.xlib.handle, "XSetScreenSaver"); + _glfw.x11.xlib.SetSelectionOwner = (PFN_XSetSelectionOwner) + _glfw_dlsym(_glfw.x11.xlib.handle, "XSetSelectionOwner"); + _glfw.x11.xlib.SetWMHints = (PFN_XSetWMHints) + _glfw_dlsym(_glfw.x11.xlib.handle, "XSetWMHints"); + _glfw.x11.xlib.SetWMNormalHints = (PFN_XSetWMNormalHints) + _glfw_dlsym(_glfw.x11.xlib.handle, "XSetWMNormalHints"); + _glfw.x11.xlib.SetWMProtocols = (PFN_XSetWMProtocols) + _glfw_dlsym(_glfw.x11.xlib.handle, "XSetWMProtocols"); + _glfw.x11.xlib.SupportsLocale = (PFN_XSupportsLocale) + _glfw_dlsym(_glfw.x11.xlib.handle, "XSupportsLocale"); + _glfw.x11.xlib.Sync = (PFN_XSync) + _glfw_dlsym(_glfw.x11.xlib.handle, "XSync"); + _glfw.x11.xlib.TranslateCoordinates = (PFN_XTranslateCoordinates) + _glfw_dlsym(_glfw.x11.xlib.handle, "XTranslateCoordinates"); + _glfw.x11.xlib.UndefineCursor = (PFN_XUndefineCursor) + _glfw_dlsym(_glfw.x11.xlib.handle, "XUndefineCursor"); + _glfw.x11.xlib.UngrabPointer = (PFN_XUngrabPointer) + _glfw_dlsym(_glfw.x11.xlib.handle, "XUngrabPointer"); + _glfw.x11.xlib.UnmapWindow = (PFN_XUnmapWindow) + _glfw_dlsym(_glfw.x11.xlib.handle, "XUnmapWindow"); + _glfw.x11.xlib.UnsetICFocus = (PFN_XUnsetICFocus) + _glfw_dlsym(_glfw.x11.xlib.handle, "XUnsetICFocus"); + _glfw.x11.xlib.VisualIDFromVisual = (PFN_XVisualIDFromVisual) + _glfw_dlsym(_glfw.x11.xlib.handle, "XVisualIDFromVisual"); + _glfw.x11.xlib.WarpPointer = (PFN_XWarpPointer) + _glfw_dlsym(_glfw.x11.xlib.handle, "XWarpPointer"); + _glfw.x11.xkb.FreeKeyboard = (PFN_XkbFreeKeyboard) + _glfw_dlsym(_glfw.x11.xlib.handle, "XkbFreeKeyboard"); + _glfw.x11.xkb.FreeNames = (PFN_XkbFreeNames) + _glfw_dlsym(_glfw.x11.xlib.handle, "XkbFreeNames"); + _glfw.x11.xkb.GetMap = (PFN_XkbGetMap) + _glfw_dlsym(_glfw.x11.xlib.handle, "XkbGetMap"); + _glfw.x11.xkb.GetNames = (PFN_XkbGetNames) + _glfw_dlsym(_glfw.x11.xlib.handle, "XkbGetNames"); + _glfw.x11.xkb.GetState = (PFN_XkbGetState) + _glfw_dlsym(_glfw.x11.xlib.handle, "XkbGetState"); + _glfw.x11.xkb.KeycodeToKeysym = (PFN_XkbKeycodeToKeysym) + _glfw_dlsym(_glfw.x11.xlib.handle, "XkbKeycodeToKeysym"); + _glfw.x11.xkb.QueryExtension = (PFN_XkbQueryExtension) + _glfw_dlsym(_glfw.x11.xlib.handle, "XkbQueryExtension"); + _glfw.x11.xkb.SelectEventDetails = (PFN_XkbSelectEventDetails) + _glfw_dlsym(_glfw.x11.xlib.handle, "XkbSelectEventDetails"); + _glfw.x11.xkb.SetDetectableAutoRepeat = (PFN_XkbSetDetectableAutoRepeat) + _glfw_dlsym(_glfw.x11.xlib.handle, "XkbSetDetectableAutoRepeat"); + _glfw.x11.xrm.DestroyDatabase = (PFN_XrmDestroyDatabase) + _glfw_dlsym(_glfw.x11.xlib.handle, "XrmDestroyDatabase"); + _glfw.x11.xrm.GetResource = (PFN_XrmGetResource) + _glfw_dlsym(_glfw.x11.xlib.handle, "XrmGetResource"); + _glfw.x11.xrm.GetStringDatabase = (PFN_XrmGetStringDatabase) + _glfw_dlsym(_glfw.x11.xlib.handle, "XrmGetStringDatabase"); + _glfw.x11.xrm.Initialize = (PFN_XrmInitialize) + _glfw_dlsym(_glfw.x11.xlib.handle, "XrmInitialize"); + _glfw.x11.xrm.UniqueQuark = (PFN_XrmUniqueQuark) + _glfw_dlsym(_glfw.x11.xlib.handle, "XrmUniqueQuark"); + _glfw.x11.xlib.UnregisterIMInstantiateCallback = (PFN_XUnregisterIMInstantiateCallback) + _glfw_dlsym(_glfw.x11.xlib.handle, "XUnregisterIMInstantiateCallback"); + _glfw.x11.xlib.utf8LookupString = (PFN_Xutf8LookupString) + _glfw_dlsym(_glfw.x11.xlib.handle, "Xutf8LookupString"); + _glfw.x11.xlib.utf8SetWMProperties = (PFN_Xutf8SetWMProperties) + _glfw_dlsym(_glfw.x11.xlib.handle, "Xutf8SetWMProperties"); XInitThreads(); XrmInitialize(); @@ -984,15 +1335,11 @@ int _glfwPlatformInit(void) { XSetLocaleModifiers(""); - _glfw.x11.im = XOpenIM(_glfw.x11.display, 0, NULL, NULL); - if (_glfw.x11.im) - { - if (!hasUsableInputMethodStyle()) - { - XCloseIM(_glfw.x11.im); - _glfw.x11.im = NULL; - } - } + // If an IM is already present our callback will be called right away + XRegisterIMInstantiateCallback(_glfw.x11.display, + NULL, NULL, NULL, + inputMethodInstantiateCallback, + NULL); } #if defined(__linux__) @@ -1029,6 +1376,11 @@ void _glfwPlatformTerminate(void) free(_glfw.x11.primarySelectionString); free(_glfw.x11.clipboardString); + XUnregisterIMInstantiateCallback(_glfw.x11.display, + NULL, NULL, NULL, + inputMethodInstantiateCallback, + NULL); + if (_glfw.x11.im) { XCloseIM(_glfw.x11.im); @@ -1091,6 +1443,12 @@ void _glfwPlatformTerminate(void) #if defined(__linux__) _glfwTerminateJoysticksLinux(); #endif + + if (_glfw.x11.xlib.handle) + { + _glfw_dlclose(_glfw.x11.xlib.handle); + _glfw.x11.xlib.handle = NULL; + } } const char* _glfwPlatformGetVersionString(void) diff --git a/src/x11_monitor.c b/src/x11_monitor.c index 4d3e1a94..809b93e4 100644 --- a/src/x11_monitor.c +++ b/src/x11_monitor.c @@ -322,12 +322,16 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); - if (xpos) - *xpos = ci->x; - if (ypos) - *ypos = ci->y; + if (ci) + { + if (xpos) + *xpos = ci->x; + if (ypos) + *ypos = ci->y; + + XRRFreeCrtcInfo(ci); + } - XRRFreeCrtcInfo(ci); XRRFreeScreenResources(sr); } } @@ -493,9 +497,15 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); - *mode = vidmodeFromModeInfo(getModeInfo(sr, ci->mode), ci); + if (ci) + { + const XRRModeInfo* mi = getModeInfo(sr, ci->mode); + if (mi) // mi can be NULL if the monitor has been disconnected + *mode = vidmodeFromModeInfo(mi, ci); + + XRRFreeCrtcInfo(ci); + } - XRRFreeCrtcInfo(ci); XRRFreeScreenResources(sr); } else diff --git a/src/x11_platform.h b/src/x11_platform.h index cb3b1068..fe82a72b 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -33,6 +33,7 @@ #include #include #include +#include #include // The XRandR extension provides mode setting and gamma control @@ -47,6 +48,207 @@ // The XInput extension provides raw mouse motion input #include +typedef XClassHint* (* PFN_XAllocClassHint)(void); +typedef XSizeHints* (* PFN_XAllocSizeHints)(void); +typedef XWMHints* (* PFN_XAllocWMHints)(void); +typedef int (* PFN_XChangeProperty)(Display*,Window,Atom,Atom,int,int,const unsigned char*,int); +typedef int (* PFN_XChangeWindowAttributes)(Display*,Window,unsigned long,XSetWindowAttributes*); +typedef Bool (* PFN_XCheckIfEvent)(Display*,XEvent*,Bool(*)(Display*,XEvent*,XPointer),XPointer); +typedef Bool (* PFN_XCheckTypedWindowEvent)(Display*,Window,int,XEvent*); +typedef int (* PFN_XCloseDisplay)(Display*); +typedef Status (* PFN_XCloseIM)(XIM); +typedef int (* PFN_XConvertSelection)(Display*,Atom,Atom,Atom,Window,Time); +typedef Colormap (* PFN_XCreateColormap)(Display*,Window,Visual*,int); +typedef Cursor (* PFN_XCreateFontCursor)(Display*,unsigned int); +typedef XIC (* PFN_XCreateIC)(XIM,...); +typedef Window (* PFN_XCreateWindow)(Display*,Window,int,int,unsigned int,unsigned int,unsigned int,int,unsigned int,Visual*,unsigned long,XSetWindowAttributes*); +typedef int (* PFN_XDefineCursor)(Display*,Window,Cursor); +typedef int (* PFN_XDeleteContext)(Display*,XID,XContext); +typedef int (* PFN_XDeleteProperty)(Display*,Window,Atom); +typedef void (* PFN_XDestroyIC)(XIC); +typedef int (* PFN_XDestroyWindow)(Display*,Window); +typedef int (* PFN_XDisplayKeycodes)(Display*,int*,int*); +typedef int (* PFN_XEventsQueued)(Display*,int); +typedef Bool (* PFN_XFilterEvent)(XEvent*,Window); +typedef int (* PFN_XFindContext)(Display*,XID,XContext,XPointer*); +typedef int (* PFN_XFlush)(Display*); +typedef int (* PFN_XFree)(void*); +typedef int (* PFN_XFreeColormap)(Display*,Colormap); +typedef int (* PFN_XFreeCursor)(Display*,Cursor); +typedef void (* PFN_XFreeEventData)(Display*,XGenericEventCookie*); +typedef int (* PFN_XGetErrorText)(Display*,int,char*,int); +typedef Bool (* PFN_XGetEventData)(Display*,XGenericEventCookie*); +typedef char* (* PFN_XGetICValues)(XIC,...); +typedef char* (* PFN_XGetIMValues)(XIM,...); +typedef int (* PFN_XGetInputFocus)(Display*,Window*,int*); +typedef KeySym* (* PFN_XGetKeyboardMapping)(Display*,KeyCode,int,int*); +typedef int (* PFN_XGetScreenSaver)(Display*,int*,int*,int*,int*); +typedef Window (* PFN_XGetSelectionOwner)(Display*,Atom); +typedef XVisualInfo* (* PFN_XGetVisualInfo)(Display*,long,XVisualInfo*,int*); +typedef Status (* PFN_XGetWMNormalHints)(Display*,Window,XSizeHints*,long*); +typedef Status (* PFN_XGetWindowAttributes)(Display*,Window,XWindowAttributes*); +typedef int (* PFN_XGetWindowProperty)(Display*,Window,Atom,long,long,Bool,Atom,Atom*,int*,unsigned long*,unsigned long*,unsigned char**); +typedef int (* PFN_XGrabPointer)(Display*,Window,Bool,unsigned int,int,int,Window,Cursor,Time); +typedef Status (* PFN_XIconifyWindow)(Display*,Window,int); +typedef Status (* PFN_XInitThreads)(void); +typedef Atom (* PFN_XInternAtom)(Display*,const char*,Bool); +typedef int (* PFN_XLookupString)(XKeyEvent*,char*,int,KeySym*,XComposeStatus*); +typedef int (* PFN_XMapRaised)(Display*,Window); +typedef int (* PFN_XMapWindow)(Display*,Window); +typedef int (* PFN_XMoveResizeWindow)(Display*,Window,int,int,unsigned int,unsigned int); +typedef int (* PFN_XMoveWindow)(Display*,Window,int,int); +typedef int (* PFN_XNextEvent)(Display*,XEvent*); +typedef Display* (* PFN_XOpenDisplay)(const char*); +typedef XIM (* PFN_XOpenIM)(Display*,XrmDatabase*,char*,char*); +typedef int (* PFN_XPeekEvent)(Display*,XEvent*); +typedef int (* PFN_XPending)(Display*); +typedef Bool (* PFN_XQueryExtension)(Display*,const char*,int*,int*,int*); +typedef Bool (* PFN_XQueryPointer)(Display*,Window,Window*,Window*,int*,int*,int*,int*,unsigned int*); +typedef int (* PFN_XRaiseWindow)(Display*,Window); +typedef Bool (* PFN_XRegisterIMInstantiateCallback)(Display*,void*,char*,char*,XIDProc,XPointer); +typedef int (* PFN_XResizeWindow)(Display*,Window,unsigned int,unsigned int); +typedef char* (* PFN_XResourceManagerString)(Display*); +typedef int (* PFN_XSaveContext)(Display*,XID,XContext,const char*); +typedef int (* PFN_XSelectInput)(Display*,Window,long); +typedef Status (* PFN_XSendEvent)(Display*,Window,Bool,long,XEvent*); +typedef int (* PFN_XSetClassHint)(Display*,Window,XClassHint*); +typedef XErrorHandler (* PFN_XSetErrorHandler)(XErrorHandler); +typedef void (* PFN_XSetICFocus)(XIC); +typedef char* (* PFN_XSetIMValues)(XIM,...); +typedef int (* PFN_XSetInputFocus)(Display*,Window,int,Time); +typedef char* (* PFN_XSetLocaleModifiers)(const char*); +typedef int (* PFN_XSetScreenSaver)(Display*,int,int,int,int); +typedef int (* PFN_XSetSelectionOwner)(Display*,Atom,Window,Time); +typedef int (* PFN_XSetWMHints)(Display*,Window,XWMHints*); +typedef void (* PFN_XSetWMNormalHints)(Display*,Window,XSizeHints*); +typedef Status (* PFN_XSetWMProtocols)(Display*,Window,Atom*,int); +typedef Bool (* PFN_XSupportsLocale)(void); +typedef int (* PFN_XSync)(Display*,Bool); +typedef Bool (* PFN_XTranslateCoordinates)(Display*,Window,Window,int,int,int*,int*,Window*); +typedef int (* PFN_XUndefineCursor)(Display*,Window); +typedef int (* PFN_XUngrabPointer)(Display*,Time); +typedef int (* PFN_XUnmapWindow)(Display*,Window); +typedef void (* PFN_XUnsetICFocus)(XIC); +typedef VisualID (* PFN_XVisualIDFromVisual)(Visual*); +typedef int (* PFN_XWarpPointer)(Display*,Window,Window,int,int,unsigned int,unsigned int,int,int); +typedef void (* PFN_XkbFreeKeyboard)(XkbDescPtr,unsigned int,Bool); +typedef void (* PFN_XkbFreeNames)(XkbDescPtr,unsigned int,Bool); +typedef XkbDescPtr (* PFN_XkbGetMap)(Display*,unsigned int,unsigned int); +typedef Status (* PFN_XkbGetNames)(Display*,unsigned int,XkbDescPtr); +typedef Status (* PFN_XkbGetState)(Display*,unsigned int,XkbStatePtr); +typedef KeySym (* PFN_XkbKeycodeToKeysym)(Display*,KeyCode,int,int); +typedef Bool (* PFN_XkbQueryExtension)(Display*,int*,int*,int*,int*,int*); +typedef Bool (* PFN_XkbSelectEventDetails)(Display*,unsigned int,unsigned int,unsigned long,unsigned long); +typedef Bool (* PFN_XkbSetDetectableAutoRepeat)(Display*,Bool,Bool*); +typedef void (* PFN_XrmDestroyDatabase)(XrmDatabase); +typedef Bool (* PFN_XrmGetResource)(XrmDatabase,const char*,const char*,char**,XrmValue*); +typedef XrmDatabase (* PFN_XrmGetStringDatabase)(const char*); +typedef void (* PFN_XrmInitialize)(void); +typedef XrmQuark (* PFN_XrmUniqueQuark)(void); +typedef Bool (* PFN_XUnregisterIMInstantiateCallback)(Display*,void*,char*,char*,XIDProc,XPointer); +typedef int (* PFN_Xutf8LookupString)(XIC,XKeyPressedEvent*,char*,int,KeySym*,Status*); +typedef void (* PFN_Xutf8SetWMProperties)(Display*,Window,const char*,const char*,char**,int,XSizeHints*,XWMHints*,XClassHint*); +#define XAllocClassHint _glfw.x11.xlib.AllocClassHint +#define XAllocSizeHints _glfw.x11.xlib.AllocSizeHints +#define XAllocWMHints _glfw.x11.xlib.AllocWMHints +#define XChangeProperty _glfw.x11.xlib.ChangeProperty +#define XChangeWindowAttributes _glfw.x11.xlib.ChangeWindowAttributes +#define XCheckIfEvent _glfw.x11.xlib.CheckIfEvent +#define XCheckTypedWindowEvent _glfw.x11.xlib.CheckTypedWindowEvent +#define XCloseDisplay _glfw.x11.xlib.CloseDisplay +#define XCloseIM _glfw.x11.xlib.CloseIM +#define XConvertSelection _glfw.x11.xlib.ConvertSelection +#define XCreateColormap _glfw.x11.xlib.CreateColormap +#define XCreateFontCursor _glfw.x11.xlib.CreateFontCursor +#define XCreateIC _glfw.x11.xlib.CreateIC +#define XCreateWindow _glfw.x11.xlib.CreateWindow +#define XDefineCursor _glfw.x11.xlib.DefineCursor +#define XDeleteContext _glfw.x11.xlib.DeleteContext +#define XDeleteProperty _glfw.x11.xlib.DeleteProperty +#define XDestroyIC _glfw.x11.xlib.DestroyIC +#define XDestroyWindow _glfw.x11.xlib.DestroyWindow +#define XDisplayKeycodes _glfw.x11.xlib.DisplayKeycodes +#define XEventsQueued _glfw.x11.xlib.EventsQueued +#define XFilterEvent _glfw.x11.xlib.FilterEvent +#define XFindContext _glfw.x11.xlib.FindContext +#define XFlush _glfw.x11.xlib.Flush +#define XFree _glfw.x11.xlib.Free +#define XFreeColormap _glfw.x11.xlib.FreeColormap +#define XFreeCursor _glfw.x11.xlib.FreeCursor +#define XFreeEventData _glfw.x11.xlib.FreeEventData +#define XGetErrorText _glfw.x11.xlib.GetErrorText +#define XGetEventData _glfw.x11.xlib.GetEventData +#define XGetICValues _glfw.x11.xlib.GetICValues +#define XGetIMValues _glfw.x11.xlib.GetIMValues +#define XGetInputFocus _glfw.x11.xlib.GetInputFocus +#define XGetKeyboardMapping _glfw.x11.xlib.GetKeyboardMapping +#define XGetScreenSaver _glfw.x11.xlib.GetScreenSaver +#define XGetSelectionOwner _glfw.x11.xlib.GetSelectionOwner +#define XGetVisualInfo _glfw.x11.xlib.GetVisualInfo +#define XGetWMNormalHints _glfw.x11.xlib.GetWMNormalHints +#define XGetWindowAttributes _glfw.x11.xlib.GetWindowAttributes +#define XGetWindowProperty _glfw.x11.xlib.GetWindowProperty +#define XGrabPointer _glfw.x11.xlib.GrabPointer +#define XIconifyWindow _glfw.x11.xlib.IconifyWindow +#define XInitThreads _glfw.x11.xlib.InitThreads +#define XInternAtom _glfw.x11.xlib.InternAtom +#define XLookupString _glfw.x11.xlib.LookupString +#define XMapRaised _glfw.x11.xlib.MapRaised +#define XMapWindow _glfw.x11.xlib.MapWindow +#define XMoveResizeWindow _glfw.x11.xlib.MoveResizeWindow +#define XMoveWindow _glfw.x11.xlib.MoveWindow +#define XNextEvent _glfw.x11.xlib.NextEvent +#define XOpenDisplay _glfw.x11.xlib.OpenDisplay +#define XOpenIM _glfw.x11.xlib.OpenIM +#define XPeekEvent _glfw.x11.xlib.PeekEvent +#define XPending _glfw.x11.xlib.Pending +#define XQueryExtension _glfw.x11.xlib.QueryExtension +#define XQueryPointer _glfw.x11.xlib.QueryPointer +#define XRaiseWindow _glfw.x11.xlib.RaiseWindow +#define XRegisterIMInstantiateCallback _glfw.x11.xlib.RegisterIMInstantiateCallback +#define XResizeWindow _glfw.x11.xlib.ResizeWindow +#define XResourceManagerString _glfw.x11.xlib.ResourceManagerString +#define XSaveContext _glfw.x11.xlib.SaveContext +#define XSelectInput _glfw.x11.xlib.SelectInput +#define XSendEvent _glfw.x11.xlib.SendEvent +#define XSetClassHint _glfw.x11.xlib.SetClassHint +#define XSetErrorHandler _glfw.x11.xlib.SetErrorHandler +#define XSetICFocus _glfw.x11.xlib.SetICFocus +#define XSetIMValues _glfw.x11.xlib.SetIMValues +#define XSetInputFocus _glfw.x11.xlib.SetInputFocus +#define XSetLocaleModifiers _glfw.x11.xlib.SetLocaleModifiers +#define XSetScreenSaver _glfw.x11.xlib.SetScreenSaver +#define XSetSelectionOwner _glfw.x11.xlib.SetSelectionOwner +#define XSetWMHints _glfw.x11.xlib.SetWMHints +#define XSetWMNormalHints _glfw.x11.xlib.SetWMNormalHints +#define XSetWMProtocols _glfw.x11.xlib.SetWMProtocols +#define XSupportsLocale _glfw.x11.xlib.SupportsLocale +#define XSync _glfw.x11.xlib.Sync +#define XTranslateCoordinates _glfw.x11.xlib.TranslateCoordinates +#define XUndefineCursor _glfw.x11.xlib.UndefineCursor +#define XUngrabPointer _glfw.x11.xlib.UngrabPointer +#define XUnmapWindow _glfw.x11.xlib.UnmapWindow +#define XUnsetICFocus _glfw.x11.xlib.UnsetICFocus +#define XVisualIDFromVisual _glfw.x11.xlib.VisualIDFromVisual +#define XWarpPointer _glfw.x11.xlib.WarpPointer +#define XkbFreeKeyboard _glfw.x11.xkb.FreeKeyboard +#define XkbFreeNames _glfw.x11.xkb.FreeNames +#define XkbGetMap _glfw.x11.xkb.GetMap +#define XkbGetNames _glfw.x11.xkb.GetNames +#define XkbGetState _glfw.x11.xkb.GetState +#define XkbKeycodeToKeysym _glfw.x11.xkb.KeycodeToKeysym +#define XkbQueryExtension _glfw.x11.xkb.QueryExtension +#define XkbSelectEventDetails _glfw.x11.xkb.SelectEventDetails +#define XkbSetDetectableAutoRepeat _glfw.x11.xkb.SetDetectableAutoRepeat +#define XrmDestroyDatabase _glfw.x11.xrm.DestroyDatabase +#define XrmGetResource _glfw.x11.xrm.GetResource +#define XrmGetStringDatabase _glfw.x11.xrm.GetStringDatabase +#define XrmInitialize _glfw.x11.xrm.Initialize +#define XrmUniqueQuark _glfw.x11.xrm.UniqueQuark +#define XUnregisterIMInstantiateCallback _glfw.x11.xlib.UnregisterIMInstantiateCallback +#define Xutf8LookupString _glfw.x11.xlib.utf8LookupString +#define Xutf8SetWMProperties _glfw.x11.xlib.utf8SetWMProperties + typedef XRRCrtcGamma* (* PFN_XRRAllocGamma)(int); typedef void (* PFN_XRRFreeCrtcInfo)(XRRCrtcInfo*); typedef void (* PFN_XRRFreeGamma)(XRRCrtcGamma*); @@ -186,6 +388,7 @@ typedef struct _GLFWwindowX11 { Colormap colormap; Window handle; + Window parent; XIC ic; GLFWbool overrideRedirect; @@ -300,6 +503,104 @@ typedef struct _GLFWlibraryX11 Atom ATOM_PAIR; Atom GLFW_SELECTION; + struct { + void* handle; + PFN_XAllocClassHint AllocClassHint; + PFN_XAllocSizeHints AllocSizeHints; + PFN_XAllocWMHints AllocWMHints; + PFN_XChangeProperty ChangeProperty; + PFN_XChangeWindowAttributes ChangeWindowAttributes; + PFN_XCheckIfEvent CheckIfEvent; + PFN_XCheckTypedWindowEvent CheckTypedWindowEvent; + PFN_XCloseDisplay CloseDisplay; + PFN_XCloseIM CloseIM; + PFN_XConvertSelection ConvertSelection; + PFN_XCreateColormap CreateColormap; + PFN_XCreateFontCursor CreateFontCursor; + PFN_XCreateIC CreateIC; + PFN_XCreateWindow CreateWindow; + PFN_XDefineCursor DefineCursor; + PFN_XDeleteContext DeleteContext; + PFN_XDeleteProperty DeleteProperty; + PFN_XDestroyIC DestroyIC; + PFN_XDestroyWindow DestroyWindow; + PFN_XDisplayKeycodes DisplayKeycodes; + PFN_XEventsQueued EventsQueued; + PFN_XFilterEvent FilterEvent; + PFN_XFindContext FindContext; + PFN_XFlush Flush; + PFN_XFree Free; + PFN_XFreeColormap FreeColormap; + PFN_XFreeCursor FreeCursor; + PFN_XFreeEventData FreeEventData; + PFN_XGetErrorText GetErrorText; + PFN_XGetEventData GetEventData; + PFN_XGetICValues GetICValues; + PFN_XGetIMValues GetIMValues; + PFN_XGetInputFocus GetInputFocus; + PFN_XGetKeyboardMapping GetKeyboardMapping; + PFN_XGetScreenSaver GetScreenSaver; + PFN_XGetSelectionOwner GetSelectionOwner; + PFN_XGetVisualInfo GetVisualInfo; + PFN_XGetWMNormalHints GetWMNormalHints; + PFN_XGetWindowAttributes GetWindowAttributes; + PFN_XGetWindowProperty GetWindowProperty; + PFN_XGrabPointer GrabPointer; + PFN_XIconifyWindow IconifyWindow; + PFN_XInitThreads InitThreads; + PFN_XInternAtom InternAtom; + PFN_XLookupString LookupString; + PFN_XMapRaised MapRaised; + PFN_XMapWindow MapWindow; + PFN_XMoveResizeWindow MoveResizeWindow; + PFN_XMoveWindow MoveWindow; + PFN_XNextEvent NextEvent; + PFN_XOpenDisplay OpenDisplay; + PFN_XOpenIM OpenIM; + PFN_XPeekEvent PeekEvent; + PFN_XPending Pending; + PFN_XQueryExtension QueryExtension; + PFN_XQueryPointer QueryPointer; + PFN_XRaiseWindow RaiseWindow; + PFN_XRegisterIMInstantiateCallback RegisterIMInstantiateCallback; + PFN_XResizeWindow ResizeWindow; + PFN_XResourceManagerString ResourceManagerString; + PFN_XSaveContext SaveContext; + PFN_XSelectInput SelectInput; + PFN_XSendEvent SendEvent; + PFN_XSetClassHint SetClassHint; + PFN_XSetErrorHandler SetErrorHandler; + PFN_XSetICFocus SetICFocus; + PFN_XSetIMValues SetIMValues; + PFN_XSetInputFocus SetInputFocus; + PFN_XSetLocaleModifiers SetLocaleModifiers; + PFN_XSetScreenSaver SetScreenSaver; + PFN_XSetSelectionOwner SetSelectionOwner; + PFN_XSetWMHints SetWMHints; + PFN_XSetWMNormalHints SetWMNormalHints; + PFN_XSetWMProtocols SetWMProtocols; + PFN_XSupportsLocale SupportsLocale; + PFN_XSync Sync; + PFN_XTranslateCoordinates TranslateCoordinates; + PFN_XUndefineCursor UndefineCursor; + PFN_XUngrabPointer UngrabPointer; + PFN_XUnmapWindow UnmapWindow; + PFN_XUnsetICFocus UnsetICFocus; + PFN_XVisualIDFromVisual VisualIDFromVisual; + PFN_XWarpPointer WarpPointer; + PFN_XUnregisterIMInstantiateCallback UnregisterIMInstantiateCallback; + PFN_Xutf8LookupString utf8LookupString; + PFN_Xutf8SetWMProperties utf8SetWMProperties; + } xlib; + + struct { + PFN_XrmDestroyDatabase DestroyDatabase; + PFN_XrmGetResource GetResource; + PFN_XrmGetStringDatabase GetStringDatabase; + PFN_XrmInitialize Initialize; + PFN_XrmUniqueQuark UniqueQuark; + } xrm; + struct { GLFWbool available; void* handle; @@ -337,6 +638,15 @@ typedef struct _GLFWlibraryX11 int major; int minor; unsigned int group; + PFN_XkbFreeKeyboard FreeKeyboard; + PFN_XkbFreeNames FreeNames; + PFN_XkbGetMap GetMap; + PFN_XkbGetNames GetNames; + PFN_XkbGetState GetState; + PFN_XkbKeycodeToKeysym KeycodeToKeysym; + PFN_XkbQueryExtension QueryExtension; + PFN_XkbSelectEventDetails SelectEventDetails; + PFN_XkbSetDetectableAutoRepeat SetDetectableAutoRepeat; } xkb; struct { @@ -455,4 +765,5 @@ void _glfwReleaseErrorHandlerX11(void); void _glfwInputErrorX11(int error, const char* message); void _glfwPushSelectionToManagerX11(void); +void _glfwCreateInputContextX11(_GLFWwindow* window); diff --git a/src/x11_window.c b/src/x11_window.c index 9f0312ce..d6e6bf37 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -590,6 +590,14 @@ static void enableCursor(_GLFWwindow* window) updateCursorImage(window); } +// Clear its handle when the input context has been destroyed +// +static void inputContextDestroyCallback(XIC ic, XPointer clientData, XPointer callData) +{ + _GLFWwindow* window = (_GLFWwindow*) clientData; + window->x11.ic = NULL; +} + // Create the X11 window (and its colormap) // static GLFWbool createNativeWindow(_GLFWwindow* window, @@ -613,46 +621,41 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, window->x11.transparent = _glfwIsVisualTransparentX11(visual); - // Create the actual window + XSetWindowAttributes wa = { 0 }; + wa.colormap = window->x11.colormap; + wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | + PointerMotionMask | ButtonPressMask | ButtonReleaseMask | + ExposureMask | FocusChangeMask | VisibilityChangeMask | + EnterWindowMask | LeaveWindowMask | PropertyChangeMask; + + _glfwGrabErrorHandlerX11(); + + window->x11.parent = _glfw.x11.root; + window->x11.handle = XCreateWindow(_glfw.x11.display, + _glfw.x11.root, + 0, 0, // Position + width, height, + 0, // Border width + depth, // Color depth + InputOutput, + visual, + CWBorderPixel | CWColormap | CWEventMask, + &wa); + + _glfwReleaseErrorHandlerX11(); + + if (!window->x11.handle) { - XSetWindowAttributes wa; - const unsigned long wamask = CWBorderPixel | CWColormap | CWEventMask; - - wa.colormap = window->x11.colormap; - wa.border_pixel = 0; - wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | - PointerMotionMask | ButtonPressMask | ButtonReleaseMask | - ExposureMask | FocusChangeMask | VisibilityChangeMask | - EnterWindowMask | LeaveWindowMask | PropertyChangeMask; - - _glfwGrabErrorHandlerX11(); - - window->x11.handle = XCreateWindow(_glfw.x11.display, - _glfw.x11.root, - 0, 0, - width, height, - 0, // Border width - depth, // Color depth - InputOutput, - visual, - wamask, - &wa); - - _glfwReleaseErrorHandlerX11(); - - if (!window->x11.handle) - { - _glfwInputErrorX11(GLFW_PLATFORM_ERROR, - "X11: Failed to create window"); - return GLFW_FALSE; - } - - XSaveContext(_glfw.x11.display, - window->x11.handle, - _glfw.x11.context, - (XPointer) window); + _glfwInputErrorX11(GLFW_PLATFORM_ERROR, + "X11: Failed to create window"); + return GLFW_FALSE; } + XSaveContext(_glfw.x11.display, + window->x11.handle, + _glfw.x11.context, + (XPointer) window); + if (!wndconfig->decorated) _glfwPlatformSetWindowDecorated(window, GLFW_FALSE); @@ -682,7 +685,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, { XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_STATE, XA_ATOM, 32, - PropModeReplace, (unsigned char*) &states, count); + PropModeReplace, (unsigned char*) states, count); } } @@ -773,20 +776,10 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, PropModeReplace, (unsigned char*) &version, 1); } - _glfwPlatformSetWindowTitle(window, wndconfig->title); - if (_glfw.x11.im) - { - window->x11.ic = XCreateIC(_glfw.x11.im, - XNInputStyle, - XIMPreeditNothing | XIMStatusNothing, - XNClientWindow, - window->x11.handle, - XNFocusWindow, - window->x11.handle, - NULL); - } + _glfwCreateInputContextX11(window); + _glfwPlatformSetWindowTitle(window, wndconfig->title); _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos); _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height); @@ -1171,8 +1164,7 @@ static void processEvent(XEvent *event) if (event->type == KeyPress || event->type == KeyRelease) keycode = event->xkey.keycode; - if (_glfw.x11.im) - filtered = XFilterEvent(event, None); + filtered = XFilterEvent(event, None); if (_glfw.x11.randr.available) { @@ -1257,6 +1249,12 @@ static void processEvent(XEvent *event) switch (event->type) { + case ReparentNotify: + { + window->x11.parent = event->xreparent.parent; + return; + } + case KeyPress: { const int key = translateKey(keycode); @@ -1541,18 +1539,34 @@ static void processEvent(XEvent *event) window->x11.height = event->xconfigure.height; } - if (event->xconfigure.x != window->x11.xpos || - event->xconfigure.y != window->x11.ypos) - { - if (window->x11.overrideRedirect || event->xany.send_event) - { - _glfwInputWindowPos(window, - event->xconfigure.x, - event->xconfigure.y); + int xpos = event->xconfigure.x; + int ypos = event->xconfigure.y; - window->x11.xpos = event->xconfigure.x; - window->x11.ypos = event->xconfigure.y; - } + // NOTE: ConfigureNotify events from the server are in local + // coordinates, so if we are reparented we need to translate + // the position into root (screen) coordinates + if (!event->xany.send_event && window->x11.parent != _glfw.x11.root) + { + _glfwGrabErrorHandlerX11(); + + Window dummy; + XTranslateCoordinates(_glfw.x11.display, + window->x11.parent, + _glfw.x11.root, + xpos, ypos, + &xpos, &ypos, + &dummy); + + _glfwReleaseErrorHandlerX11(); + if (_glfw.x11.errorCode == BadWindow) + return; + } + + if (xpos != window->x11.xpos || ypos != window->x11.ypos) + { + _glfwInputWindowPos(window, xpos, ypos); + window->x11.xpos = xpos; + window->x11.ypos = ypos; } return; @@ -1943,6 +1957,38 @@ void _glfwPushSelectionToManagerX11(void) } } +void _glfwCreateInputContextX11(_GLFWwindow* window) +{ + XIMCallback callback; + callback.callback = (XIMProc) inputContextDestroyCallback; + callback.client_data = (XPointer) window; + + window->x11.ic = XCreateIC(_glfw.x11.im, + XNInputStyle, + XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, + window->x11.handle, + XNFocusWindow, + window->x11.handle, + XNDestroyCallback, + &callback, + NULL); + + if (window->x11.ic) + { + XWindowAttributes attribs; + XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs); + + unsigned long filter = 0; + if (XGetICValues(window->x11.ic, XNFilterEvents, &filter, NULL) == NULL) + { + XSelectInput(_glfw.x11.display, + window->x11.handle, + attribs.your_event_mask | filter); + } + } +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// @@ -2340,18 +2386,67 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window) void _glfwPlatformMaximizeWindow(_GLFWwindow* window) { - if (_glfw.x11.NET_WM_STATE && - _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && - _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) + if (!_glfw.x11.NET_WM_STATE || + !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || + !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) + { + return; + } + + if (_glfwPlatformWindowVisible(window)) { sendEventToWM(window, - _glfw.x11.NET_WM_STATE, - _NET_WM_STATE_ADD, - _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, - _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ, - 1, 0); - XFlush(_glfw.x11.display); + _glfw.x11.NET_WM_STATE, + _NET_WM_STATE_ADD, + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ, + 1, 0); } + else + { + Atom* states = NULL; + unsigned long count = + _glfwGetWindowPropertyX11(window->x11.handle, + _glfw.x11.NET_WM_STATE, + XA_ATOM, + (unsigned char**) &states); + + // NOTE: We don't check for failure as this property may not exist yet + // and that's fine (and we'll create it implicitly with append) + + Atom missing[2] = + { + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ + }; + unsigned long missingCount = 2; + + for (unsigned long i = 0; i < count; i++) + { + for (unsigned long j = 0; j < missingCount; j++) + { + if (states[i] == missing[j]) + { + missing[j] = missing[missingCount - 1]; + missingCount--; + } + } + } + + if (states) + XFree(states); + + if (!missingCount) + return; + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_STATE, XA_ATOM, 32, + PropModeAppend, + (unsigned char*) missing, + missingCount); + } + + XFlush(_glfw.x11.display); } void _glfwPlatformShowWindow(_GLFWwindow* window) @@ -2371,6 +2466,9 @@ void _glfwPlatformHideWindow(_GLFWwindow* window) void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) { + if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION) + return; + sendEventToWM(window, _glfw.x11.NET_WM_STATE, _NET_WM_STATE_ADD, @@ -2382,7 +2480,7 @@ void _glfwPlatformFocusWindow(_GLFWwindow* window) { if (_glfw.x11.NET_ACTIVE_WINDOW) sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0); - else + else if (_glfwPlatformWindowVisible(window)) { XRaiseWindow(_glfw.x11.display, window->x11.handle); XSetInputFocus(_glfw.x11.display, window->x11.handle, @@ -2567,7 +2665,7 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) if (_glfwPlatformWindowVisible(window)) { - const Atom action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; + const long action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; sendEventToWM(window, _glfw.x11.NET_WM_STATE, action, @@ -2576,15 +2674,16 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) } else { - Atom* states; + Atom* states = NULL; unsigned long i, count; count = _glfwGetWindowPropertyX11(window->x11.handle, _glfw.x11.NET_WM_STATE, XA_ATOM, (unsigned char**) &states); - if (!states) - return; + + // NOTE: We don't check for failure as this property may not exist yet + // and that's fine (and we'll create it implicitly with append) if (enabled) { @@ -2594,32 +2693,36 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) break; } - if (i == count) - { - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_STATE, XA_ATOM, 32, - PropModeAppend, - (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE, - 1); - } + if (i < count) + return; + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_STATE, XA_ATOM, 32, + PropModeAppend, + (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE, + 1); } - else + else if (states) { for (i = 0; i < count; i++) { if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE) - { - states[i] = states[count - 1]; - count--; - } + break; } + if (i == count) + return; + + states[i] = states[count - 1]; + count--; + XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_STATE, XA_ATOM, 32, - PropModeReplace, (unsigned char*) &states, count); + PropModeReplace, (unsigned char*) states, count); } - XFree(states); + if (states) + XFree(states); } XFlush(_glfw.x11.display); @@ -2684,7 +2787,7 @@ void _glfwPlatformPollEvents(void) #endif XPending(_glfw.x11.display); - while (XQLength(_glfw.x11.display)) + while (QLength(_glfw.x11.display)) { XEvent event; XNextEvent(_glfw.x11.display, &event); @@ -2787,6 +2890,13 @@ const char* _glfwPlatformGetScancodeName(int scancode) if (!_glfw.x11.xkb.available) return NULL; + if (scancode < 0 || scancode > 0xff || + _glfw.x11.keycodes[scancode] == GLFW_KEY_UNKNOWN) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode"); + return NULL; + } + const int key = _glfw.x11.keycodes[scancode]; const KeySym keysym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, _glfw.x11.xkb.group, 0); @@ -2915,8 +3025,9 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) void _glfwPlatformSetClipboardString(const char* string) { + char* copy = _glfw_strdup(string); free(_glfw.x11.clipboardString); - _glfw.x11.clipboardString = _glfw_strdup(string); + _glfw.x11.clipboardString = copy; XSetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD, diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c471d8b9..e36ed56c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,7 +1,7 @@ link_libraries(glfw) -include_directories(${glfw_INCLUDE_DIRS} "${GLFW_SOURCE_DIR}/deps") +include_directories("${GLFW_SOURCE_DIR}/deps") if (MATH_LIBRARY) link_libraries("${MATH_LIBRARY}") @@ -20,17 +20,6 @@ set(GETOPT "${GLFW_SOURCE_DIR}/deps/getopt.h" set(TINYCTHREAD "${GLFW_SOURCE_DIR}/deps/tinycthread.h" "${GLFW_SOURCE_DIR}/deps/tinycthread.c") -if (${CMAKE_VERSION} VERSION_EQUAL "3.1.0" OR - ${CMAKE_VERSION} VERSION_GREATER "3.1.0") - set(CMAKE_C_STANDARD 99) -else() - # Remove this fallback when removing support for CMake version less than 3.1 - add_compile_options("$<$:-std=c99>" - "$<$:-std=c99>" - "$<$:-std=c99>") - -endif() - add_executable(clipboard clipboard.c ${GETOPT} ${GLAD_GL}) add_executable(events events.c ${GETOPT} ${GLAD_GL}) add_executable(msaa msaa.c ${GETOPT} ${GLAD_GL}) @@ -50,27 +39,28 @@ add_executable(tearing WIN32 MACOSX_BUNDLE tearing.c ${GLAD_GL}) add_executable(threads WIN32 MACOSX_BUNDLE threads.c ${TINYCTHREAD} ${GLAD_GL}) add_executable(timeout WIN32 MACOSX_BUNDLE timeout.c ${GLAD_GL}) add_executable(title WIN32 MACOSX_BUNDLE title.c ${GLAD_GL}) -add_executable(triangle-vulkan WIN32 triangle-vulkan.c ${ICON} ${GLAD_VULKAN}) -add_executable(windows WIN32 MACOSX_BUNDLE windows.c ${GETOPT} ${GLAD_GL}) +add_executable(triangle-vulkan WIN32 triangle-vulkan.c ${GLAD_VULKAN}) +add_executable(windows WIN32 MACOSX_BUNDLE windows.c ${GLAD_GL}) -target_link_libraries(empty "${CMAKE_THREAD_LIBS_INIT}") -target_link_libraries(threads "${CMAKE_THREAD_LIBS_INIT}") +target_link_libraries(empty Threads::Threads) +target_link_libraries(threads Threads::Threads) if (RT_LIBRARY) target_link_libraries(empty "${RT_LIBRARY}") target_link_libraries(threads "${RT_LIBRARY}") endif() -set(WINDOWS_BINARIES empty gamma icon inputlag joysticks opacity tearing - threads timeout title triangle-vulkan windows) +set(GUI_ONLY_BINARIES empty gamma icon inputlag joysticks opacity tearing + threads timeout title triangle-vulkan windows) set(CONSOLE_BINARIES clipboard events msaa glfwinfo iconify monitors reopen cursor) -set_target_properties(${WINDOWS_BINARIES} ${CONSOLE_BINARIES} PROPERTIES +set_target_properties(${GUI_ONLY_BINARIES} ${CONSOLE_BINARIES} PROPERTIES + C_STANDARD 99 FOLDER "GLFW3/Tests") if (MSVC) # Tell MSVC to use main instead of WinMain for Windows subsystem executables - set_target_properties(${WINDOWS_BINARIES} ${CONSOLE_BINARIES} PROPERTIES + set_target_properties(${GUI_ONLY_BINARIES} PROPERTIES LINK_FLAGS "/ENTRY:mainCRTStartup") endif() @@ -84,12 +74,11 @@ if (APPLE) set_target_properties(threads PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Threads") set_target_properties(timeout PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Timeout") set_target_properties(title PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Title") - set_target_properties(triangle-vulkan PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Vulkan Triangle") set_target_properties(windows PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Windows") - set_target_properties(${WINDOWS_BINARIES} ${CONSOLE_BINARIES} PROPERTIES + set_target_properties(${GUI_ONLY_BINARIES} PROPERTIES MACOSX_BUNDLE_SHORT_VERSION_STRING ${GLFW_VERSION} MACOSX_BUNDLE_LONG_VERSION_STRING ${GLFW_VERSION} - MACOSX_BUNDLE_INFO_PLIST "${GLFW_SOURCE_DIR}/CMake/MacOSXBundleInfo.plist.in") + MACOSX_BUNDLE_INFO_PLIST "${GLFW_SOURCE_DIR}/CMake/Info.plist.in") endif() diff --git a/tests/monitors.c b/tests/monitors.c index 2b75d7b1..c66950bf 100644 --- a/tests/monitors.c +++ b/tests/monitors.c @@ -241,6 +241,8 @@ int main(int argc, char** argv) glfwSetErrorCallback(error_callback); + glfwInitHint(GLFW_COCOA_MENUBAR, GLFW_FALSE); + if (!glfwInit()) exit(EXIT_FAILURE); diff --git a/tests/windows.c b/tests/windows.c index 6669856f..881338fb 100644 --- a/tests/windows.c +++ b/tests/windows.c @@ -34,8 +34,7 @@ #include #include -#include "getopt.h" - +static GLFWwindow* windows[4]; static const char* titles[] = { "Red", @@ -55,20 +54,26 @@ static const struct { 0.98f, 0.74f, 0.04f } }; -static void usage(void) -{ - printf("Usage: windows [-h] [-b] [-f] \n"); - printf("Options:\n"); - printf(" -b create decorated windows\n"); - printf(" -f set focus on show off for all but first window\n"); - printf(" -h show this help\n"); -} - static void error_callback(int error, const char* description) { fprintf(stderr, "Error: %s\n", description); } +static void arrange_windows(void) +{ + int xbase, ybase; + glfwGetWindowPos(windows[0], &xbase, &ybase); + + for (int i = 0; i < 4; i++) + { + int left, top, right, bottom; + glfwGetWindowFrameSize(windows[i], &left, &top, &right, &bottom); + glfwSetWindowPos(windows[i], + xbase + (i & 1) * (200 + left + right), + ybase + (i >> 1) * (200 + top + bottom)); + } +} + static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { if (action != GLFW_PRESS) @@ -87,49 +92,34 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action, case GLFW_KEY_ESCAPE: glfwSetWindowShouldClose(window, GLFW_TRUE); break; + + case GLFW_KEY_D: + { + for (int i = 0; i < 4; i++) + { + const int decorated = glfwGetWindowAttrib(windows[i], GLFW_DECORATED); + glfwSetWindowAttrib(windows[i], GLFW_DECORATED, !decorated); + } + + arrange_windows(); + break; + } } } int main(int argc, char** argv) { - int i, ch; - int decorated = GLFW_FALSE; - int focusOnShow = GLFW_TRUE; - int running = GLFW_TRUE; - GLFWwindow* windows[4]; - - while ((ch = getopt(argc, argv, "bfh")) != -1) - { - switch (ch) - { - case 'b': - decorated = GLFW_TRUE; - break; - case 'f': - focusOnShow = GLFW_FALSE; - break; - case 'h': - usage(); - exit(EXIT_SUCCESS); - default: - usage(); - exit(EXIT_FAILURE); - } - } - glfwSetErrorCallback(error_callback); if (!glfwInit()) exit(EXIT_FAILURE); - glfwWindowHint(GLFW_DECORATED, decorated); glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); - for (i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) { - int left, top, right, bottom; - if (i) - glfwWindowHint(GLFW_FOCUS_ON_SHOW, focusOnShow); + if (i > 0) + glfwWindowHint(GLFW_FOCUS_ON_SHOW, GLFW_FALSE); windows[i] = glfwCreateWindow(200, 200, titles[i], NULL, NULL); if (!windows[i]) @@ -143,32 +133,29 @@ int main(int argc, char** argv) glfwMakeContextCurrent(windows[i]); gladLoadGL(glfwGetProcAddress); glClearColor(colors[i].r, colors[i].g, colors[i].b, 1.f); - - glfwGetWindowFrameSize(windows[i], &left, &top, &right, &bottom); - glfwSetWindowPos(windows[i], - 100 + (i & 1) * (200 + left + right), - 100 + (i >> 1) * (200 + top + bottom)); } - for (i = 0; i < 4; i++) + arrange_windows(); + + for (int i = 0; i < 4; i++) glfwShowWindow(windows[i]); - while (running) + for (;;) { - for (i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) { glfwMakeContextCurrent(windows[i]); glClear(GL_COLOR_BUFFER_BIT); glfwSwapBuffers(windows[i]); if (glfwWindowShouldClose(windows[i])) - running = GLFW_FALSE; + { + glfwTerminate(); + exit(EXIT_SUCCESS); + } } glfwWaitEvents(); } - - glfwTerminate(); - exit(EXIT_SUCCESS); }