diff --git a/.appveyor.yml b/.appveyor.yml index 74a2f0aab..ae658cc37 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,22 +1,22 @@ -branches: - only: - - ci - - master -skip_tags: true -environment: - matrix: - - BUILD_SHARED_LIBS: ON - - BUILD_SHARED_LIBS: OFF -matrix: - fast_finish: true -build_script: - - mkdir build - - cd build - - cmake -DBUILD_SHARED_LIBS=%BUILD_SHARED_LIBS% .. - - cmake --build . -notifications: - - provider: Email - to: - - ci@glfw.org - - on_build_failure: true - - on_build_success: false +branches: + only: + - ci + - master +skip_tags: true +environment: + matrix: + - BUILD_SHARED_LIBS: ON + - BUILD_SHARED_LIBS: OFF +matrix: + fast_finish: true +build_script: + - mkdir build + - cd build + - cmake -DBUILD_SHARED_LIBS=%BUILD_SHARED_LIBS% .. + - cmake --build . +notifications: + - provider: Email + to: + - ci@glfw.org + - on_build_failure: true + - on_build_success: false diff --git a/CONTRIBUTING.md b/.github/CONTRIBUTING.md similarity index 97% rename from CONTRIBUTING.md rename to .github/CONTRIBUTING.md index fbe09669b..53c3a6c3c 100644 --- a/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,106 +1,106 @@ -# Contribution Guide - -This file is a work in progress and you can report errors or submit patches for -it the same as any other file. - - -## Reporting a bug - -If GLFW is behaving unexpectedly, make sure you have set an error callback. -GLFW will often tell you the cause of an issue via this callback. - -If GLFW is crashing or triggering asserts, make sure that all your object -handles and other pointers are valid. - -Always include the __operating system name and version__ (i.e. `Windows -7 64-bit` or `Ubuntu 15.10`). If you are using an official release of GLFW, -include the __GLFW release version__ (i.e. `3.1.2`), otherwise include the -__GLFW commit ID__ (i.e. `3795d78b14ef06008889cc422a1fb8d642597751`) from Git. -If possible, please also include the __GLFW version string__ (`3.2.0 X11 EGL -clock_gettime /dev/js XI Xf86vm`), as described -[here](http://www.glfw.org/docs/latest/intro.html#intro_version_string). - - -### Reporting a compile or link bug - -__Note:__ GLFW needs many system APIs to do its job. See the [Building -applications](http://www.glfw.org/docs/latest/build.html) guide for more -information. - -In addition to the information above, always include the complete build log from -your compiler and linker. Issue posts are editable so it can always be -shortened later. - - -### Reporting a context creation bug - -__Note:__ Windows ships with graphics drivers that do not support OpenGL. If -GLFW says that your machine lacks support for OpenGL, it very likely does. -Install drivers from the computer manufacturer or graphics card manufacturer -([Nvidia](http://www.geforce.com/drivers), - [AMD](http://support.amd.com/en-us/download), - [Intel](https://www-ssl.intel.com/content/www/us/en/support/detect.html)) to -fix this. - -__Note:__ AMD only supports OpenGL ES on Windows via EGL. EGL support is not -enabled in GLFW by default. You need to [enable EGL when -compiling](http://www.glfw.org/docs/latest/compile.html) GLFW to use this. - -The `glfwinfo` tool is included in the GLFW source tree as `tests/glfwinfo.c` -and is built along with the library. It lets you request any kind of context -and framebuffer format supported by the GLFW API without having to recompile. -If context creation fails in your application, please verify that it also fails -with this tool before reporting it as a bug. - -In addition to the information above (OS and GLFW version), always include the -__GPU model and driver version__ (i.e. `GeForce GTX660 with 352.79`) when -reporting this kind of bug. - - -### Reporting a monitor or video mode bug - -__Note:__ On headless systems on some platforms, no monitors are reported. This -causes glfwGetPrimaryMonitor to return `NULL`, which not all applications are -prepared for. - -__Note:__ Some third-party tools report more video modes than those approved of -by the OS. For safety and compatbility, GLFW only reports video modes the OS -wants programs to use. This is not a bug. - -The `monitors` tool is included in the GLFW source tree as `tests/monitors.c` -and is built along with the library. lists all information about connected -monitors made available by GLFW. - -In addition to the information above (OS and GLFW version), please also include -the output of the `monitors` tool when reporting this kind of bug. If it -doesn't work at all, please mention this. - - -### Reporting a window event bug - -__Note:__ While GLFW tries to provide the exact same behavior between platforms, -the exact ordering of related window events will sometimes differ. - -The `events` tool is included in the GLFW source tree as `tests/events.c` and is -built along with the library. It prints all information provided to every -callback supported by GLFW as events occur. Each event is listed with the time -and a unique number to make discussions about event logs easier. The tool has -command-line options for creating multiple windows and full screen windows. - - -### Reporting a documentation bug - -If you found the error in the generated documentation then it's fine to just -link to that webpage. You don't need to figure out which documentation source -file the text comes from. - - -## Contributing a bug fix - -There should be text here, but there isn't. - - -## Contributing a feature - -This is not (yet) the text you are looking for. - +# Contribution Guide + +This file is a work in progress and you can report errors or submit patches for +it the same as any other file. + + +## Reporting a bug + +If GLFW is behaving unexpectedly, make sure you have set an error callback. +GLFW will often tell you the cause of an issue via this callback. + +If GLFW is crashing or triggering asserts, make sure that all your object +handles and other pointers are valid. + +Always include the __operating system name and version__ (i.e. `Windows +7 64-bit` or `Ubuntu 15.10`). If you are using an official release of GLFW, +include the __GLFW release version__ (i.e. `3.1.2`), otherwise include the +__GLFW commit ID__ (i.e. `3795d78b14ef06008889cc422a1fb8d642597751`) from Git. +If possible, please also include the __GLFW version string__ (`3.2.0 X11 EGL +clock_gettime /dev/js XI Xf86vm`), as described +[here](http://www.glfw.org/docs/latest/intro.html#intro_version_string). + + +### Reporting a compile or link bug + +__Note:__ GLFW needs many system APIs to do its job. See the [Building +applications](http://www.glfw.org/docs/latest/build.html) guide for more +information. + +In addition to the information above, always include the complete build log from +your compiler and linker. Issue posts are editable so it can always be +shortened later. + + +### Reporting a context creation bug + +__Note:__ Windows ships with graphics drivers that do not support OpenGL. If +GLFW says that your machine lacks support for OpenGL, it very likely does. +Install drivers from the computer manufacturer or graphics card manufacturer +([Nvidia](http://www.geforce.com/drivers), + [AMD](http://support.amd.com/en-us/download), + [Intel](https://www-ssl.intel.com/content/www/us/en/support/detect.html)) to +fix this. + +__Note:__ AMD only supports OpenGL ES on Windows via EGL. EGL support is not +enabled in GLFW by default. You need to [enable EGL when +compiling](http://www.glfw.org/docs/latest/compile.html) GLFW to use this. + +The `glfwinfo` tool is included in the GLFW source tree as `tests/glfwinfo.c` +and is built along with the library. It lets you request any kind of context +and framebuffer format supported by the GLFW API without having to recompile. +If context creation fails in your application, please verify that it also fails +with this tool before reporting it as a bug. + +In addition to the information above (OS and GLFW version), always include the +__GPU model and driver version__ (i.e. `GeForce GTX660 with 352.79`) when +reporting this kind of bug. + + +### Reporting a monitor or video mode bug + +__Note:__ On headless systems on some platforms, no monitors are reported. This +causes glfwGetPrimaryMonitor to return `NULL`, which not all applications are +prepared for. + +__Note:__ Some third-party tools report more video modes than those approved of +by the OS. For safety and compatbility, GLFW only reports video modes the OS +wants programs to use. This is not a bug. + +The `monitors` tool is included in the GLFW source tree as `tests/monitors.c` +and is built along with the library. lists all information about connected +monitors made available by GLFW. + +In addition to the information above (OS and GLFW version), please also include +the output of the `monitors` tool when reporting this kind of bug. If it +doesn't work at all, please mention this. + + +### Reporting a window event bug + +__Note:__ While GLFW tries to provide the exact same behavior between platforms, +the exact ordering of related window events will sometimes differ. + +The `events` tool is included in the GLFW source tree as `tests/events.c` and is +built along with the library. It prints all information provided to every +callback supported by GLFW as events occur. Each event is listed with the time +and a unique number to make discussions about event logs easier. The tool has +command-line options for creating multiple windows and full screen windows. + + +### Reporting a documentation bug + +If you found the error in the generated documentation then it's fine to just +link to that webpage. You don't need to figure out which documentation source +file the text comes from. + + +## Contributing a bug fix + +There should be text here, but there isn't. + + +## Contributing a feature + +This is not (yet) the text you are looking for. + diff --git a/.gitignore b/.gitignore index aacae734f..7a365a5da 100644 --- a/.gitignore +++ b/.gitignore @@ -13,11 +13,13 @@ Debug Release MinSizeRel RelWithDebInfo +*.xcodeproj # CMake files Makefile CMakeCache.txt CMakeFiles +CMakeScripts cmake_install.cmake cmake_uninstall.cmake @@ -30,6 +32,10 @@ src/glfw_config.h src/glfw3.pc src/glfw3Config.cmake src/glfw3ConfigVersion.cmake +src/wayland-pointer-constraints-unstable-v1-client-protocol.h +src/wayland-pointer-constraints-unstable-v1-protocol.c +src/wayland-relative-pointer-unstable-v1-client-protocol.h +src/wayland-relative-pointer-unstable-v1-protocol.c # Compiled binaries src/libglfw.so diff --git a/CMake/modules/FindEGL.cmake b/CMake/modules/FindEGL.cmake deleted file mode 100644 index 83bb9e36b..000000000 --- a/CMake/modules/FindEGL.cmake +++ /dev/null @@ -1,16 +0,0 @@ -# Find EGL -# -# EGL_INCLUDE_DIR -# EGL_LIBRARY -# EGL_FOUND - -find_path(EGL_INCLUDE_DIR NAMES EGL/egl.h PATHS /opt/vc/include) - -set(EGL_NAMES ${EGL_NAMES} egl EGL libEGL) -find_library(EGL_LIBRARY NAMES ${EGL_NAMES} PATHS /opt/vc/lib) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(EGL DEFAULT_MSG EGL_LIBRARY EGL_INCLUDE_DIR) - -mark_as_advanced(EGL_INCLUDE_DIR EGL_LIBRARY) - diff --git a/CMake/modules/FindGLESv1.cmake b/CMake/modules/FindGLESv1.cmake deleted file mode 100644 index 70d3eb9fc..000000000 --- a/CMake/modules/FindGLESv1.cmake +++ /dev/null @@ -1,16 +0,0 @@ -# Find GLESv1 -# -# GLESv1_INCLUDE_DIR -# GLESv1_LIBRARY -# GLESv1_FOUND - -find_path(GLESv1_INCLUDE_DIR NAMES GLES/gl.h PATHS /opt/vc/include) - -set(GLESv1_NAMES ${GLESv1_NAMES} GLESv1_CM libGLES_CM) -find_library(GLESv1_LIBRARY NAMES ${GLESv1_NAMES} PATHS /opt/vc/lib) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(GLESv1 DEFAULT_MSG GLESv1_LIBRARY GLESv1_INCLUDE_DIR) - -mark_as_advanced(GLESv1_INCLUDE_DIR GLESv1_LIBRARY) - diff --git a/CMake/modules/FindGLESv2.cmake b/CMake/modules/FindGLESv2.cmake deleted file mode 100644 index ff5ba954c..000000000 --- a/CMake/modules/FindGLESv2.cmake +++ /dev/null @@ -1,16 +0,0 @@ -# Find GLESv2 -# -# GLESv2_INCLUDE_DIR -# GLESv2_LIBRARY -# GLESv2_FOUND - -find_path(GLESv2_INCLUDE_DIR NAMES GLES2/gl2.h PATHS /opt/vc/include) - -set(GLESv2_NAMES ${GLESv2_NAMES} GLESv2 libGLESv2) -find_library(GLESv2_LIBRARY NAMES ${GLESv2_NAMES} PATHS /opt/vc/lib) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(GLESv2 DEFAULT_MSG GLESv2_LIBRARY GLESv2_INCLUDE_DIR) - -mark_as_advanced(GLESv2_INCLUDE_DIR GLESv2_LIBRARY) - diff --git a/CMake/modules/FindVulkan.cmake b/CMake/modules/FindVulkan.cmake index c7190bcbd..75cc25c79 100644 --- a/CMake/modules/FindVulkan.cmake +++ b/CMake/modules/FindVulkan.cmake @@ -6,9 +6,17 @@ if (WIN32) find_path(VULKAN_INCLUDE_DIR NAMES vulkan/vulkan.h HINTS + "$ENV{VULKAN_SDK}/Include" "$ENV{VK_SDK_PATH}/Include") - find_library(VULKAN_LIBRARY NAMES vulkan-1 HINTS - "$ENV{VK_SDK_PATH}/Bin") + if (CMAKE_CL_64) + find_library(VULKAN_LIBRARY NAMES vulkan-1 HINTS + "$ENV{VULKAN_SDK}/Bin" + "$ENV{VK_SDK_PATH}/Bin") + else() + find_library(VULKAN_LIBRARY NAMES vulkan-1 HINTS + "$ENV{VULKAN_SDK}/Bin32" + "$ENV{VK_SDK_PATH}/Bin32") + endif() else() find_path(VULKAN_INCLUDE_DIR NAMES vulkan/vulkan.h HINTS "$ENV{VULKAN_SDK}/include") diff --git a/CMake/modules/FindWayland.cmake b/CMake/modules/FindWayland.cmake deleted file mode 100644 index f93218b87..000000000 --- a/CMake/modules/FindWayland.cmake +++ /dev/null @@ -1,66 +0,0 @@ -# Try to find Wayland on a Unix system -# -# This will define: -# -# WAYLAND_FOUND - True if Wayland is found -# WAYLAND_LIBRARIES - Link these to use Wayland -# WAYLAND_INCLUDE_DIR - Include directory for Wayland -# WAYLAND_DEFINITIONS - Compiler flags for using Wayland -# -# In addition the following more fine grained variables will be defined: -# -# WAYLAND_CLIENT_FOUND WAYLAND_CLIENT_INCLUDE_DIR WAYLAND_CLIENT_LIBRARIES -# WAYLAND_SERVER_FOUND WAYLAND_SERVER_INCLUDE_DIR WAYLAND_SERVER_LIBRARIES -# WAYLAND_EGL_FOUND WAYLAND_EGL_INCLUDE_DIR WAYLAND_EGL_LIBRARIES -# -# Copyright (c) 2013 Martin Gräßlin -# -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. - -IF (NOT WIN32) - IF (WAYLAND_INCLUDE_DIR AND WAYLAND_LIBRARIES) - # In the cache already - SET(WAYLAND_FIND_QUIETLY TRUE) - ENDIF () - - # Use pkg-config to get the directories and then use these values - # in the FIND_PATH() and FIND_LIBRARY() calls - FIND_PACKAGE(PkgConfig) - PKG_CHECK_MODULES(PKG_WAYLAND QUIET wayland-client wayland-server wayland-egl wayland-cursor) - - SET(WAYLAND_DEFINITIONS ${PKG_WAYLAND_CFLAGS}) - - FIND_PATH(WAYLAND_CLIENT_INCLUDE_DIR NAMES wayland-client.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS}) - FIND_PATH(WAYLAND_SERVER_INCLUDE_DIR NAMES wayland-server.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS}) - FIND_PATH(WAYLAND_EGL_INCLUDE_DIR NAMES wayland-egl.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS}) - FIND_PATH(WAYLAND_CURSOR_INCLUDE_DIR NAMES wayland-cursor.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS}) - - FIND_LIBRARY(WAYLAND_CLIENT_LIBRARIES NAMES wayland-client HINTS ${PKG_WAYLAND_LIBRARY_DIRS}) - FIND_LIBRARY(WAYLAND_SERVER_LIBRARIES NAMES wayland-server HINTS ${PKG_WAYLAND_LIBRARY_DIRS}) - FIND_LIBRARY(WAYLAND_EGL_LIBRARIES NAMES wayland-egl HINTS ${PKG_WAYLAND_LIBRARY_DIRS}) - FIND_LIBRARY(WAYLAND_CURSOR_LIBRARIES NAMES wayland-cursor HINTS ${PKG_WAYLAND_LIBRARY_DIRS}) - - set(WAYLAND_INCLUDE_DIR ${WAYLAND_CLIENT_INCLUDE_DIR} ${WAYLAND_SERVER_INCLUDE_DIR} ${WAYLAND_EGL_INCLUDE_DIR} ${WAYLAND_CURSOR_INCLUDE_DIR}) - - set(WAYLAND_LIBRARIES ${WAYLAND_CLIENT_LIBRARIES} ${WAYLAND_SERVER_LIBRARIES} ${WAYLAND_EGL_LIBRARIES} ${WAYLAND_CURSOR_LIBRARIES}) - - list(REMOVE_DUPLICATES WAYLAND_INCLUDE_DIR) - - include(FindPackageHandleStandardArgs) - - FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_CLIENT DEFAULT_MSG WAYLAND_CLIENT_LIBRARIES WAYLAND_CLIENT_INCLUDE_DIR) - FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_SERVER DEFAULT_MSG WAYLAND_SERVER_LIBRARIES WAYLAND_SERVER_INCLUDE_DIR) - FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_EGL DEFAULT_MSG WAYLAND_EGL_LIBRARIES WAYLAND_EGL_INCLUDE_DIR) - FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_CURSOR DEFAULT_MSG WAYLAND_CURSOR_LIBRARIES WAYLAND_CURSOR_INCLUDE_DIR) - FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND DEFAULT_MSG WAYLAND_LIBRARIES WAYLAND_INCLUDE_DIR) - - MARK_AS_ADVANCED( - WAYLAND_INCLUDE_DIR WAYLAND_LIBRARIES - WAYLAND_CLIENT_INCLUDE_DIR WAYLAND_CLIENT_LIBRARIES - WAYLAND_SERVER_INCLUDE_DIR WAYLAND_SERVER_LIBRARIES - WAYLAND_EGL_INCLUDE_DIR WAYLAND_EGL_LIBRARIES - WAYLAND_CURSOR_INCLUDE_DIR WAYLAND_CURSOR_LIBRARIES - ) - -ENDIF () diff --git a/CMake/modules/FindWaylandProtocols.cmake b/CMake/modules/FindWaylandProtocols.cmake new file mode 100644 index 000000000..8eb83f27e --- /dev/null +++ b/CMake/modules/FindWaylandProtocols.cmake @@ -0,0 +1,26 @@ +find_package(PkgConfig) + +pkg_check_modules(WaylandProtocols QUIET wayland-protocols>=${WaylandProtocols_FIND_VERSION}) + +execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=pkgdatadir wayland-protocols + OUTPUT_VARIABLE WaylandProtocols_PKGDATADIR + RESULT_VARIABLE _pkgconfig_failed) +if (_pkgconfig_failed) + message(FATAL_ERROR "Missing wayland-protocols pkgdatadir") +endif() + +string(REGEX REPLACE "[\r\n]" "" WaylandProtocols_PKGDATADIR "${WaylandProtocols_PKGDATADIR}") + +find_package_handle_standard_args(WaylandProtocols + FOUND_VAR + WaylandProtocols_FOUND + REQUIRED_VARS + WaylandProtocols_PKGDATADIR + VERSION_VAR + WaylandProtocols_VERSION + HANDLE_COMPONENTS +) + +set(WAYLAND_PROTOCOLS_FOUND ${WaylandProtocols_FOUND}) +set(WAYLAND_PROTOCOLS_PKGDATADIR ${WaylandProtocols_PKGDATADIR}) +set(WAYLAND_PROTOCOLS_VERSION ${WaylandProtocols_VERSION}) diff --git a/CMakeLists.txt b/CMakeLists.txt index e6585a1c7..02040dd06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,5 @@ +set(CMAKE_LEGACY_CYGWIN_WIN32 OFF) + project(GLFW C) cmake_minimum_required(VERSION 2.8.12) @@ -33,13 +35,11 @@ if (APPLE) option(GLFW_USE_CHDIR "Make glfwInit chdir to Contents/Resources" ON) option(GLFW_USE_MENUBAR "Populate the menu bar on first window creation" ON) option(GLFW_USE_RETINA "Use the full resolution of Retina displays" ON) -else() - option(GLFW_USE_EGL "Use EGL for context creation" OFF) endif() if (UNIX AND NOT APPLE) - option(GLFW_USE_WAYLAND "Use Wayland for context creation (implies EGL as well)" OFF) - option(GLFW_USE_MIR "Use Mir for context creation (implies EGL as well)" OFF) + option(GLFW_USE_WAYLAND "Use Wayland for window creation" OFF) + option(GLFW_USE_MIR "Use Mir for window creation" OFF) endif() if (MSVC) @@ -57,12 +57,6 @@ else() set(GLFW_LIB_NAME glfw3) endif() -if (GLFW_USE_WAYLAND) - set(GLFW_USE_EGL ON) -elseif (GLFW_USE_MIR) - set(GLFW_USE_EGL ON) -endif() - set(CMAKE_MODULE_PATH "${GLFW_SOURCE_DIR}/CMake/modules") find_package(Threads REQUIRED) @@ -71,10 +65,6 @@ find_package(Vulkan) if (GLFW_BUILD_DOCS) set(DOXYGEN_SKIP_DOT TRUE) find_package(Doxygen) - - if (GLFW_DOCUMENT_INTERNALS) - set(GLFW_INTERNAL_DOCS "${GLFW_SOURCE_DIR}/src/internal.h ${GLFW_SOURCE_DIR}/docs/internal.dox") - endif() endif() #-------------------------------------------------------------------- @@ -131,19 +121,9 @@ endif() if (WIN32) set(_GLFW_WIN32 1) message(STATUS "Using Win32 for window creation") - - if (GLFW_USE_EGL) - set(_GLFW_EGL 1) - message(STATUS "Using EGL for context creation") - else() - set(_GLFW_WGL 1) - message(STATUS "Using WGL for context creation") - endif() elseif (APPLE) set(_GLFW_COCOA 1) message(STATUS "Using Cocoa for window creation") - set(_GLFW_NSGL 1) - message(STATUS "Using NSGL for context creation") elseif (UNIX) if (GLFW_USE_WAYLAND) set(_GLFW_WAYLAND 1) @@ -155,14 +135,6 @@ elseif (UNIX) set(_GLFW_X11 1) message(STATUS "Using X11 for window creation") endif() - - if (GLFW_USE_EGL) - set(_GLFW_EGL 1) - message(STATUS "Using EGL for context creation") - else() - set(_GLFW_GLX 1) - message(STATUS "Using GLX for context creation") - endif() else() message(FATAL_ERROR "No supported platform was detected") endif() @@ -234,21 +206,6 @@ if (_GLFW_X11) list(APPEND glfw_LIBRARIES "${X11_Xinerama_LIB}") list(APPEND glfw_PKG_DEPS "xinerama") - # Check for XInput (high-resolution cursor motion) - if (X11_Xinput_FOUND) - list(APPEND glfw_INCLUDE_DIRS "${X11_Xinput_INCLUDE_PATH}") - list(APPEND glfw_PKG_DEPS "xi") - - if (X11_Xinput_LIB) - list(APPEND glfw_LIBRARIES "${X11_Xinput_LIB}") - else() - # Backwards compatibility (bug in CMake 2.8.7) - list(APPEND glfw_LIBRARIES Xi) - endif() - - set(_GLFW_HAS_XINPUT TRUE) - endif() - # Check for Xf86VidMode (fallback gamma control) if (X11_xf86vmode_FOUND) list(APPEND glfw_INCLUDE_DIRS "${X11_xf86vmode_INCLUDE_PATH}") @@ -286,11 +243,17 @@ endif() # Use Wayland for window creation #-------------------------------------------------------------------- if (_GLFW_WAYLAND) + find_package(ECM REQUIRED NO_MODULE) + set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH}) + find_package(Wayland REQUIRED) + find_package(WaylandScanner REQUIRED) + find_package(WaylandProtocols 1.1 REQUIRED) + list(APPEND glfw_PKG_DEPS "wayland-egl") - list(APPEND glfw_INCLUDE_DIRS "${WAYLAND_INCLUDE_DIR}") - list(APPEND glfw_LIBRARIES "${WAYLAND_LIBRARIES}" "${CMAKE_THREAD_LIBS_INIT}") + list(APPEND glfw_INCLUDE_DIRS "${Wayland_INCLUDE_DIR}") + list(APPEND glfw_LIBRARIES "${Wayland_LIBRARIES}" "${CMAKE_THREAD_LIBS_INIT}") find_package(XKBCommon REQUIRED) list(APPEND glfw_PKG_DEPS "xkbcommon") @@ -317,7 +280,7 @@ endif() #-------------------------------------------------------------------- # Use Cocoa for window creation and NSOpenGL for context creation #-------------------------------------------------------------------- -if (_GLFW_COCOA AND _GLFW_NSGL) +if (_GLFW_COCOA) if (GLFW_USE_MENUBAR) set(_GLFW_USE_MENUBAR 1) @@ -378,10 +341,6 @@ write_basic_package_version_file(src/glfw3ConfigVersion.cmake VERSION ${GLFW_VERSION_FULL} COMPATIBILITY SameMajorVersion) -if (GLFW_BUILD_DOCS) - configure_file(docs/Doxyfile.in docs/Doxyfile @ONLY) -endif() - configure_file(src/glfw_config.h.in src/glfw_config.h @ONLY) configure_file(src/glfw3.pc.in src/glfw3.pc @ONLY) diff --git a/README.md b/README.md index 9fe915965..aa72d5e70 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ for creating windows, contexts and surfaces, reading input, handling events, etc Version 3.2 is _not yet described_. If you are new to GLFW, you may find the -[introductory tutorial](http://www.glfw.org/docs/latest/quick.html) for GLFW +[tutorial](http://www.glfw.org/docs/latest/quick.html) for GLFW 3 useful. If you have used GLFW 2 in the past, there is a [transition guide](http://www.glfw.org/docs/latest/moving.html) for moving to the GLFW 3 API. @@ -52,8 +52,8 @@ GLFW itself needs only the headers and libraries for your window system. It does not need the headers for any context creation API (WGL, GLX, EGL, NSGL) or rendering API (OpenGL, OpenGL ES, Vulkan) to enable support for them. -GLFW bundles a number of dependencies in the `deps/` directory. These are only -used by the tests and examples and are not required to build the library. +The examples and test programs depend on a number of tiny libraries. These are +located in the `deps/` directory. - [getopt\_port](https://github.com/kimgr/getopt_port/) for examples with command-line options @@ -63,46 +63,75 @@ used by the tests and examples and are not required to build the library. [glad](https://github.com/Dav1dde/glad) for examples using modern OpenGL - [linmath.h](https://github.com/datenwolf/linmath.h) for linear algebra in examples + - [Vulkan headers](https://www.khronos.org/registry/vulkan/) for Vulkan tests The Vulkan example additionally requires the Vulkan SDK to be installed, or it will not be included in the build. +The documentation is generated with [Doxygen](http://doxygen.org/). If CMake +does not find Doxygen, the documentation will not be generated. + ## Changelog - Added `glfwVulkanSupported`, `glfwGetRequiredInstanceExtensions`, `glfwGetInstanceProcAddress`, `glfwGetPhysicalDevicePresentationSupport` and `glfwCreateWindowSurface` for platform independent Vulkan support + - Added `glfwSetWindowMonitor` for switching between windowed and full screen + modes and updating the monitor and desired video mode of full screen windows + - Added `glfwMaximizeWindow` and `GLFW_MAXIMIZED` for window maximization + - Added `glfwFocusWindow` for giving windows input focus - Added `glfwSetWindowSizeLimits` and `glfwSetWindowAspectRatio` for setting absolute and relative window size limits - Added `glfwGetKeyName` for querying the layout-specific name of printable keys + - Added `glfwWaitEventsTimeout` for waiting for events for a set amount of time + - Added `glfwSetWindowIcon` for setting the icon of a window + - Added `glfwGetTimerValue` and `glfwGetTimerFrequency` for raw timer access + - Added `glfwSetJoystickCallback` and `GLFWjoystickfun` for joystick connection + and disconnection events - Added `GLFW_NO_API` for creating window without contexts - Added `GLFW_CONTEXT_NO_ERROR` context hint for `GL_KHR_no_error` support - Added `GLFW_INCLUDE_VULKAN` for including the Vulkan header + - Added `GLFW_CONTEXT_CREATION_API`, `GLFW_NATIVE_CONTEXT_API` and + `GLFW_EGL_CONTEXT_API` for run-time context creation API selection - Added `GLFW_TRUE` and `GLFW_FALSE` as client API independent boolean values - - Added `glfwGetGLXWindow` to query the `GLXWindow` of a window - Added icons to examples on Windows and OS X - Relaxed rules for native access header macros - Removed dependency on external OpenGL or OpenGL ES headers - - Removed `_GLFW_USE_OPENGL`, `_GLFW_USE_GLESV1` and `_GLFW_USE_GLESV2` - configuration macros + - Removed `_GLFW_USE_OPENGL`, `_GLFW_USE_GLESV1`, `_GLFW_USE_GLESV2`, + `_GLFW_WGL`, `_GLFW_NSGL`, `_GLFW_GLX` and `_GLFW_EGL` configuration macros - [Win32] Added support for Windows 8.1 per-monitor DPI + - [Win32] Replaced winmm with XInput and DirectInput for joystick input - [Win32] Bugfix: Window creation would segfault if video mode setting required the system to be restarted - [Win32] Bugfix: MinGW import library lacked the `lib` prefix - [Win32] Bugfix: Monitor connection and disconnection events were not reported when no windows existed + - [Win32] Bugfix: Activating or deactivating displays in software did not + trigger monitor callback + - [Win32] Bugfix: No monitors were listed on headless and VMware guest systems + - [Win32] Bugfix: Pressing Ctrl+Pause would report `GLFW_KEY_UNKNOWN` + - [Win32] Bugfix: Window size events would be reported in wrong order when + restoring a full screen window + - [Cocoa] Made joystick polling more efficient - [Cocoa] Removed support for OS X 10.6 - [Cocoa] Bugfix: Full screen windows on secondary monitors were mispositioned - [Cocoa] Bugfix: Connecting a joystick that reports no name would segfault + - [Cocoa] Bugfix: Modifier flags cache was not updated when window became key + - [Cocoa] Bugfix: Dead key character composition did not work + - [Cocoa] Bugfix: The CGL context was not released until the autorelease pool + was drained by another function - [X11] Bugfix: Monitor connection and disconnection events were not reported - [X11] Bugfix: Decoding of UTF-8 text from XIM could continue past the end - [X11] Bugfix: An XKB structure was leaked during `glfwInit` + - [X11] Bugfix: XInput2 `XI_Motion` events interfered with the Steam overlay - [POSIX] Bugfix: An unrelated TLS key could be deleted by `glfwTerminate` + - [Linux] Made joystick polling more efficient - [WGL] Changed extension loading to only be performed once - [WGL] Removed dependency on external WGL headers - - [GLX] Replaced legacy renderable with `GLXWindow` + - [GLX] Added `glfwGetGLXWindow` to query the `GLXWindow` of a window + - [GLX] Replaced legacy drawable with `GLXWindow` - [GLX] Removed dependency on external GLX headers - [GLX] Bugfix: NetBSD does not provide `libGL.so.1` - [EGL] Added `_GLFW_USE_EGLPLATFORM_H` configuration macro for controlling @@ -119,7 +148,7 @@ can find the latest version of GLFW, as well as news, documentation and other information about the project. If you have questions related to the use of GLFW, we have a -[support forum](https://sourceforge.net/p/glfw/discussion/247562/), and the IRC +[support forum](http://discourse.glfw.org/), and the IRC channel `#glfw` on [Freenode](http://freenode.net/). If you have a bug to report, a patch to submit or a feature you'd like to @@ -155,6 +184,7 @@ skills. - Paul R. Deppe - Michael Dickens - Роман Донченко + - Mario Dorn - Jonathan Dummer - Ralph Eastwood - Siavash Eliasi @@ -169,6 +199,7 @@ skills. - heromyth - Lucas Hinderberger - Paul Holden + - IntellectualKitty - Aaron Jacobs - Toni Jovanoski - Arseny Kapoulkine @@ -205,6 +236,7 @@ skills. - Peoro - Braden Pellett - Arturo J. Pérez + - Orson Peters - Emmanuel Gil Peyrot - Cyril Pichard - Pieroman @@ -218,6 +250,7 @@ skills. - SephiRok - Steve Sexton - Systemcluster + - Yoshiki Shibukawa - Dmitri Shuralyov - Daniel Skorupski - Bradley Smith @@ -229,6 +262,7 @@ skills. - TTK-Bandit - Sergey Tikhomirov - A. Tombs + - Ioannis Tsakpinis - Samuli Tuomola - urraka - Jari Vetoniemi @@ -237,6 +271,7 @@ skills. - Simon Voordouw - Torsten Walluhn - Patrick Walton + - Xo Wang - Jay Weisskopf - Frank Wille - yuriks diff --git a/deps/tinycthread.h b/deps/tinycthread.h index 86a908046..6da30d703 100644 --- a/deps/tinycthread.h +++ b/deps/tinycthread.h @@ -78,6 +78,7 @@ freely, subject to the following restrictions: /* Platform specific includes */ #if defined(_TTHREAD_POSIX_) + #include #include #elif defined(_TTHREAD_WIN32_) #ifndef WIN32_LEAN_AND_MEAN diff --git a/deps/vulkan/vk_platform.h b/deps/vulkan/vk_platform.h index a53e725a9..5d0fc766e 100644 --- a/deps/vulkan/vk_platform.h +++ b/deps/vulkan/vk_platform.h @@ -4,29 +4,22 @@ /* ** Copyright (c) 2014-2015 The Khronos Group Inc. ** -** Permission is hereby granted, free of charge, to any person obtaining a -** copy of this software and/or associated documentation files (the -** "Materials"), to deal in the Materials without restriction, including -** without limitation the rights to use, copy, modify, merge, publish, -** distribute, sublicense, and/or sell copies of the Materials, and to -** permit persons to whom the Materials are furnished to do so, subject to -** the following conditions: +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at ** -** The above copyright notice and this permission notice shall be included -** in all copies or substantial portions of the Materials. +** http://www.apache.org/licenses/LICENSE-2.0 ** -** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. */ -#ifndef __VK_PLATFORM_H__ -#define __VK_PLATFORM_H__ +#ifndef VK_PLATFORM_H_ +#define VK_PLATFORM_H_ #ifdef __cplusplus extern "C" @@ -124,4 +117,4 @@ extern "C" #include #endif -#endif // __VK_PLATFORM_H__ +#endif diff --git a/deps/vulkan/vulkan.h b/deps/vulkan/vulkan.h index cd6a71ac1..eb8343e26 100644 --- a/deps/vulkan/vulkan.h +++ b/deps/vulkan/vulkan.h @@ -1,5 +1,5 @@ -#ifndef __vulkan_h_ -#define __vulkan_h_ 1 +#ifndef VULKAN_H_ +#define VULKAN_H_ 1 #ifdef __cplusplus extern "C" { @@ -8,24 +8,17 @@ extern "C" { /* ** Copyright (c) 2015-2016 The Khronos Group Inc. ** -** Permission is hereby granted, free of charge, to any person obtaining a -** copy of this software and/or associated documentation files (the -** "Materials"), to deal in the Materials without restriction, including -** without limitation the rights to use, copy, modify, merge, publish, -** distribute, sublicense, and/or sell copies of the Materials, and to -** permit persons to whom the Materials are furnished to do so, subject to -** the following conditions: +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at ** -** The above copyright notice and this permission notice shall be included -** in all copies or substantial portions of the Materials. +** http://www.apache.org/licenses/LICENSE-2.0 ** -** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. */ /* @@ -40,12 +33,18 @@ extern "C" { #define VK_MAKE_VERSION(major, minor, patch) \ (((major) << 22) | ((minor) << 12) | (patch)) -// Vulkan API version supported by this file -#define VK_API_VERSION VK_MAKE_VERSION(1, 0, 3) +// DEPRECATED: This define has been removed. Specific version defines (e.g. VK_API_VERSION_1_0), or the VK_MAKE_VERSION macro, should be used instead. +//#define VK_API_VERSION VK_MAKE_VERSION(1, 0, 0) + +// Vulkan 1.0 version number +#define VK_API_VERSION_1_0 VK_MAKE_VERSION(1, 0, 0) #define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22) #define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff) #define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff) +// Version of this file +#define VK_HEADER_VERSION 11 + #define VK_NULL_HANDLE 0 @@ -142,6 +141,7 @@ typedef enum VkResult { VK_ERROR_OUT_OF_DATE_KHR = -1000001004, VK_ERROR_INCOMPATIBLE_DISPLAY_KHR = -1000003001, VK_ERROR_VALIDATION_FAILED_EXT = -1000011001, + VK_ERROR_INVALID_SHADER_NV = -1000012000, VK_RESULT_BEGIN_RANGE = VK_ERROR_FORMAT_NOT_SUPPORTED, VK_RESULT_END_RANGE = VK_INCOMPLETE, VK_RESULT_RANGE_SIZE = (VK_INCOMPLETE - VK_ERROR_FORMAT_NOT_SUPPORTED + 1), @@ -209,7 +209,7 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR = 1000007000, VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR = 1000008000, VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000, - VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT = 1000011000, + VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT = 1000011000, VK_STRUCTURE_TYPE_BEGIN_RANGE = VK_STRUCTURE_TYPE_APPLICATION_INFO, VK_STRUCTURE_TYPE_END_RANGE = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO, VK_STRUCTURE_TYPE_RANGE_SIZE = (VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO - VK_STRUCTURE_TYPE_APPLICATION_INFO + 1), @@ -679,6 +679,7 @@ typedef enum VkDynamicState { typedef enum VkFilter { VK_FILTER_NEAREST = 0, VK_FILTER_LINEAR = 1, + VK_FILTER_CUBIC_IMG = 1000015000, VK_FILTER_BEGIN_RANGE = VK_FILTER_NEAREST, VK_FILTER_END_RANGE = VK_FILTER_LINEAR, VK_FILTER_RANGE_SIZE = (VK_FILTER_LINEAR - VK_FILTER_NEAREST + 1), @@ -701,8 +702,8 @@ typedef enum VkSamplerAddressMode { VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER = 3, VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE = 4, VK_SAMPLER_ADDRESS_MODE_BEGIN_RANGE = VK_SAMPLER_ADDRESS_MODE_REPEAT, - VK_SAMPLER_ADDRESS_MODE_END_RANGE = VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE, - VK_SAMPLER_ADDRESS_MODE_RANGE_SIZE = (VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE - VK_SAMPLER_ADDRESS_MODE_REPEAT + 1), + VK_SAMPLER_ADDRESS_MODE_END_RANGE = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + VK_SAMPLER_ADDRESS_MODE_RANGE_SIZE = (VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER - VK_SAMPLER_ADDRESS_MODE_REPEAT + 1), VK_SAMPLER_ADDRESS_MODE_MAX_ENUM = 0x7FFFFFFF } VkSamplerAddressMode; @@ -808,6 +809,8 @@ typedef enum VkFormatFeatureFlagBits { VK_FORMAT_FEATURE_BLIT_SRC_BIT = 0x00000400, VK_FORMAT_FEATURE_BLIT_DST_BIT = 0x00000800, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT = 0x00001000, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG = 0x00002000, + VK_FORMAT_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkFormatFeatureFlagBits; typedef VkFlags VkFormatFeatureFlags; @@ -820,6 +823,7 @@ typedef enum VkImageUsageFlagBits { VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT = 0x00000020, VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT = 0x00000040, VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT = 0x00000080, + VK_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkImageUsageFlagBits; typedef VkFlags VkImageUsageFlags; @@ -829,6 +833,7 @@ typedef enum VkImageCreateFlagBits { VK_IMAGE_CREATE_SPARSE_ALIASED_BIT = 0x00000004, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT = 0x00000008, VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT = 0x00000010, + VK_IMAGE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkImageCreateFlagBits; typedef VkFlags VkImageCreateFlags; @@ -840,6 +845,7 @@ typedef enum VkSampleCountFlagBits { VK_SAMPLE_COUNT_16_BIT = 0x00000010, VK_SAMPLE_COUNT_32_BIT = 0x00000020, VK_SAMPLE_COUNT_64_BIT = 0x00000040, + VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkSampleCountFlagBits; typedef VkFlags VkSampleCountFlags; @@ -848,6 +854,7 @@ typedef enum VkQueueFlagBits { VK_QUEUE_COMPUTE_BIT = 0x00000002, VK_QUEUE_TRANSFER_BIT = 0x00000004, VK_QUEUE_SPARSE_BINDING_BIT = 0x00000008, + VK_QUEUE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkQueueFlagBits; typedef VkFlags VkQueueFlags; @@ -857,11 +864,13 @@ typedef enum VkMemoryPropertyFlagBits { VK_MEMORY_PROPERTY_HOST_COHERENT_BIT = 0x00000004, VK_MEMORY_PROPERTY_HOST_CACHED_BIT = 0x00000008, VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT = 0x00000010, + VK_MEMORY_PROPERTY_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkMemoryPropertyFlagBits; typedef VkFlags VkMemoryPropertyFlags; typedef enum VkMemoryHeapFlagBits { VK_MEMORY_HEAP_DEVICE_LOCAL_BIT = 0x00000001, + VK_MEMORY_HEAP_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkMemoryHeapFlagBits; typedef VkFlags VkMemoryHeapFlags; typedef VkFlags VkDeviceCreateFlags; @@ -885,6 +894,7 @@ typedef enum VkPipelineStageFlagBits { VK_PIPELINE_STAGE_HOST_BIT = 0x00004000, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT = 0x00008000, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT = 0x00010000, + VK_PIPELINE_STAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkPipelineStageFlagBits; typedef VkFlags VkPipelineStageFlags; typedef VkFlags VkMemoryMapFlags; @@ -894,6 +904,7 @@ typedef enum VkImageAspectFlagBits { VK_IMAGE_ASPECT_DEPTH_BIT = 0x00000002, VK_IMAGE_ASPECT_STENCIL_BIT = 0x00000004, VK_IMAGE_ASPECT_METADATA_BIT = 0x00000008, + VK_IMAGE_ASPECT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkImageAspectFlagBits; typedef VkFlags VkImageAspectFlags; @@ -901,16 +912,19 @@ typedef enum VkSparseImageFormatFlagBits { VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT = 0x00000001, VK_SPARSE_IMAGE_FORMAT_ALIGNED_MIP_SIZE_BIT = 0x00000002, VK_SPARSE_IMAGE_FORMAT_NONSTANDARD_BLOCK_SIZE_BIT = 0x00000004, + VK_SPARSE_IMAGE_FORMAT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkSparseImageFormatFlagBits; typedef VkFlags VkSparseImageFormatFlags; typedef enum VkSparseMemoryBindFlagBits { VK_SPARSE_MEMORY_BIND_METADATA_BIT = 0x00000001, + VK_SPARSE_MEMORY_BIND_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkSparseMemoryBindFlagBits; typedef VkFlags VkSparseMemoryBindFlags; typedef enum VkFenceCreateFlagBits { VK_FENCE_CREATE_SIGNALED_BIT = 0x00000001, + VK_FENCE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkFenceCreateFlagBits; typedef VkFlags VkFenceCreateFlags; typedef VkFlags VkSemaphoreCreateFlags; @@ -929,6 +943,7 @@ typedef enum VkQueryPipelineStatisticFlagBits { VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT = 0x00000100, VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT = 0x00000200, VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT = 0x00000400, + VK_QUERY_PIPELINE_STATISTIC_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkQueryPipelineStatisticFlagBits; typedef VkFlags VkQueryPipelineStatisticFlags; @@ -937,6 +952,7 @@ typedef enum VkQueryResultFlagBits { VK_QUERY_RESULT_WAIT_BIT = 0x00000002, VK_QUERY_RESULT_WITH_AVAILABILITY_BIT = 0x00000004, VK_QUERY_RESULT_PARTIAL_BIT = 0x00000008, + VK_QUERY_RESULT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkQueryResultFlagBits; typedef VkFlags VkQueryResultFlags; @@ -944,6 +960,7 @@ typedef enum VkBufferCreateFlagBits { VK_BUFFER_CREATE_SPARSE_BINDING_BIT = 0x00000001, VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT = 0x00000002, VK_BUFFER_CREATE_SPARSE_ALIASED_BIT = 0x00000004, + VK_BUFFER_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkBufferCreateFlagBits; typedef VkFlags VkBufferCreateFlags; @@ -957,6 +974,7 @@ typedef enum VkBufferUsageFlagBits { VK_BUFFER_USAGE_INDEX_BUFFER_BIT = 0x00000040, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT = 0x00000080, VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT = 0x00000100, + VK_BUFFER_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkBufferUsageFlagBits; typedef VkFlags VkBufferUsageFlags; typedef VkFlags VkBufferViewCreateFlags; @@ -968,6 +986,7 @@ typedef enum VkPipelineCreateFlagBits { VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT = 0x00000001, VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT = 0x00000002, VK_PIPELINE_CREATE_DERIVATIVE_BIT = 0x00000004, + VK_PIPELINE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkPipelineCreateFlagBits; typedef VkFlags VkPipelineCreateFlags; typedef VkFlags VkPipelineShaderStageCreateFlags; @@ -979,8 +998,9 @@ typedef enum VkShaderStageFlagBits { VK_SHADER_STAGE_GEOMETRY_BIT = 0x00000008, VK_SHADER_STAGE_FRAGMENT_BIT = 0x00000010, VK_SHADER_STAGE_COMPUTE_BIT = 0x00000020, - VK_SHADER_STAGE_ALL_GRAPHICS = 0x1F, + VK_SHADER_STAGE_ALL_GRAPHICS = 0x0000001F, VK_SHADER_STAGE_ALL = 0x7FFFFFFF, + VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkShaderStageFlagBits; typedef VkFlags VkPipelineVertexInputStateCreateFlags; typedef VkFlags VkPipelineInputAssemblyStateCreateFlags; @@ -992,7 +1012,8 @@ typedef enum VkCullModeFlagBits { VK_CULL_MODE_NONE = 0, VK_CULL_MODE_FRONT_BIT = 0x00000001, VK_CULL_MODE_BACK_BIT = 0x00000002, - VK_CULL_MODE_FRONT_AND_BACK = 0x3, + VK_CULL_MODE_FRONT_AND_BACK = 0x00000003, + VK_CULL_MODE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkCullModeFlagBits; typedef VkFlags VkCullModeFlags; typedef VkFlags VkPipelineMultisampleStateCreateFlags; @@ -1004,6 +1025,7 @@ typedef enum VkColorComponentFlagBits { VK_COLOR_COMPONENT_G_BIT = 0x00000002, VK_COLOR_COMPONENT_B_BIT = 0x00000004, VK_COLOR_COMPONENT_A_BIT = 0x00000008, + VK_COLOR_COMPONENT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkColorComponentFlagBits; typedef VkFlags VkColorComponentFlags; typedef VkFlags VkPipelineDynamicStateCreateFlags; @@ -1014,6 +1036,7 @@ typedef VkFlags VkDescriptorSetLayoutCreateFlags; typedef enum VkDescriptorPoolCreateFlagBits { VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT = 0x00000001, + VK_DESCRIPTOR_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkDescriptorPoolCreateFlagBits; typedef VkFlags VkDescriptorPoolCreateFlags; typedef VkFlags VkDescriptorPoolResetFlags; @@ -1022,6 +1045,7 @@ typedef VkFlags VkRenderPassCreateFlags; typedef enum VkAttachmentDescriptionFlagBits { VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT = 0x00000001, + VK_ATTACHMENT_DESCRIPTION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkAttachmentDescriptionFlagBits; typedef VkFlags VkAttachmentDescriptionFlags; typedef VkFlags VkSubpassDescriptionFlags; @@ -1044,22 +1068,26 @@ typedef enum VkAccessFlagBits { VK_ACCESS_HOST_WRITE_BIT = 0x00004000, VK_ACCESS_MEMORY_READ_BIT = 0x00008000, VK_ACCESS_MEMORY_WRITE_BIT = 0x00010000, + VK_ACCESS_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkAccessFlagBits; typedef VkFlags VkAccessFlags; typedef enum VkDependencyFlagBits { VK_DEPENDENCY_BY_REGION_BIT = 0x00000001, + VK_DEPENDENCY_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkDependencyFlagBits; typedef VkFlags VkDependencyFlags; typedef enum VkCommandPoolCreateFlagBits { VK_COMMAND_POOL_CREATE_TRANSIENT_BIT = 0x00000001, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT = 0x00000002, + VK_COMMAND_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkCommandPoolCreateFlagBits; typedef VkFlags VkCommandPoolCreateFlags; typedef enum VkCommandPoolResetFlagBits { VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT = 0x00000001, + VK_COMMAND_POOL_RESET_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkCommandPoolResetFlagBits; typedef VkFlags VkCommandPoolResetFlags; @@ -1067,23 +1095,27 @@ typedef enum VkCommandBufferUsageFlagBits { VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT = 0x00000001, VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT = 0x00000002, VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT = 0x00000004, + VK_COMMAND_BUFFER_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkCommandBufferUsageFlagBits; typedef VkFlags VkCommandBufferUsageFlags; typedef enum VkQueryControlFlagBits { VK_QUERY_CONTROL_PRECISE_BIT = 0x00000001, + VK_QUERY_CONTROL_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkQueryControlFlagBits; typedef VkFlags VkQueryControlFlags; typedef enum VkCommandBufferResetFlagBits { VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT = 0x00000001, + VK_COMMAND_BUFFER_RESET_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkCommandBufferResetFlagBits; typedef VkFlags VkCommandBufferResetFlags; typedef enum VkStencilFaceFlagBits { VK_STENCIL_FACE_FRONT_BIT = 0x00000001, VK_STENCIL_FACE_BACK_BIT = 0x00000002, - VK_STENCIL_FRONT_AND_BACK = 0x3, + VK_STENCIL_FRONT_AND_BACK = 0x00000003, + VK_STENCIL_FACE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkStencilFaceFlagBits; typedef VkFlags VkStencilFaceFlags; @@ -3140,10 +3172,10 @@ VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSurfaceKHR) typedef enum VkColorSpaceKHR { VK_COLORSPACE_SRGB_NONLINEAR_KHR = 0, - VK_COLORSPACE_BEGIN_RANGE = VK_COLORSPACE_SRGB_NONLINEAR_KHR, - VK_COLORSPACE_END_RANGE = VK_COLORSPACE_SRGB_NONLINEAR_KHR, - VK_COLORSPACE_RANGE_SIZE = (VK_COLORSPACE_SRGB_NONLINEAR_KHR - VK_COLORSPACE_SRGB_NONLINEAR_KHR + 1), - VK_COLORSPACE_MAX_ENUM = 0x7FFFFFFF + VK_COLOR_SPACE_BEGIN_RANGE_KHR = VK_COLORSPACE_SRGB_NONLINEAR_KHR, + VK_COLOR_SPACE_END_RANGE_KHR = VK_COLORSPACE_SRGB_NONLINEAR_KHR, + VK_COLOR_SPACE_RANGE_SIZE_KHR = (VK_COLORSPACE_SRGB_NONLINEAR_KHR - VK_COLORSPACE_SRGB_NONLINEAR_KHR + 1), + VK_COLOR_SPACE_MAX_ENUM_KHR = 0x7FFFFFFF } VkColorSpaceKHR; typedef enum VkPresentModeKHR { @@ -3151,10 +3183,10 @@ typedef enum VkPresentModeKHR { VK_PRESENT_MODE_MAILBOX_KHR = 1, VK_PRESENT_MODE_FIFO_KHR = 2, VK_PRESENT_MODE_FIFO_RELAXED_KHR = 3, - VK_PRESENT_MODE_BEGIN_RANGE = VK_PRESENT_MODE_IMMEDIATE_KHR, - VK_PRESENT_MODE_END_RANGE = VK_PRESENT_MODE_FIFO_RELAXED_KHR, - VK_PRESENT_MODE_RANGE_SIZE = (VK_PRESENT_MODE_FIFO_RELAXED_KHR - VK_PRESENT_MODE_IMMEDIATE_KHR + 1), - VK_PRESENT_MODE_MAX_ENUM = 0x7FFFFFFF + VK_PRESENT_MODE_BEGIN_RANGE_KHR = VK_PRESENT_MODE_IMMEDIATE_KHR, + VK_PRESENT_MODE_END_RANGE_KHR = VK_PRESENT_MODE_FIFO_RELAXED_KHR, + VK_PRESENT_MODE_RANGE_SIZE_KHR = (VK_PRESENT_MODE_FIFO_RELAXED_KHR - VK_PRESENT_MODE_IMMEDIATE_KHR + 1), + VK_PRESENT_MODE_MAX_ENUM_KHR = 0x7FFFFFFF } VkPresentModeKHR; @@ -3168,6 +3200,7 @@ typedef enum VkSurfaceTransformFlagBitsKHR { VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR = 0x00000040, VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR = 0x00000080, VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR = 0x00000100, + VK_SURFACE_TRANSFORM_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF } VkSurfaceTransformFlagBitsKHR; typedef VkFlags VkSurfaceTransformFlagsKHR; @@ -3176,6 +3209,7 @@ typedef enum VkCompositeAlphaFlagBitsKHR { VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR = 0x00000002, VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR = 0x00000004, VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR = 0x00000008, + VK_COMPOSITE_ALPHA_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF } VkCompositeAlphaFlagBitsKHR; typedef VkFlags VkCompositeAlphaFlagsKHR; @@ -3237,7 +3271,7 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfacePresentModesKHR( #define VK_KHR_swapchain 1 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSwapchainKHR) -#define VK_KHR_SWAPCHAIN_SPEC_VERSION 67 +#define VK_KHR_SWAPCHAIN_SPEC_VERSION 68 #define VK_KHR_SWAPCHAIN_EXTENSION_NAME "VK_KHR_swapchain" typedef VkFlags VkSwapchainCreateFlagsKHR; @@ -3325,9 +3359,10 @@ typedef enum VkDisplayPlaneAlphaFlagBitsKHR { VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR = 0x00000002, VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR = 0x00000004, VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR = 0x00000008, + VK_DISPLAY_PLANE_ALPHA_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF } VkDisplayPlaneAlphaFlagBitsKHR; -typedef VkFlags VkDisplayModeCreateFlagsKHR; typedef VkFlags VkDisplayPlaneAlphaFlagsKHR; +typedef VkFlags VkDisplayModeCreateFlagsKHR; typedef VkFlags VkDisplaySurfaceCreateFlagsKHR; typedef struct VkDisplayPropertiesKHR { @@ -3392,7 +3427,7 @@ typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceDisplayPropertiesKHR)(VkPhys typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR)(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkDisplayPlanePropertiesKHR* pProperties); typedef VkResult (VKAPI_PTR *PFN_vkGetDisplayPlaneSupportedDisplaysKHR)(VkPhysicalDevice physicalDevice, uint32_t planeIndex, uint32_t* pDisplayCount, VkDisplayKHR* pDisplays); typedef VkResult (VKAPI_PTR *PFN_vkGetDisplayModePropertiesKHR)(VkPhysicalDevice physicalDevice, VkDisplayKHR display, uint32_t* pPropertyCount, VkDisplayModePropertiesKHR* pProperties); -typedef VkResult (VKAPI_PTR *PFN_vkCreateDisplayModeKHR)(VkPhysicalDevice physicalDevice, VkDisplayKHR display, const VkDisplayModeCreateInfoKHR*pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDisplayModeKHR* pMode); +typedef VkResult (VKAPI_PTR *PFN_vkCreateDisplayModeKHR)(VkPhysicalDevice physicalDevice, VkDisplayKHR display, const VkDisplayModeCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDisplayModeKHR* pMode); typedef VkResult (VKAPI_PTR *PFN_vkGetDisplayPlaneCapabilitiesKHR)(VkPhysicalDevice physicalDevice, VkDisplayModeKHR mode, uint32_t planeIndex, VkDisplayPlaneCapabilitiesKHR* pCapabilities); typedef VkResult (VKAPI_PTR *PFN_vkCreateDisplayPlaneSurfaceKHR)(VkInstance instance, const VkDisplaySurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); @@ -3667,11 +3702,17 @@ VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceWin32PresentationSupportKHR( #endif #endif /* VK_USE_PLATFORM_WIN32_KHR */ +#define VK_KHR_sampler_mirror_clamp_to_edge 1 +#define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_SPEC_VERSION 1 +#define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME "VK_KHR_sampler_mirror_clamp_to_edge" + + #define VK_EXT_debug_report 1 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT) -#define VK_EXT_DEBUG_REPORT_SPEC_VERSION 1 +#define VK_EXT_DEBUG_REPORT_SPEC_VERSION 2 #define VK_EXT_DEBUG_REPORT_EXTENSION_NAME "VK_EXT_debug_report" +#define VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT typedef enum VkDebugReportObjectTypeEXT { @@ -3704,11 +3745,19 @@ typedef enum VkDebugReportObjectTypeEXT { VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT = 26, VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT = 27, VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT = 28, + VK_DEBUG_REPORT_OBJECT_TYPE_BEGIN_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_END_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT - VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT + 1), + VK_DEBUG_REPORT_OBJECT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF } VkDebugReportObjectTypeEXT; typedef enum VkDebugReportErrorEXT { VK_DEBUG_REPORT_ERROR_NONE_EXT = 0, VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT = 1, + VK_DEBUG_REPORT_ERROR_BEGIN_RANGE_EXT = VK_DEBUG_REPORT_ERROR_NONE_EXT, + VK_DEBUG_REPORT_ERROR_END_RANGE_EXT = VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT, + VK_DEBUG_REPORT_ERROR_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT - VK_DEBUG_REPORT_ERROR_NONE_EXT + 1), + VK_DEBUG_REPORT_ERROR_MAX_ENUM_EXT = 0x7FFFFFFF } VkDebugReportErrorEXT; @@ -3718,6 +3767,7 @@ typedef enum VkDebugReportFlagBitsEXT { VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT = 0x00000004, VK_DEBUG_REPORT_ERROR_BIT_EXT = 0x00000008, VK_DEBUG_REPORT_DEBUG_BIT_EXT = 0x00000010, + VK_DEBUG_REPORT_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF } VkDebugReportFlagBitsEXT; typedef VkFlags VkDebugReportFlagsEXT; @@ -3768,6 +3818,16 @@ VKAPI_ATTR void VKAPI_CALL vkDebugReportMessageEXT( const char* pMessage); #endif +#define VK_NV_glsl_shader 1 +#define VK_NV_GLSL_SHADER_SPEC_VERSION 1 +#define VK_NV_GLSL_SHADER_EXTENSION_NAME "VK_NV_glsl_shader" + + +#define VK_IMG_filter_cubic 1 +#define VK_IMG_FILTER_CUBIC_SPEC_VERSION 1 +#define VK_IMG_FILTER_CUBIC_EXTENSION_NAME "VK_IMG_filter_cubic" + + #ifdef __cplusplus } #endif diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index 45a6162e8..063aa48a4 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -1,5 +1,33 @@ -add_custom_target(docs ALL ${DOXYGEN_EXECUTABLE} - WORKING_DIRECTORY ${GLFW_BINARY_DIR}/docs +set(glfw_DOCS_SOURCES + "${GLFW_SOURCE_DIR}/include/GLFW/glfw3.h" + "${GLFW_SOURCE_DIR}/include/GLFW/glfw3native.h" + "${GLFW_SOURCE_DIR}/docs/main.dox" + "${GLFW_SOURCE_DIR}/docs/news.dox" + "${GLFW_SOURCE_DIR}/docs/moving.dox" + "${GLFW_SOURCE_DIR}/docs/quick.dox" + "${GLFW_SOURCE_DIR}/docs/compile.dox" + "${GLFW_SOURCE_DIR}/docs/build.dox" + "${GLFW_SOURCE_DIR}/docs/intro.dox" + "${GLFW_SOURCE_DIR}/docs/context.dox" + "${GLFW_SOURCE_DIR}/docs/monitor.dox" + "${GLFW_SOURCE_DIR}/docs/window.dox" + "${GLFW_SOURCE_DIR}/docs/input.dox" + "${GLFW_SOURCE_DIR}/docs/vulkan.dox" + "${GLFW_SOURCE_DIR}/docs/rift.dox" + "${GLFW_SOURCE_DIR}/docs/compat.dox") + +if (GLFW_DOCUMENT_INTERNALS) + list(APPEND glfw_DOCS_SOURCES "${GLFW_SOURCE_DIR}/src/internal.h") +endif() + +foreach(arg ${glfw_DOCS_SOURCES}) + set(GLFW_DOCS_SOURCES "${GLFW_DOCS_SOURCES} ${arg}") +endforeach() + +configure_file(Doxyfile.in Doxyfile @ONLY) + +add_custom_target(docs ALL "${DOXYGEN_EXECUTABLE}" + WORKING_DIRECTORY "${GLFW_BINARY_DIR}/docs" COMMENT "Generating HTML documentation" VERBATIM) diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index 696cb2cf1..b046f898b 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -660,23 +660,7 @@ WARN_LOGFILE = @GLFW_BINARY_DIR@/docs/warnings.txt # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = @GLFW_INTERNAL_DOCS@ \ - @GLFW_SOURCE_DIR@/include/GLFW/glfw3.h \ - @GLFW_SOURCE_DIR@/include/GLFW/glfw3native.h \ - @GLFW_SOURCE_DIR@/docs/main.dox \ - @GLFW_SOURCE_DIR@/docs/news.dox \ - @GLFW_SOURCE_DIR@/docs/moving.dox \ - @GLFW_SOURCE_DIR@/docs/quick.dox \ - @GLFW_SOURCE_DIR@/docs/compile.dox \ - @GLFW_SOURCE_DIR@/docs/build.dox \ - @GLFW_SOURCE_DIR@/docs/intro.dox \ - @GLFW_SOURCE_DIR@/docs/context.dox \ - @GLFW_SOURCE_DIR@/docs/monitor.dox \ - @GLFW_SOURCE_DIR@/docs/window.dox \ - @GLFW_SOURCE_DIR@/docs/input.dox \ - @GLFW_SOURCE_DIR@/docs/vulkan.dox \ - @GLFW_SOURCE_DIR@/docs/rift.dox \ - @GLFW_SOURCE_DIR@/docs/compat.dox +INPUT = @GLFW_DOCS_SOURCES@ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is diff --git a/docs/build.dox b/docs/build.dox index 16cb35a4f..fde65add1 100644 --- a/docs/build.dox +++ b/docs/build.dox @@ -1,13 +1,13 @@ /*! -@page build Building applications +@page build_guide Building applications @tableofcontents This is about compiling and linking applications that use GLFW. For information on -how to write such applications, start with the [introductory tutorial](@ref quick). -For information on how to compile the GLFW library itself, see the @ref compile -guide. +how to write such applications, start with the +[introductory tutorial](@ref quick_guide). For information on how to compile +the GLFW library itself, see @ref compile_guide. This is not a tutorial on compilation or linking. It assumes basic understanding of how to compile and link a C program as well as how to use the @@ -158,7 +158,17 @@ build_link_cmake_module. With just a few changes to your `CMakeLists.txt` you can have the GLFW source tree built along with your application. -Firstly, add the root directory of the GLFW source tree to your project. This +When including GLFW as part of your build, you probably don't want to build the +GLFW tests, examples and documentation. To disable these, set the corresponding +cache variables before adding the GLFW source tree. + +@code +set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE) +set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE) +set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) +@endcode + +Then 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. @code{.cmake} @@ -231,7 +241,7 @@ target_link_libraries(myapp ${OPENGL_glu_LIBRARY}) @subsection build_link_pkgconfig With makefiles and pkg-config on Unix GLFW supports [pkg-config](http://www.freedesktop.org/wiki/Software/pkg-config/), -and the `glfw3.pc` pkf-config file is generated when the GLFW library is built +and the `glfw3.pc` pkg-config file is generated when the GLFW library is built and is installed along with it. A pkg-config file describes all necessary compile-time and link-time flags and dependencies needed to use a library. When they are updated or if they differ between systems, you will get the correct diff --git a/docs/compat.dox b/docs/compat.dox index be1cf70d9..ee10770f5 100644 --- a/docs/compat.dox +++ b/docs/compat.dox @@ -1,6 +1,6 @@ /*! -@page compat Standards conformance +@page compat_guide Standards conformance @tableofcontents @@ -63,10 +63,6 @@ GLFW uses the to provide file drop events. If the application originating the drag does not support this protocol, drag and drop will not work. -GLFW uses the XInput 2 extension to provide sub-pixel cursor motion events. If -the running X server does not support this version of this extension, cursor -motion will be snapped to the pixel grid. - GLFW uses the XRandR 1.3 extension to provide multi-monitor support. If the running X server does not support this version of this extension, multi-monitor support will not function and only a single, desktop-spanning monitor will be diff --git a/docs/compile.dox b/docs/compile.dox index d1a0dd529..adadf7793 100644 --- a/docs/compile.dox +++ b/docs/compile.dox @@ -1,11 +1,11 @@ /*! -@page compile Compiling GLFW +@page compile_guide Compiling GLFW @tableofcontents This is about compiling the GLFW library itself. For information on how to -build applications that use GLFW, see the @ref build guide. +build applications that use GLFW, see @ref build_guide. @section compile_cmake Using CMake @@ -87,10 +87,10 @@ Once you have Xcode installed, move on to @ref compile_generate. @subsubsection compile_deps_x11 Dependencies for Linux and X11 -To compile GLFW for X11, you need to have the X11 and OpenGL header packages -installed, as well as the basic development tools like GCC and make. For -example, on Ubuntu and other distributions based on Debian GNU/Linux, you need -to install the `xorg-dev` package, which pulls in all X.org header packages. +To compile GLFW for X11, you need to have the X11 packages installed, as well as +the basic development tools like GCC and make. For example, on Ubuntu and other +distributions based on Debian GNU/Linux, you need to install the `xorg-dev` +package, which pulls in all X.org header packages. Once you have installed the necessary packages, move on to @ref compile_generate. @@ -156,7 +156,7 @@ necessary to compile GLFW. Go ahead and compile the actual GLFW library with these files, as you would with any other project. Once the GLFW library is compiled, you are ready to build your applications, -linking it to the GLFW library. See the @ref build guide for more information. +linking it to the GLFW library. See @ref build_guide for more information. @subsection compile_options CMake options @@ -166,7 +166,7 @@ available on all supported platforms. Some of these are de facto standards among projects using CMake and so have no `GLFW_` prefix. If you are using the GUI version of CMake, these are listed and can be changed -from there. If you are using the command-line versionof CMake you can use the +from there. If you are using the command-line version of CMake you can use the `ccmake` ncurses GUI to set options. Some package systems like Ubuntu and other distributions based on Debian GNU/Linux have this tool in a separate `cmake-curses-gui` package. @@ -225,19 +225,13 @@ need to be exported by the EXE to be detected by the driver, so the override will not work if GLFW is built as a DLL. -@subsubsection compile_options_egl EGL specific CMake options - -`GLFW_USE_EGL` determines whether to use EGL instead of the platform-specific -context creation API. Note that EGL is not yet provided on all supported -platforms. - - @section compile_manual Compiling GLFW manually If you wish to compile GLFW without its CMake build environment then you will have to do at least some of the platform detection yourself. GLFW needs -a number of configuration macros to be defined in order to know what it's being -compiled for and has many optional, platform-specific ones for various features. +a configuration macro to be defined in order to know what window system it's +being compiled for and also has optional, platform-specific ones for various +features. When building with CMake, the `glfw_config.h` configuration header is generated based on the current platform and CMake options. The GLFW CMake environment @@ -245,10 +239,6 @@ defines `_GLFW_USE_CONFIG_H`, which causes this header to be included by `internal.h`. Without this macro, GLFW will expect the necessary configuration macros to be defined on the command-line. -Three macros _must_ be defined when compiling GLFW: one selecting the window -creation API and one selecting the context creation API. Exactly one of each -kind must be defined for GLFW to compile and link. - The window creation API is used to create windows, handle input, monitors, gamma ramps and clipboard. The options are: @@ -258,24 +248,17 @@ ramps and clipboard. The options are: - `_GLFW_WAYLAND` to use the Wayland API (experimental and incomplete) - `_GLFW_MIR` to use the Mir API (experimental and incomplete) -The context creation API is used to enumerate pixel formats / framebuffer -configurations and to create contexts. The options are: - - - `_GLFW_NSGL` to use the Cocoa OpenGL framework - - `_GLFW_WGL` to use the Win32 WGL API - - `_GLFW_GLX` to use the X11 GLX API - - `_GLFW_EGL` to use the EGL API - -Wayland and Mir both require the EGL backend. - If you are building GLFW as a shared library / dynamic library / DLL then you must also define `_GLFW_BUILD_DLL`. Otherwise, you must not define it. +For the EGL context creation API, the following options are available: + + - `_GLFW_USE_EGLPLATFORM_H` to use `EGL/eglplatform.h` for native handle + definitions (fallback) + If you are using the X11 window creation API, support for the following X11 extensions can be enabled: - - `_GLFW_HAS_XINPUT` to use XInput2 for high-resolution cursor motion - (recommended) - `_GLFW_HAS_XF86VM` to use Xxf86vm as a fallback when RandR gamma is broken (recommended) @@ -289,12 +272,6 @@ available: - `_GLFW_USE_RETINA` to have windows use the full resolution of Retina displays (recommended) -If you are using the EGL context creation API, the following options are -available: - - - `_GLFW_USE_EGLPLATFORM_H` to use `EGL/eglplatform.h` for native handle - definitions (fallback) - @note None of the @ref build_macros may be defined during the compilation of GLFW. If you define any of these in your build files, make sure they are not applied to the GLFW sources. diff --git a/docs/context.dox b/docs/context.dox index 57aed221a..7cc1f120c 100644 --- a/docs/context.dox +++ b/docs/context.dox @@ -1,16 +1,19 @@ /*! -@page context Context guide +@page context_guide Context guide @tableofcontents This guide introduces the OpenGL and OpenGL ES context related functions of -GLFW. There are also guides for the other areas of the GLFW API. +GLFW. For details on a specific function, see the +[reference documentation](@ref context). There are also guides for the other +areas of the GLFW API. - - @ref intro - - @ref window - - @ref monitor - - @ref input + - @ref intro_guide + - @ref window_guide + - @ref vulkan_guide + - @ref monitor_guide + - @ref input_guide @section context_object Context objects @@ -29,7 +32,7 @@ the `glfwinfo` test program. @note Vulkan does not have a context and the Vulkan instance is created via the Vulkan API itself. If you will be using Vulkan to render to a window, disable context creation by setting the [GLFW_CLIENT_API](@ref window_hints_ctx) hint to -`GLFW_NO_API`. For more information, see the @ref vulkan. +`GLFW_NO_API`. For more information, see the @ref vulkan_guide. @subsection context_hints Context creation hints @@ -174,9 +177,10 @@ python main.py --generator c --no-loader --out-path output @endcode The `--no-loader` option is added because GLFW already provides a function for -loading OpenGL and OpenGL ES function pointers and glad can call this instead of -having to implement its own. There are several other command-line options as -well. See the glad documentation for details. +loading OpenGL and OpenGL ES function pointers, one that automatically uses the +selected context creation API, and glad can call this instead of having to +implement its own. There are several other command-line options as well. See +the glad documentation for details. Add the generated `output/src/glad.c`, `output/include/glad/glad.h` and `output/include/KHR/khrplatform.h` files to your build. Then you need to diff --git a/docs/input.dox b/docs/input.dox index e36770ade..7ac3f2292 100644 --- a/docs/input.dox +++ b/docs/input.dox @@ -1,16 +1,17 @@ /*! -@page input Input guide +@page input_guide Input guide @tableofcontents -This guide introduces the input related functions of GLFW. There are also -guides for the other areas of GLFW. +This guide introduces the input related functions of GLFW. For details on +a specific function, see the [reference documentation](@ref input). There are +also guides for the other areas of GLFW. - - @ref intro - - @ref window - - @ref context - - @ref monitor + - @ref intro_guide + - @ref window_guide + - @ref context_guide + - @ref monitor_guide GLFW provides many kinds of input. While some can only be polled, like time, or only received via callbacks, like scrolling, there are those that provide both @@ -32,8 +33,10 @@ information. GLFW needs to communicate regularly with the window system both in order to receive events and to show that the application hasn't locked up. Event -processing must be done regularly while you have visible windows and is normally -done each frame after [buffer swapping](@ref buffer_swap). +processing must be done regularly while you have any windows and is normally +done each frame after [buffer swapping](@ref buffer_swap). Even when you have +no windows, event polling needs to be done in order to receive monitor +connection events. There are two functions for processing pending events. @ref glfwPollEvents, processes only those events that have already been received and then returns @@ -57,6 +60,17 @@ processes all received events. This saves a great deal of CPU cycles and is useful for, for example, editing tools. There must be at least one GLFW window for this function to sleep. +If you want to wait for events but have UI elements that need periodic updates, +call @ref glfwWaitEventsTimeout. + +@code +glfwWaitEventsTimeout(0.7); +@endcode + +It puts the thread to sleep until at least one event has been received, or until +the specified number of seconds have elapsed. It then processes any received +events. + If the main thread is sleeping in @ref glfwWaitEvents, you can wake it from another thread by posting an empty event to the event queue with @ref glfwPostEmptyEvent. @@ -224,8 +238,9 @@ position callback. glfwSetCursorPosCallback(window, cursor_pos_callback); @endcode -The callback functions receives the cursor position. On platforms that provide -it, the full sub-pixel cursor position is passed on. +The callback functions receives the cursor position, measured in screen +coordinates but relative to the top-left corner of the window client area. On +platforms that provide it, the full sub-pixel cursor position is passed on. @code static void cursor_position_callback(GLFWwindow* window, double xpos, double ypos) @@ -532,6 +547,33 @@ and make may have the same name. Only the [joystick token](@ref joysticks) is guaranteed to be unique, and only until that joystick is disconnected. +@subsection joystick_event Joystick configuration changes + +If you wish to be notified when a joystick is connected or disconnected, set +a joystick callback. + +@code +glfwSetJoystickCallback(joystick_callback); +@endcode + +The callback function receives the ID of the joystick that has been connected +and disconnected and the event that occurred. + +@code +void joystick_callback(int joy, int event) +{ + if (event == GLFW_CONNECTED) + { + // The joystick was connected + } + else if (event == GLFW_DISCONNECTED) + { + // The joystick was disconnected + } +} +@endcode + + @section time Time input GLFW provides high-resolution time input, in seconds, with @ref glfwGetTime. @@ -552,6 +594,21 @@ glfwSetTime(4.0); This sets the timer to the specified time, in seconds. +You can also access the raw timer value, measured in 1 / frequency +seconds, with @ref glfwGetTimerValue. + +@code +uint64_t value = glfwGetTimerValue(); +@endcode + +The frequency of the raw timer varies depending on what time sources are +available on the machine. You can query its frequency, in Hz, with @ref +glfwGetTimerFrequency. + +@code +uint64_t freqency = glfwGetTimerFrequency(); +@endcode + @section clipboard Clipboard input and output diff --git a/docs/internal.dox b/docs/internal.dox index 0389af6ec..593ad67d3 100644 --- a/docs/internal.dox +++ b/docs/internal.dox @@ -1,6 +1,6 @@ /*! -@page internals Internal structure +@page internals_guide Internal structure @tableofcontents diff --git a/docs/intro.dox b/docs/intro.dox index cb3bdbc94..6f0a22919 100644 --- a/docs/intro.dox +++ b/docs/intro.dox @@ -1,18 +1,21 @@ /*! -@page intro Introduction to the API +@page intro_guide Introduction to the API @tableofcontents -This guide introduces the basic concepts of GLFW and describes initialization, +This guide introduces the basic concepts of GLFW and describes initialization reference error handling and API guarantees and limitations. For a broad but shallow -tutorial, see @ref quick instead. There are also guides for the other areas of -GLFW. +tutorial, see @ref quick_guide instead. For details on a specific function, see the +[reference documentation](@ref init). - - @ref window - - @ref context - - @ref monitor - - @ref input +There are also guides for the other areas of GLFW. + + - @ref window_guide + - @ref context_guide + - @ref vulkan_guide + - @ref monitor_guide + - @ref input_guide @section intro_init Initialization and termination @@ -194,6 +197,7 @@ function: - @ref glfwDestroyCursor - @ref glfwPollEvents - @ref glfwWaitEvents + - @ref glfwWaitEventsTimeout - @ref glfwTerminate These functions may be made reentrant in future minor or patch releases, but @@ -239,8 +243,15 @@ may be called from any thread: - @ref glfwExtensionSupported - @ref glfwGetProcAddress -The timer may be accessed from any thread, but this is not synchronized by GLFW. -The following timer related functions may be called from any thread: +The raw timer may be queried from any thread. The following raw timer related +functions may be called from any thread: + + - @ref glfwGetTimerFrequency + - @ref glfwGetTimerValue + +The regular timer may be queried from any thread, but this is not synchronized +by GLFW with calls to @ref glfwSetTime. The following timer related functions +may be called from any thread: - @ref glfwGetTime @@ -250,13 +261,22 @@ version related functions may be called from any thread: - @ref glfwGetVersion - @ref glfwGetVersionString +Vulkan objects may be created and information queried from any thread. The +following Vulkan related functions may be called from any thread: + + - @ref glfwVulkanSupported + - @ref glfwGetRequiredInstanceExtensions + - @ref glfwGetInstanceProcAddress + - @ref glfwGetPhysicalDevicePresentationSupport + - @ref glfwCreateWindowSurface + GLFW uses no synchronization objects internally except for thread-local storage to keep track of the current context for each thread. Synchronization is left to the application. Functions that may currently be called from any thread will always remain so, -but functions that are currently limited to the main may be updated to allow -calls from any thread in future releases. +but functions that are currently limited to the main thread may be updated to +allow calls from any thread in future releases. @subsection compatibility Version compatibility diff --git a/docs/main.dox b/docs/main.dox index 9b59822e5..9f9ef7977 100644 --- a/docs/main.dox +++ b/docs/main.dox @@ -11,36 +11,36 @@ for creating windows, contexts and surfaces, reading input, handling events, etc See @ref news_32 for release highlights or the [version history](http://www.glfw.org/changelog.html) for details. -@ref quick is a guide for those new to GLFW. It takes you through how to write -a small but complete program. For people coming from GLFW 2, the @ref moving -guide explains what has changed and how to update existing code to use the new -API. +@ref quick_guide is a guide for users new to GLFW. It takes you through how to +write a small but complete program. -There are guides for each of the various areas of the API. +There are guides for each section of the API: - - @ref intro – initialization, error handling and high-level design - - @ref window – creating and working with windows and framebuffers - - @ref context – working with OpenGL and OpenGL ES contexts - - @ref monitor – enumerating and working with monitors and video modes - - @ref input – receiving events, polling and processing input + - @ref intro_guide – initialization, error handling and high-level design + - @ref window_guide – creating and working with windows and framebuffers + - @ref context_guide – working with OpenGL and OpenGL ES contexts + - @ref vulkan_guide - working with Vulkan objects and extensions + - @ref monitor_guide – enumerating and working with monitors and video modes + - @ref input_guide – receiving events, polling and processing input -Once you have written a program, see the @ref compile and @ref build guides. +Once you have written a program, see @ref compile_guide and @ref build_guide. The [reference documentation](modules.html) provides more detailed information about specific functions. +@ref moving_guide explains what has changed and how to update existing code to +use the new API. + There is a section on @ref guarantees_limitations for pointer lifetimes, reentrancy, thread safety, event order and backward and forward compatibility. -The @ref vulkan guide fills in the gaps for how to use Vulkan with GLFW. - -The @ref rift fills in the gaps for how to use LibOVR with GLFW. +@ref rift_guide fills in the gaps for how to use LibOVR with GLFW. The [FAQ](http://www.glfw.org/faq.html) answers many common questions about the design, implementation and use of GLFW. -Finally, the @ref compat guide explains what APIs, standards and protocols GLFW -uses and what happens when they are not present on a given machine. +Finally, @ref compat_guide explains what APIs, standards and protocols GLFW uses +and what happens when they are not present on a given machine. This documentation was generated with Doxygen. The sources for it are available in both the [source distribution](http://www.glfw.org/download.html) and diff --git a/docs/monitor.dox b/docs/monitor.dox index 3b3c843c1..53e88aac4 100644 --- a/docs/monitor.dox +++ b/docs/monitor.dox @@ -1,16 +1,17 @@ /*! -@page monitor Monitor guide +@page monitor_guide Monitor guide @tableofcontents -This guide introduces the monitor related functions of GLFW. There are also -guides for the other areas of GLFW. +This guide introduces the monitor related functions of GLFW. For details on +a specific function, see the [reference documentation](@ref monitor). There are +also guides for the other areas of GLFW. - - @ref intro - - @ref window - - @ref context - - @ref input + - @ref intro_guide + - @ref window_guide + - @ref context_guide + - @ref input_guide @section monitor_object Monitor objects @@ -94,8 +95,9 @@ a gamma ramp. @subsection monitor_modes Video modes GLFW generally does a good job selecting a suitable video mode when you create -a full screen window, but it is sometimes useful to know exactly which video -modes are supported. +a full screen window, change its video mode or or make a windowed one full +screen, but it is sometimes useful to know exactly which video modes are +supported. Video modes are represented as @ref GLFWvidmode structures. You can get an array of the video modes supported by a monitor with @ref glfwGetVideoModes. diff --git a/docs/moving.dox b/docs/moving.dox index a222a3572..7baab6f19 100644 --- a/docs/moving.dox +++ b/docs/moving.dox @@ -1,6 +1,6 @@ /*! -@page moving Moving from GLFW 2 to 3 +@page moving_guide Moving from GLFW 2 to 3 @tableofcontents @@ -130,7 +130,7 @@ GLFW 3 provides support for multiple monitors. To request a full screen mode wi instead of passing `GLFW_FULLSCREEN` you specify which monitor you wish the window to use. The @ref glfwGetPrimaryMonitor function returns the monitor that GLFW 2 would have selected, but there are many other -[monitor functions](@ref monitor). Monitor handles are pointers to the +[monitor functions](@ref monitor_guide). Monitor handles are pointers to the [opaque](https://en.wikipedia.org/wiki/Opaque_data_type) type @ref GLFWmonitor. @par Old basic full screen diff --git a/docs/news.dox b/docs/news.dox index 0ccb4c89d..e0fb5f80c 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -26,6 +26,47 @@ Vulkan header inclusion can be selected with [GLFW_INCLUDE_VULKAN](@ref build_macros). +@subsection news_32_setwindowmonitor Window mode switching + +GLFW now supports switching between windowed and full screen modes and updating +the monitor and desired resolution and refresh rate of full screen windows with +@ref glfwSetWindowMonitor. + + +@subsection news_32_maximize Window maxmimization support + +GLFW now supports window maximization with @ref glfwMaximizeWindow and the +[GLFW_MAXIMIZED](@ref window_attribs_wnd) window hint and attribute. + + +@subsection news_32_icon Window icon support + +GLFW now supports setting the icon of windows with @ref glfwSetWindowIcon. + + +@subsection news_32_focus Window input focus control + +GLFW now supports giving windows input focus with @ref glfwFocusWindow. + + +@subsection news_32_timer Raw timer access + +GLFW now supports raw timer values with @ref glfwGetTimerValue and @ref +glfwGetTimerFrequency. + + +@subsection news_32_waittimeout Wait for events with timeout + +GLFW now supports waiting for events for a set amount of time with @ref +glfwWaitEventsTimeout. + + +@subsection news_32_contextapi Run-time context creation API selection + +GLFW now supports selecting the context creation API at run-time with +[GLFW_CONTEXT_CREATION_API](@ref window_hints_ctx). + + @section news_31 New features in 3.1 These are the release highlights. For a full list of changes see the @@ -96,7 +137,7 @@ and the cursor position directly instead of returning cached data. @subsection news_31_libovr Better interoperability with Oculus Rift GLFW now provides native access functions for the OS level handles corresponding -to monitor objects, as well as a [brief guide](@ref rift). It is also regularly +to monitor objects, as well as the @ref rift_guide. It is also regularly tested for compatibility with the latest version of LibOVR (0.4.4 on release). diff --git a/docs/quick.dox b/docs/quick.dox index 543932724..dccc834d9 100644 --- a/docs/quick.dox +++ b/docs/quick.dox @@ -1,6 +1,6 @@ /*! -@page quick Getting started +@page quick_guide Getting started @tableofcontents @@ -10,7 +10,7 @@ and exit when the user closes the window or presses _Escape_. This guide will introduce a few of the most commonly used functions, but there are many more. This guide assumes no experience with earlier versions of GLFW. If you -have used GLFW 2 in the past, read the @ref moving guide, as some functions +have used GLFW 2 in the past, read @ref moving_guide, as some functions behave differently in GLFW 3. @@ -346,11 +346,11 @@ This tutorial used only a few of the many functions GLFW provides. There are guides for each of the areas covered by GLFW. Each guide will introduce all the functions for that category. - - @ref intro - - @ref window - - @ref context - - @ref monitor - - @ref input + - @ref intro_guide + - @ref window_guide + - @ref context_guide + - @ref monitor_guide + - @ref input_guide You can access reference documentation for any GLFW function by clicking it and the reference for each function links to related functions and guide sections. @@ -359,6 +359,6 @@ The tutorial ends here. Once you have written a program that uses GLFW, you will need to compile and link it. How to do that depends on the development environment you are using and is best explained by the documentation for that environment. To learn about the details that are specific to GLFW, see -@ref build. +@ref build_guide. */ diff --git a/docs/rift.dox b/docs/rift.dox index edf206f76..2de395fc8 100644 --- a/docs/rift.dox +++ b/docs/rift.dox @@ -1,6 +1,6 @@ /*! -@page rift Oculus Rift guide +@page rift_guide Using GLFW with LibOVR @tableofcontents diff --git a/docs/vulkan.dox b/docs/vulkan.dox index 7cd25abc2..988c2b84b 100644 --- a/docs/vulkan.dox +++ b/docs/vulkan.dox @@ -1,185 +1,212 @@ -/*! - -@page vulkan Vulkan guide - -@tableofcontents - -This guide is intended to fill the gaps between the Vulkan documentation and the -rest of the GLFW documentation and is not a replacement for either. - -To develop for Vulkan you should install an SDK for your platform, for example -the [LunarG Vulkan SDK](http://lunarg.com/vulkan-sdk/). Apart from the headers -and libraries, it also provides the validation layers necessary for development. - -GLFW itself does not need a Vulkan SDK to enable support for Vulkan. However, -any Vulkan-specific test and example programs are built only if the CMake files -find the LunarG SDK. - - -@section vulkan_include Including the Vulkan and GLFW header files - -To include the Vulkan header, define `GLFW_INCLUDE_VULKAN` before including the -GLFW header. - -@code -#define GLFW_INCLUDE_VULKAN -#include -@endcode - -If you want to include the Vulkan header from a custom location or use your own -custom Vulkan header then you need to include them before the GLFW header. - -@code -#include -#include -@endcode - -Unless a Vulkan header is included, either by the GLFW header or above it, any -GLFW functions that use Vulkan types will not be declared. - -The `VK_USE_PLATFORM_*_KHR` macros do not need to be defined for the Vulkan part -of GLFW to work. - - -@section vulkan_support Querying for Vulkan support - -If you are loading the Vulkan loader dynamically instead of linking directly -against it, you can check for the availability of a loader with @ref -glfwVulkanSupported. - -@code -if (glfwVulkanSupported()) -{ - // Vulkan is available, at least for compute -} -@endcode - -This function returns `GLFW_TRUE` if the Vulkan loader was found. This check is -performed by @ref glfwInit. - -If no loader was found, calling any other Vulkan related GLFW function will -generate a @ref GLFW_API_UNAVAILABLE error. - - -@subsection vulkan_proc Querying Vulkan function pointers - -To load any Vulkan core or extension function from the found loader, call @ref -glfwGetInstanceProcAddress. - -@code -PFN_vkCreateDevice pfnCreateDevice = (PFN_vkCreateDevice) - glfwGetInstanceProcAddress(instance, "vkCreateDevice"); -@endcode - -This is equivalent to calling `vkGetInstanceProcAddr`. If that fails, the -function falls back to a platform-specific query of the Vulkan loader (i.e. -`dlsym` or `GetProcAddress`). If that also fails, the function returns `NULL`. -For more information about `vkGetInstanceProcAddr`, see the Vulkan -documentation. - -Vulkan also provides `vkGetDeviceProcAddr` for loading device-specific versions -of Vulkan function. This function can be retrieved from an instance with @ref -glfwGetInstanceProcAddress. - -@code -PFN_vkGetDeviceProcAddr pfnGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr) - glfwGetInstanceProcAddress(instance, "vkGetDeviceProcAddr"); -@endcode - -Device-specific functions may execute a little bit faster, due to not having to -dispatch internally based on the device passed to them. For more information -about `vkGetDeviceProcAddr`, see the Vulkan documentation. - - -@section vulkan_ext Querying required Vulkan extensions - -To do anything useful with Vulkan you need to create an instance. If you want -to use Vulkan to render to a window, you must enable the instance extensions -GLFW requires to create Vulkan surfaces. - -To query the instance extensions required, call @ref -glfwGetRequiredInstanceExtensions. - -@code -int count; -const char** extensions = glfwGetRequiredInstanceExtensions(&count); -@endcode - -These extensions must all be enabled when creating instances that are going to -be passed to @ref glfwGetPhysicalDevicePresentationSupport and @ref -glfwCreateWindowSurface. The set of extensions will vary depending on platform -and may also vary depending on graphics drivers and other factors. - -If it fails it will return `NULL` and Vulkan window surface creation will not be -possible. You may still use Vulkan for off-screen rendering and compute work. - -The returned array will always contain `VK_KHR_surface`, so if you don't -require any additional extensions you can pass this list directly to the -`VkInstanceCreateInfo` struct. - -@code -VkInstanceCreateInfo ici; - -memset(&ici, 0, sizeof(ici)); -ici.enabledExtensionCount = count; -ici.ppEnabledExtensionNames = extensions; -... -@endcode - -Additional extensions may be required by future versions of GLFW. You should -check whether any extensions you wish to enable are already in the returned -array, as it is an error to specify an extension more than once in the -`VkInstanceCreateInfo` struct. - - -@section vulkan_present Querying for Vulkan presentation support - -Not every Vulkan queue family of every device can present images to surfaces. -To check whether a specific queue family of a physical device supports image -presentation without first having to create a window and surface, call @ref -glfwGetPhysicalDevicePresentationSupport. - -@code -if (glfwGetPhysicalDevicePresentationSupport(instance, physical_device, queue_family_index)) -{ - // Queue family supports image presentation -} -@endcode - -The `VK_KHR_surface` extension also provides the -`vkGetPhysicalDeviceSurfaceSupportKHR` function, which performs the same test on -an existing Vulkan surface. - - -@section vulkan_window Creating the window - -Unless you will be using OpenGL or OpenGL ES in addition to Vulkan, there is no -need to create a context for that window. You can disable context creation by -setting the [GLFW_CLIENT_API](@ref window_hints_ctx) hint to `GLFW_NO_API`. - -@code -glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); -GLFWwindow* window = glfwCreateWindow(640, 480, "Window Title", NULL, NULL); -@endcode - -See @ref context_less for more information. - - -@section vulkan_surface Creating a Vulkan window surface - -You can create a Vulkan surface (as defined by the `VK_KHR_surface` extension) -for a GLFW window with @ref glfwCreateWindowSurface. - -@code -VkSurfaceKHR surface; -VkResult err = glfwCreateWindowSurface(instance, window, NULL, &surface); -if (err) -{ - // Window surface creation failed -} -@endcode - -It is your responsibility to destroy the surface. GLFW does not destroy it for -you. Call `vkDestroySurfaceKHR` function from the same extension to destroy it. - -*/ +/*! + +@page vulkan_guide Vulkan guide + +@tableofcontents + +This guide is intended to fill the gaps between the [Vulkan +documentation](https://www.khronos.org/vulkan/) and the rest of the GLFW +documentation and is not a replacement for either. It assumes some familiarity +with Vulkan concepts like loaders, devices, queues and surfaces and leaves it to +the Vulkan documentation to explain the details of Vulkan functions. + +To develop for Vulkan you should install an SDK for your platform, for example +the [LunarG Vulkan SDK](https://vulkan.lunarg.com/). Apart from the headers and +libraries, it also provides the validation layers necessary for development. + +The GLFW library does not need the Vulkan SDK to enable support for Vulkan. +However, any Vulkan-specific test and example programs are built only if the +CMake files find a Vulkan SDK. + +For details on a specific function, see the +[reference documentation](@ref vulkan). There are also guides for the other +areas of the GLFW API. + + - @ref intro_guide + - @ref window_guide + - @ref context_guide + - @ref monitor_guide + - @ref input_guide + + +@section vulkan_include Including the Vulkan and GLFW header files + +To include the Vulkan header, define [GLFW_INCLUDE_VULKAN](@ref build_macros) +before including the GLFW header. + +@code +#define GLFW_INCLUDE_VULKAN +#include +@endcode + +If you want to include the Vulkan header from a custom location or use your own +custom Vulkan header then you need to include them before the GLFW header. + +@code +#include +#include +@endcode + +Unless a Vulkan header is included, either by the GLFW header or above it, any +GLFW functions that take or return Vulkan types will not be declared. + +The `VK_USE_PLATFORM_*_KHR` macros do not need to be defined for the Vulkan part +of GLFW to work. Define them only if you are using these extensions directly. + + +@section vulkan_support Querying for Vulkan support + +If you are linking directly against the Vulkan loader then you can skip this +section. The canonical desktop loader library exports all Vulkan core and +Khronos extension functions, allowing them to be called directly. + +If you are loading the Vulkan loader dynamically instead of linking directly +against it, you can check for the availability of a loader with @ref +glfwVulkanSupported. + +@code +if (glfwVulkanSupported()) +{ + // Vulkan is available, at least for compute +} +@endcode + +This function returns `GLFW_TRUE` if the Vulkan loader was found. This check is +performed by @ref glfwInit. + +If no loader was found, calling any other Vulkan related GLFW function will +generate a @ref GLFW_API_UNAVAILABLE error. + + +@subsection vulkan_proc Querying Vulkan function pointers + +To load any Vulkan core or extension function from the found loader, call @ref +glfwGetInstanceProcAddress. To load functions needed for instance creation, +pass `NULL` as the instance. + +@code +PFN_vkCreateInstance pfnCreateInstance = (PFN_vkCreateInstance) + glfwGetInstanceProcAddress(NULL, "vkCreateInstance"); +@endcode + +Once you have created an instance, you can load from it all other Vulkan core +functions and functions from any instance extensions you enabled. + +@code +PFN_vkCreateDevice pfnCreateDevice = (PFN_vkCreateDevice) + glfwGetInstanceProcAddress(instance, "vkCreateDevice"); +@endcode + +This function in turn calls `vkGetInstanceProcAddr`. If that fails, the +function falls back to a platform-specific query of the Vulkan loader (i.e. +`dlsym` or `GetProcAddress`). If that also fails, the function returns `NULL`. +For more information about `vkGetInstanceProcAddr`, see the Vulkan +documentation. + +Vulkan also provides `vkGetDeviceProcAddr` for loading device-specific versions +of Vulkan function. This function can be retrieved from an instance with @ref +glfwGetInstanceProcAddress. + +@code +PFN_vkGetDeviceProcAddr pfnGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr) + glfwGetInstanceProcAddress(instance, "vkGetDeviceProcAddr"); +@endcode + +Device-specific functions may execute a little bit faster, due to not having to +dispatch internally based on the device passed to them. For more information +about `vkGetDeviceProcAddr`, see the Vulkan documentation. + + +@section vulkan_ext Querying required Vulkan extensions + +To do anything useful with Vulkan you need to create an instance. If you want +to use Vulkan to render to a window, you must enable the instance extensions +GLFW requires to create Vulkan surfaces. + +To query the instance extensions required, call @ref +glfwGetRequiredInstanceExtensions. + +@code +uint32_t count; +const char** extensions = glfwGetRequiredInstanceExtensions(&count); +@endcode + +These extensions must all be enabled when creating instances that are going to +be passed to @ref glfwGetPhysicalDevicePresentationSupport and @ref +glfwCreateWindowSurface. The set of extensions will vary depending on platform +and may also vary depending on graphics drivers and other factors. + +If it fails it will return `NULL` and GLFW will not be able to create Vulkan +window surfaces. You can still use Vulkan for off-screen rendering and compute +work. + +The returned array will always contain `VK_KHR_surface`, so if you don't +require any additional extensions you can pass this list directly to the +`VkInstanceCreateInfo` struct. + +@code +VkInstanceCreateInfo ici; + +memset(&ici, 0, sizeof(ici)); +ici.enabledExtensionCount = count; +ici.ppEnabledExtensionNames = extensions; +... +@endcode + +Additional extensions may be required by future versions of GLFW. You should +check whether any extensions you wish to enable are already in the returned +array, as it is an error to specify an extension more than once in the +`VkInstanceCreateInfo` struct. + + +@section vulkan_present Querying for Vulkan presentation support + +Not every queue family of every Vulkan device can present images to surfaces. +To check whether a specific queue family of a physical device supports image +presentation without first having to create a window and surface, call @ref +glfwGetPhysicalDevicePresentationSupport. + +@code +if (glfwGetPhysicalDevicePresentationSupport(instance, physical_device, queue_family_index)) +{ + // Queue family supports image presentation +} +@endcode + +The `VK_KHR_surface` extension additionally provides the +`vkGetPhysicalDeviceSurfaceSupportKHR` function, which performs the same test on +an existing Vulkan surface. + + +@section vulkan_window Creating the window + +Unless you will be using OpenGL or OpenGL ES with the same window as Vulkan, +there is no need to create a context. You can disable context creation with the +[GLFW_CLIENT_API](@ref window_hints_ctx) hint. + +@code +glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); +GLFWwindow* window = glfwCreateWindow(640, 480, "Window Title", NULL, NULL); +@endcode + +See @ref context_less for more information. + + +@section vulkan_surface Creating a Vulkan window surface + +You can create a Vulkan surface (as defined by the `VK_KHR_surface` extension) +for a GLFW window with @ref glfwCreateWindowSurface. + +@code +VkSurfaceKHR surface; +VkResult err = glfwCreateWindowSurface(instance, window, NULL, &surface); +if (err) +{ + // Window surface creation failed +} +@endcode + +It is your responsibility to destroy the surface. GLFW does not destroy it for +you. Call `vkDestroySurfaceKHR` function from the same extension to destroy it. + +*/ diff --git a/docs/window.dox b/docs/window.dox index 876927c7c..b66874bb9 100644 --- a/docs/window.dox +++ b/docs/window.dox @@ -1,16 +1,17 @@ /*! -@page window Window guide +@page window_guide Window guide @tableofcontents -This guide introduces the window related functions of GLFW. There are also -guides for the other areas of GLFW. +This guide introduces the window related functions of GLFW. For details on +a specific function, see the [reference documentation](@ref window). There are +also guides for the other areas of GLFW. - - @ref intro - - @ref context - - @ref monitor - - @ref input + - @ref intro_guide + - @ref context_guide + - @ref monitor_guide + - @ref input_guide @section window_object Window objects @@ -55,6 +56,10 @@ GLFWwindow* window = glfwCreateWindow(640, 480, "My Title", glfwGetPrimaryMonito Full screen windows cover the entire display area of a monitor, have no border or decorations. +Windowed mode windows can be made full screen by setting a monitor with @ref +glfwSetWindowMonitor, and full screen ones can be made windowed by unsetting it +with the same function. + Each field of the @ref GLFWvidmode structure corresponds to a function parameter or window hint and combine to form the _desired video mode_ for that window. The supported video mode most closely matching the desired video mode will be @@ -70,9 +75,11 @@ GLFWvidmode.greenBits | `GLFW_GREEN_BITS` hint GLFWvidmode.blueBits | `GLFW_BLUE_BITS` hint GLFWvidmode.refreshRate | `GLFW_REFRESH_RATE` hint -Once you have a full screen window, you can change its resolution with @ref -glfwSetWindowSize. The new video mode will be selected and set the same way as -the video mode chosen by @ref glfwCreateWindow. +Once you have a full screen window, you can change its resolution, refresh rate +and monitor with @ref glfwSetWindowMonitor. If you just need change its +resolution you can also call @ref glfwSetWindowSize. In all cases, the new +video mode will be selected the same way as the video mode chosen by @ref +glfwCreateWindow. By default, the original video mode of the monitor will be restored and the window iconified if it loses input focus, to allow the user to switch back to @@ -100,6 +107,18 @@ glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate); GLFWwindow* window = glfwCreateWindow(mode->width, mode->height, "My Title", monitor, NULL); @endcode +This also works for windowed mode windows that are made full screen. + +@code +const GLFWvidmode* mode = glfwGetVideoMode(monitor); + +glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate); +@endcode + +Note that @ref glfwGetVideoMode returns the _current_ video mode of a monitor, +so if you already have a full screen window on that monitor that you want to +make windowed full screen, you need to have saved the desktop resolution before. + @subsection window_destruction Window destruction @@ -140,6 +159,7 @@ The following hints are always hard constraints: - `GLFW_STEREO` - `GLFW_DOUBLEBUFFER` - `GLFW_CLIENT_API` +- `GLFW_CONTEXT_CREATION_API` The following additional hints are hard constraints when requesting an OpenGL context, but are ignored when requesting an OpenGL ES context: @@ -174,6 +194,9 @@ above other regular windows, also called topmost or always-on-top. This is intended primarily for debugging purposes and cannot be used to implement proper full screen windows. This hint is ignored for full screen windows. +`GLFW_MAXIMIZED` specifies whether the windowed mode window will be maximized +when created. This hint is ignored for full screen windows. + @subsubsection window_hints_fb Framebuffer related hints @@ -206,8 +229,9 @@ Zero disables multisampling. `GLFW_DONT_CARE` means the application has no preference. `GLFW_SRGB_CAPABLE` specifies whether the framebuffer should be sRGB capable. -If supported, the created context will provide `GL_ARB_framebuffer_sRGB` or -`GL_EXT_framebuffer_sRGB`. +If supported, a created OpenGL context will support the `GL_FRAMEBUFFER_SRGB` +enable, also called `GL_FRAMEBUFFER_SRGB_EXT`) for controlling sRGB rendering +and a created OpenGL ES context will always have sRGB rendering enabled. `GLFW_DOUBLEBUFFER` specifies whether the framebuffer should be double buffered. You nearly always want to use double buffering. This is a hard constraint. @@ -226,6 +250,24 @@ This hint is ignored for windowed mode windows. Possible values are `GLFW_OPENGL_API`, `GLFW_OPENGL_ES_API` and `GLFW_NO_API`. This is a hard constraint. +`GLFW_CONTEXT_CREATION_API` specifies which context creation API to use to +create the context. Possible values are `GLFW_NATIVE_CONTEXT_API` and +`GLFW_EGL_CONTEXT_API`. This is a hard constraint. If no client API is +requested, this hint is ignored. + +@par +__OS X:__ The EGL API is not available on this platform and requests to use it +will fail. + +@par +__Wayland, Mir:__ The EGL API _is_ the native context creation API, so this hint +will have no effect. + +@note An OpenGL extension loader library that assumes it knows which context +creation API is used on a given platform may fail if you change this hint. This +can be resolved by having it load via @ref glfwGetProcAddress, which always uses +the selected API. + `GLFW_CONTEXT_VERSION_MAJOR` and `GLFW_CONTEXT_VERSION_MINOR` specify the client API version that the created context must be compatible with. The exact behavior of these hints depend on the requested client API. @@ -321,6 +363,7 @@ Window hint | Default value | Supported values `GLFW_FOCUSED` | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` `GLFW_AUTO_ICONIFY` | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` `GLFW_FLOATING` | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` +`GLFW_MAXIMIZED` | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` `GLFW_RED_BITS` | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` `GLFW_GREEN_BITS` | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` `GLFW_BLUE_BITS` | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` @@ -338,6 +381,7 @@ Window hint | Default value | Supported values `GLFW_SRGB_CAPABLE` | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` `GLFW_DOUBLEBUFFER` | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` `GLFW_CLIENT_API` | `GLFW_OPENGL_API` | `GLFW_OPENGL_API`, `GLFW_OPENGL_ES_API` or `GLFW_NO_API` +`GLFW_CONTEXT_CREATION_API` | `GLFW_NATIVE_CONTEXT_API` | `GLFW_NATIVE_CONTEXT_API` or `GLFW_EGL_CONTEXT_API` `GLFW_CONTEXT_VERSION_MAJOR` | 1 | Any valid major version number of the chosen client API `GLFW_CONTEXT_VERSION_MINOR` | 0 | Any valid minor version number of the chosen client API `GLFW_CONTEXT_ROBUSTNESS` | `GLFW_NO_ROBUSTNESS` | `GLFW_NO_ROBUSTNESS`, `GLFW_NO_RESET_NOTIFICATION` or `GLFW_LOSE_CONTEXT_ON_RESET` @@ -417,7 +461,7 @@ glfwSetWindowSize(window, 640, 480); @endcode For full screen windows, the specified size becomes the new resolution of the -window's *desired video mode*. The video mode most closely matching the new +window's desired video mode. The video mode most closely matching the new desired video mode is set immediately. The window is resized to fit the resolution of the set video mode. @@ -611,6 +655,26 @@ glfwSetWindowTitle(window, u8"This is always a UTF-8 string"); @endcode +@subsection window_icon Window icon + +Decorated windows have icons on some platforms. You can set this icon by +specifying a list of candidate images with @ref glfwSetWindowIcon. + +@code +GLFWimage images[2]; +images[0] = load_icon("my_icon.png"); +images[1] = load_icon("my_icon_small.png"); + +glfwSetWindowIcon(window, 2, images); +@endcode + +To revert to the default window icon, pass in an empty image array. + +@code +glfwSetWindowIcon(window, 0, NULL); +@endcode + + @subsection window_monitor Window monitor Full screen windows are associated with a specific monitor. You can get the @@ -622,8 +686,31 @@ GLFWmonitor* monitor = glfwGetWindowMonitor(window); This monitor handle is one of those returned by @ref glfwGetMonitors. -For windowed mode windows, this function returns `NULL`. This is the -recommended way to tell full screen windows from windowed mode windows. +For windowed mode windows, this function returns `NULL`. This is how to tell +full screen windows from windowed mode windows. + +You can move windows between monitors or between full screen and windowed mode +with @ref glfwSetWindowMonitor. When making a window full screen on the same or +on a different monitor, specify the desired monitor, resolution and refresh +rate. The position arguments are ignored. + +@code +const GLFWvidmode* mode = glfwGetVideoMode(monitor); + +glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate); +@endcode + +When making the window windowed, specify the desired position and size. The +refresh rate argument is ignored. + +@code +glfwSetWindowMonitor(window, NULL, xpos, ypos, width, height, 0); +@endcode + +This restores any previous window settings such as whether it is decorated, +floating, resizable, has size or aspect ratio limits, etc.. To restore a window +that was originally windowed to its original size and position, save these +before making it full screen and then pass them in as above. @subsection window_iconify Window iconification @@ -709,6 +796,13 @@ int visible = glfwGetWindowAttrib(window, GLFW_VISIBLE); @subsection window_focus Window input focus +Windows can be given input focus and brought to the front with @ref +glfwFocusWindow. + +@code +glfwFocusWindow(window); +@endcode + If you wish to be notified when a window gains or loses input focus, whether by the user, system or your own code, set a focus callback. @@ -789,6 +883,9 @@ same name. `GLFW_ICONIFIED` indicates whether the specified window is iconified, whether by the user or with @ref glfwIconifyWindow. +`GLFW_MAXIMIZED` indicates whether the specified window is maximized, whether by +the user or with @ref glfwMaximizeWindow. + `GLFW_VISIBLE` indicates whether the specified window is visible. Window visibility can be controlled with @ref glfwShowWindow and @ref glfwHideWindow and initial visibility is controlled by the [window hint](@ref window_hints_wnd) @@ -812,6 +909,10 @@ topmost or always-on-top. This is controlled by the `GLFW_CLIENT_API` indicates the client API provided by the window's context; either `GLFW_OPENGL_API`, `GLFW_OPENGL_ES_API` or `GLFW_NO_API`. +`GLFW_CONTEXT_CREATION_API` indicates the context creation API used to create +the window's context; either `GLFW_NATIVE_CONTEXT_API` or +`GLFW_EGL_CONTEXT_API`. + `GLFW_CONTEXT_VERSION_MAJOR`, `GLFW_CONTEXT_VERSION_MINOR` and `GLFW_CONTEXT_REVISION` indicate the client API version of the window's context. @@ -835,9 +936,9 @@ window's context supports robustness, or `GLFW_NO_ROBUSTNESS` otherwise. @subsubsection window_attribs_fb Framebuffer related attributes -The attributes of the default framebuffer (i.e. the framebuffer attached to the -window) are not provided by this function but can be queried with both OpenGL -and OpenGL ES. +GLFW does not expose attributes of the default framebuffer (i.e. the framebuffer +attached to the window) as these can be queried directly with either OpenGL, +OpenGL ES or Vulkan. If you are using version 3.0 or later of OpenGL or OpenGL ES, the `glGetFramebufferAttachmentParameteriv` function can be used to retrieve the @@ -859,8 +960,8 @@ Stencil bits | `GL_STENCIL_BITS` | `GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE` MSAA samples | `GL_SAMPLES` | _Not provided by this function_ When calling `glGetFramebufferAttachmentParameteriv`, the red, green, blue and -alpha sizes can be queried from the `GL_BACK_LEFT`, while the depth and stencil -sizes can be queried from the `GL_DEPTH` and `GL_STENCIL` attachments, +alpha sizes are queried from the `GL_BACK_LEFT`, while the depth and stencil +sizes are queried from the `GL_DEPTH` and `GL_STENCIL` attachments, respectively. diff --git a/examples/boing.c b/examples/boing.c index 886cf1f45..45c867fd1 100644 --- a/examples/boing.c +++ b/examples/boing.c @@ -89,6 +89,7 @@ typedef enum { DRAW_BALL, DRAW_BALL_SHADOW } DRAW_BALL_ENUM; typedef struct {float x; float y; float z;} vertex_t; /* Global vars */ +int windowed_xpos, windowed_ypos, windowed_width, windowed_height; int width, height; GLfloat deg_rot_y = 0.f; GLfloat deg_rot_y_inc = 2.f; @@ -236,8 +237,32 @@ void reshape( GLFWwindow* window, int w, int h ) void key_callback( GLFWwindow* window, int key, int scancode, int action, int mods ) { - if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + if (action != GLFW_PRESS) + return; + + if (key == GLFW_KEY_ESCAPE && mods == 0) glfwSetWindowShouldClose(window, GLFW_TRUE); + if ((key == GLFW_KEY_ENTER && mods == GLFW_MOD_ALT) || + (key == GLFW_KEY_F11 && mods == GLFW_MOD_ALT)) + { + if (glfwGetWindowMonitor(window)) + { + glfwSetWindowMonitor(window, NULL, + windowed_xpos, windowed_ypos, + windowed_width, windowed_height, 0); + } + else + { + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + if (monitor) + { + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + glfwGetWindowPos(window, &windowed_xpos, &windowed_ypos); + glfwGetWindowSize(window, &windowed_width, &windowed_height); + glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate); + } + } + } } static void set_ball_pos ( GLfloat x, GLfloat y ) @@ -602,8 +627,6 @@ int main( void ) if( !glfwInit() ) exit( EXIT_FAILURE ); - glfwWindowHint(GLFW_DEPTH_BITS, 16); - window = glfwCreateWindow( 400, 400, "Boing (classic Amiga demo)", NULL, NULL ); if (!window) { diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index b7aa193d3..ed7f11a6d 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -46,37 +46,37 @@ extern "C" { * * For more information about how to use this file, see @ref build_include. */ -/*! @defgroup context Context handling +/*! @defgroup context Context reference * - * This is the reference documentation for context related functions. For more - * information, see the @ref context. + * This is the reference documentation for OpenGL and OpenGL ES context related + * functions. For more task-oriented information, see the @ref context_guide. */ -/*! @defgroup vulkan Vulkan support +/*! @defgroup vulkan Vulkan reference * - * This is the reference documentation for Vulkan related functions. For more - * information, see the @ref vulkan. + * This is the reference documentation for Vulkan related functions and types. + * For more task-oriented information, see the @ref vulkan_guide. */ -/*! @defgroup init Initialization, version and errors +/*! @defgroup init Initialization, version and error reference * * This is the reference documentation for initialization and termination of - * the library, version management and error handling. For more information, - * see the @ref intro. + * the library, version management and error handling. For more task-oriented + * information, see the @ref intro_guide. */ -/*! @defgroup input Input handling +/*! @defgroup input Input reference * * This is the reference documentation for input related functions and types. - * For more information, see the @ref input. + * For more task-oriented information, see the @ref input_guide. */ -/*! @defgroup monitor Monitor handling +/*! @defgroup monitor Monitor reference * * This is the reference documentation for monitor related functions and types. - * For more information, see the @ref monitor. + * For more task-oriented information, see the @ref monitor_guide. */ -/*! @defgroup window Window handling +/*! @defgroup window Window reference * * This is the reference documentation for window related functions and types, - * including creation, deletion and event polling. For more information, see - * the @ref window. + * including creation, deletion and event polling. For more task-oriented + * information, see the @ref window_guide. */ @@ -117,14 +117,14 @@ extern "C" { /* Most Windows GLU headers need wchar_t. * The OS X OpenGL header blocks the definition of ptrdiff_t by glext.h. + * Include it unconditionally to avoid surprising side-effects. */ -#if !defined(GLFW_INCLUDE_NONE) - #include -#endif +#include +#include /* Include the chosen client API headers. */ -#if defined(__APPLE_CC__) +#if defined(__APPLE__) #if defined(GLFW_INCLUDE_GLCOREARB) #include #if defined(GLFW_INCLUDE_GLEXT) @@ -626,6 +626,7 @@ extern "C" { #define GLFW_DECORATED 0x00020005 #define GLFW_AUTO_ICONIFY 0x00020006 #define GLFW_FLOATING 0x00020007 +#define GLFW_MAXIMIZED 0x00020008 #define GLFW_RED_BITS 0x00021001 #define GLFW_GREEN_BITS 0x00021002 @@ -655,6 +656,7 @@ extern "C" { #define GLFW_OPENGL_PROFILE 0x00022008 #define GLFW_CONTEXT_RELEASE_BEHAVIOR 0x00022009 #define GLFW_CONTEXT_NO_ERROR 0x0002200A +#define GLFW_CONTEXT_CREATION_API 0x0002200B #define GLFW_NO_API 0 #define GLFW_OPENGL_API 0x00030001 @@ -680,6 +682,9 @@ extern "C" { #define GLFW_RELEASE_BEHAVIOR_FLUSH 0x00035001 #define GLFW_RELEASE_BEHAVIOR_NONE 0x00035002 +#define GLFW_NATIVE_CONTEXT_API 0x00036001 +#define GLFW_EGL_CONTEXT_API 0x00036002 + /*! @defgroup shapes Standard cursor shapes * * See [standard cursor creation](@ref cursor_standard) for how these are used. @@ -957,8 +962,10 @@ typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int); * This is the function signature for cursor position callback functions. * * @param[in] window The window that received the event. - * @param[in] xpos The new x-coordinate, in screen coordinates, of the cursor. - * @param[in] ypos The new y-coordinate, in screen coordinates, of the cursor. + * @param[in] xpos The new cursor x-coordinate, relative to the left edge of + * the client area. + * @param[in] ypos The new cursor y-coordinate, relative to the top edge of the + * client area. * * @sa @ref cursor_pos * @sa glfwSetCursorPosCallback @@ -1095,6 +1102,23 @@ typedef void (* GLFWdropfun)(GLFWwindow*,int,const char**); */ typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); +/*! @brief The function signature for joystick configuration callbacks. + * + * This is the function signature for joystick configuration callback + * functions. + * + * @param[in] joy The joystick that was connected or disconnected. + * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. + * + * @sa @ref joystick_event + * @sa glfwSetJoystickCallback + * + * @since Added in version 3.2. + * + * @ingroup input + */ +typedef void (* GLFWjoystickfun)(int,int); + /*! @brief Video mode type. * * This describes a single video mode. @@ -1704,17 +1728,17 @@ GLFWAPI void glfwWindowHint(int hint, int value); * glfwGetWindowAttrib, @ref glfwGetWindowSize and @ref glfwGetFramebufferSize. * * To create a full screen window, you need to specify the monitor the window - * will cover. If no monitor is specified, windowed mode will be used. Unless - * you have a way for the user to choose a specific monitor, it is recommended - * that you pick the primary monitor. For more information on how to query - * connected monitors, see @ref monitor_monitors. + * will cover. If no monitor is specified, the window will be windowed mode. + * Unless you have a way for the user to choose a specific monitor, it is + * recommended that you pick the primary monitor. For more information on how + * to query connected monitors, see @ref monitor_monitors. * * For full screen windows, the specified size becomes the resolution of the - * window's _desired video mode_. As long as a full screen window has input - * focus, the supported video mode most closely matching the desired video mode - * is set for the specified monitor. For more information about full screen - * windows, including the creation of so called _windowed full screen_ or - * _borderless full screen_ windows, see @ref window_windowed_full_screen. + * window's _desired video mode_. As long as a full screen window is not + * iconified, the supported video mode most closely matching the desired video + * mode is set for the specified monitor. For more information about full + * screen windows, including the creation of so called _windowed full screen_ + * or _borderless full screen_ windows, see @ref window_windowed_full_screen. * * By default, newly created windows use the placement recommended by the * window system. To create the window at a specific position, make it @@ -1722,8 +1746,8 @@ GLFWAPI void glfwWindowHint(int hint, int value); * hint, set its [position](@ref window_pos) and then [show](@ref window_hide) * it. * - * If a full screen window has input focus, the screensaver is prohibited from - * starting. + * As long as at least one full screen window is not iconified, the screensaver + * is prohibited from starting. * * Window systems put limits on window sizes. Very large or very small window * dimensions may be overridden by the window system on creation. Check the @@ -1737,7 +1761,7 @@ GLFWAPI void glfwWindowHint(int hint, int value); * @param[in] height The desired height, in screen coordinates, of the window. * This must be greater than zero. * @param[in] title The initial, UTF-8 encoded window title. - * @param[in] monitor The monitor to use for full screen mode, or `NULL` to use + * @param[in] monitor The monitor to use for full screen mode, or `NULL` for * windowed mode. * @param[in] share The window whose context to share resources with, or `NULL` * to not share resources. @@ -1899,6 +1923,45 @@ GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value); */ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); +/*! @brief Sets the icon for the specified window. + * + * This function sets the icon of the specified window. If passed an array of + * candidate images, those of or closest to the sizes desired by the system are + * selected. If no images are specified, the window reverts to its default + * icon. + * + * The desired image sizes varies depending on platform and system settings. + * The selected images will be rescaled as needed. Good sizes include 16x16, + * 32x32 and 48x48. + * + * @param[in] window The window whose icon to set. + * @param[in] count The number of images in the specified array, or zero to + * revert to the default window icon. + * @param[in] images The images to create the icon from. This is ignored if + * count is zero. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The specified image data is copied before this function + * returns. + * + * @remark @osx The GLFW window has no icon, as it is not a document + * window, but the dock icon will be the same as the application bundle's icon. + * For more information on bundles, see the + * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) + * in the Mac Developer Library. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_icon + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* images); + /*! @brief Retrieves the position of the client area of the specified window. * * This function retrieves the position, in screen coordinates, of the @@ -1991,11 +2054,15 @@ GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); /*! @brief Sets the size limits of the specified window. * * This function sets the size limits of the client area of the specified - * window. If the window is full screen or not resizable, this function does - * nothing. + * window. If the window is full screen, the size limits only take effect + * once it is made windowed. If the window is not resizable, this function + * does nothing. * - * The size limits are applied immediately and may cause the window to be - * resized. + * The size limits are applied immediately to a windowed mode window and may + * cause it to be resized. + * + * The maximum dimensions must be greater than or equal to the minimum + * dimensions and all must be greater than or equal to zero. * * @param[in] window The window to set limits for. * @param[in] minwidth The minimum width, in screen coordinates, of the client @@ -2007,8 +2074,8 @@ GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); * @param[in] maxheight The maximum height, in screen coordinates, of the * client area, or `GLFW_DONT_CARE`. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. * * @remark If you set size limits and an aspect ratio that conflict, the * results are undefined. @@ -2027,7 +2094,8 @@ GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minhe /*! @brief Sets the aspect ratio of the specified window. * * This function sets the required aspect ratio of the client area of the - * specified window. If the window is full screen or not resizable, this + * specified window. If the window is full screen, the aspect ratio only takes + * effect once it is made windowed. If the window is not resizable, this * function does nothing. * * The aspect ratio is specified as a numerator and a denominator and both @@ -2037,8 +2105,8 @@ GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minhe * If the numerator and denominator is set to `GLFW_DONT_CARE` then the aspect * ratio limit is disabled. * - * The aspect ratio is applied immediately and may cause the window to be - * resized. + * The aspect ratio is applied immediately to a windowed mode window and may + * cause it to be resized. * * @param[in] window The window to set limits for. * @param[in] numer The numerator of the desired aspect ratio, or @@ -2068,17 +2136,22 @@ GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom); * This function sets the size, in screen coordinates, of the client area of * the specified window. * - * For full screen windows, this function selects and switches to the resolution - * closest to the specified size, without affecting the window's context. As - * the context is unaffected, the bit depths of the framebuffer remain - * unchanged. + * For full screen windows, this function updates the resolution of its desired + * video mode and switches to the video mode closest to it, without affecting + * the window's context. As the context is unaffected, the bit depths of the + * framebuffer remain unchanged. + * + * If you wish to update the refresh rate of the desired video mode in addition + * to its resolution, see @ref glfwSetWindowMonitor. * * The window manager may put limits on what sizes are allowed. GLFW cannot * and should not override these limits. * * @param[in] window The window to resize. - * @param[in] width The desired width of the specified window. - * @param[in] height The desired height of the specified window. + * @param[in] width The desired width, in screen coordinates, of the window + * client area. + * @param[in] height The desired height, in screen coordinates, of the window + * client area. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -2087,6 +2160,7 @@ GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom); * * @sa @ref window_size * @sa glfwGetWindowSize + * @sa glfwSetWindowMonitor * * @since Added in version 1.0. * @glfw3 Added window handle parameter. @@ -2179,6 +2253,7 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int * * @sa @ref window_iconify * @sa glfwRestoreWindow + * @sa glfwMaximizeWindow * * @since Added in version 2.1. * @glfw3 Added window handle parameter. @@ -2190,7 +2265,8 @@ GLFWAPI void glfwIconifyWindow(GLFWwindow* window); /*! @brief Restores the specified window. * * This function restores the specified window if it was previously iconified - * (minimized). If the window is already restored, this function does nothing. + * (minimized) or maximized. If the window is already restored, this function + * does nothing. * * If the specified window is a full screen window, the resolution chosen for * the window is restored on the selected monitor. @@ -2204,6 +2280,7 @@ GLFWAPI void glfwIconifyWindow(GLFWwindow* window); * * @sa @ref window_iconify * @sa glfwIconifyWindow + * @sa glfwMaximizeWindow * * @since Added in version 2.1. * @glfw3 Added window handle parameter. @@ -2212,6 +2289,28 @@ GLFWAPI void glfwIconifyWindow(GLFWwindow* window); */ GLFWAPI void glfwRestoreWindow(GLFWwindow* window); +/*! @brief Maximizes the specified window. + * + * This function maximizes the specified window if it was previously not + * maximized. If the window is already maximized, this function does nothing. + * + * If the specified window is a full screen window, this function does nothing. + * + * @param[in] window The window to maximize. + * + * @par Thread Safety + * This function may only be called from the main thread. + * + * @sa @ref window_iconify + * @sa glfwIconifyWindow + * @sa glfwRestoreWindow + * + * @since Added in GLFW 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwMaximizeWindow(GLFWwindow* window); + /*! @brief Makes the specified window visible. * * This function makes the specified window visible if it was previously @@ -2256,6 +2355,34 @@ GLFWAPI void glfwShowWindow(GLFWwindow* window); */ GLFWAPI void glfwHideWindow(GLFWwindow* window); +/*! @brief Brings the specified window to front and sets input focus. + * + * This function brings the specified window to front and sets input focus. + * The window should already be visible and not iconified. + * + * By default, both windowed and full screen mode windows are focused when + * initially created. Set the [GLFW_FOCUSED](@ref window_hints_wnd) to disable + * this behavior. + * + * __Do not use this function__ to steal focus from other applications unless + * you are certain that is what the user wants. Focus stealing can be + * extremely disruptive. + * + * @param[in] window The window to give input focus. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_focus + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwFocusWindow(GLFWwindow* window); + /*! @brief Returns the monitor that the window uses for full screen mode. * * This function returns the handle of the monitor that the specified window is @@ -2270,6 +2397,7 @@ GLFWAPI void glfwHideWindow(GLFWwindow* window); * @thread_safety This function must only be called from the main thread. * * @sa @ref window_monitor + * @sa glfwSetWindowMonitor * * @since Added in version 3.0. * @@ -2277,6 +2405,54 @@ GLFWAPI void glfwHideWindow(GLFWwindow* window); */ GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); +/*! @brief Sets the mode, monitor, video mode and placement of a window. + * + * This function sets the monitor that the window uses for full screen mode or, + * if the monitor is `NULL`, makes it windowed mode. + * + * When setting a monitor, this function updates the width, height and refresh + * rate of the desired video mode and switches to the video mode closest to it. + * The window position is ignored when setting a monitor. + * + * When the monitor is `NULL`, the position, width and height are used to + * place the window client area. The refresh rate is ignored when no monitor + * is specified. + * + * If you only wish to update the resolution of a full screen window or the + * size of a windowed mode window, see @ref glfwSetWindowSize. + * + * When a window transitions from full screen to windowed mode, this function + * restores any previous window settings such as whether it is decorated, + * floating, resizable, has size or aspect ratio limits, etc.. + * + * @param[in] window The window whose monitor, size or video mode to set. + * @param[in] monitor The desired monitor, or `NULL` to set windowed mode. + * @param[in] xpos The desired x-coordinate of the upper-left corner of the + * client area. + * @param[in] ypos The desired y-coordinate of the upper-left corner of the + * client area. + * @param[in] width The desired with, in screen coordinates, of the client area + * or video mode. + * @param[in] height The desired height, in screen coordinates, of the client + * area or video mode. + * @param[in] refreshRate The desired refresh rate, in Hz, of the video mode. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_monitor + * @sa @ref window_full_screen + * @sa glfwGetWindowMonitor + * @sa glfwSetWindowSize + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); + /*! @brief Returns an attribute of the specified window. * * This function returns the value of an attribute of the specified window or @@ -2566,6 +2742,7 @@ GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window * * @sa @ref events * @sa glfwWaitEvents + * @sa glfwWaitEventsTimeout * * @since Added in version 1.0. * @@ -2610,6 +2787,7 @@ GLFWAPI void glfwPollEvents(void); * * @sa @ref events * @sa glfwPollEvents + * @sa glfwWaitEventsTimeout * * @since Added in version 2.5. * @@ -2617,6 +2795,52 @@ GLFWAPI void glfwPollEvents(void); */ GLFWAPI void glfwWaitEvents(void); +/*! @brief Waits with timeout until events are queued and processes them. + * + * This function puts the calling thread to sleep until at least one event is + * available in the event queue, or until the specified timeout is reached. If + * one or more events are available, it behaves exactly like @ref + * glfwPollEvents, i.e. the events in the queue are processed and the function + * then returns immediately. Processing events will cause the window and input + * callbacks associated with those events to be called. + * + * The timeout value must be a positive finite number. + * + * Since not all events are associated with callbacks, this function may return + * without a callback having been called even if you are monitoring all + * callbacks. + * + * On some platforms, a window move, resize or menu operation will cause event + * processing to block. This is due to how event processing is designed on + * those platforms. You can use the + * [window refresh callback](@ref window_refresh) to redraw the contents of + * your window when necessary during such operations. + * + * On some platforms, certain callbacks may be called outside of a call to one + * of the event processing functions. + * + * If no windows exist, this function returns immediately. For synchronization + * of threads in applications that do not create windows, use your threading + * library of choice. + * + * Event processing is not required for joystick input to work. + * + * @param[in] timeout The maximum amount of time, in seconds, to wait. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref events + * @sa glfwPollEvents + * @sa glfwWaitEvents + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwWaitEventsTimeout(double timeout); + /*! @brief Posts an empty event to the event queue. * * This function posts an empty event from the current thread to the event @@ -2714,13 +2938,42 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); /*! @brief Returns the localized name of the specified printable key. * * This function returns the localized name of the specified printable key. + * This is intended for displaying key bindings to the user. * - * If the key is `GLFW_KEY_UNKNOWN`, the scancode is used, otherwise the - * scancode is ignored. + * If the key is `GLFW_KEY_UNKNOWN`, the scancode is used instead, otherwise + * the scancode is ignored. If a non-printable key or (if the key is + * `GLFW_KEY_UNKNOWN`) a scancode that maps to a non-printable key is + * specified, this function returns `NULL`. + * + * This behavior allows you to pass in the arguments passed to the + * [key callback](@ref input_key) without modification. + * + * The printable keys are: + * - `GLFW_KEY_APOSTROPHE` + * - `GLFW_KEY_COMMA` + * - `GLFW_KEY_MINUS` + * - `GLFW_KEY_PERIOD` + * - `GLFW_KEY_SLASH` + * - `GLFW_KEY_SEMICOLON` + * - `GLFW_KEY_EQUAL` + * - `GLFW_KEY_LEFT_BRACKET` + * - `GLFW_KEY_RIGHT_BRACKET` + * - `GLFW_KEY_BACKSLASH` + * - `GLFW_KEY_WORLD_1` + * - `GLFW_KEY_WORLD_2` + * - `GLFW_KEY_0` to `GLFW_KEY_9` + * - `GLFW_KEY_A` to `GLFW_KEY_Z` + * - `GLFW_KEY_KP_0` to `GLFW_KEY_KP_9` + * - `GLFW_KEY_KP_DECIMAL` + * - `GLFW_KEY_KP_DIVIDE` + * - `GLFW_KEY_KP_MULTIPLY` + * - `GLFW_KEY_KP_SUBTRACT` + * - `GLFW_KEY_KP_ADD` + * - `GLFW_KEY_KP_EQUAL` * * @param[in] key The key to query, or `GLFW_KEY_UNKNOWN`. * @param[in] scancode The scancode of the key to query. - * @return The localized name of the key. + * @return The localized name of the key, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -3368,6 +3621,29 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int joy, int* count); */ GLFWAPI const char* glfwGetJoystickName(int joy); +/*! @brief Sets the joystick configuration callback. + * + * This function sets the joystick configuration callback, or removes the + * currently set callback. This is called when a joystick is connected to or + * disconnected from the system. + * + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_event + * + * @since Added in version 3.2. + * + * @ingroup input + */ +GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun); + /*! @brief Sets the clipboard to the specified string. * * This function sets the system clipboard to the specified, UTF-8 encoded @@ -3438,8 +3714,8 @@ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. + * @thread_safety This function may be called from any thread. Reading of the + * internal timer offset is not atomic. * * @sa @ref time * @@ -3464,7 +3740,8 @@ GLFWAPI double glfwGetTime(void); * floor((264 - 1) / 109) and is due to implementations * storing nanoseconds in 64 bits. The limit may be increased in the future. * - * @thread_safety This function must only be called from the main thread. + * @thread_safety This function may be called from any thread. Writing of the + * internal timer offset is not atomic. * * @sa @ref time * @@ -3474,6 +3751,48 @@ GLFWAPI double glfwGetTime(void); */ GLFWAPI void glfwSetTime(double time); +/*! @brief Returns the current value of the raw timer. + * + * This function returns the current value of the raw timer, measured in + * 1 / frequency seconds. To get the frequency, call @ref + * glfwGetTimerFrequency. + * + * @return The value of the timer, or zero if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref time + * @sa glfwGetTimerFrequency + * + * @since Added in version 3.2. + * + * @ingroup input + */ +GLFWAPI uint64_t glfwGetTimerValue(void); + +/*! @brief Returns the frequency, in Hz, of the raw timer. + * + * This function returns the frequency, in Hz, of the raw timer. + * + * @return The frequency of the timer, in Hz, or zero if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref time + * @sa glfwGetTimerValue + * + * @since Added in version 3.2. + * + * @ingroup input + */ +GLFWAPI uint64_t glfwGetTimerFrequency(void); + /*! @brief Makes the context of the specified window current for the calling * thread. * @@ -3531,14 +3850,18 @@ GLFWAPI GLFWwindow* glfwGetCurrentContext(void); /*! @brief Swaps the front and back buffers of the specified window. * - * This function swaps the front and back buffers of the specified window. If - * the swap interval is greater than zero, the GPU driver waits the specified - * number of screen updates before swapping the buffers. + * This function swaps the front and back buffers of the specified window when + * rendering with OpenGL or OpenGL ES. If the swap interval is greater than + * zero, the GPU driver waits the specified number of screen updates before + * swapping the buffers. * * The specified window must have an OpenGL or OpenGL ES context. Specifying * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT * error. * + * This function does not apply to Vulkan. If you are rendering with Vulkan, + * see `vkQueuePresentKHR` instead. + * * @param[in] window The window whose buffers to swap. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref @@ -3561,11 +3884,11 @@ GLFWAPI void glfwSwapBuffers(GLFWwindow* window); /*! @brief Sets the swap interval for the current context. * - * This function sets the swap interval for the current context, i.e. the - * number of screen updates to wait from the time @ref glfwSwapBuffers was - * called before swapping the buffers and returning. This is sometimes called - * _vertical synchronization_, _vertical retrace synchronization_ or just - * _vsync_. + * This function sets the swap interval for the current OpenGL or OpenGL ES + * context, i.e. the number of screen updates to wait from the time @ref + * glfwSwapBuffers was called before swapping the buffers and returning. This + * is sometimes called _vertical synchronization_, _vertical retrace + * synchronization_ or just _vsync_. * * Contexts that support either of the `WGL_EXT_swap_control_tear` and * `GLX_EXT_swap_control_tear` extensions also accept negative swap intervals, @@ -3577,6 +3900,9 @@ GLFWAPI void glfwSwapBuffers(GLFWwindow* window); * A context must be current on the calling thread. Calling this function * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. * + * This function does not apply to Vulkan. If you are rendering with Vulkan, + * see the present mode of your swapchain instead. + * * @param[in] interval The minimum number of screen updates to wait for * until the buffers are swapped by @ref glfwSwapBuffers. * @@ -3606,9 +3932,9 @@ GLFWAPI void glfwSwapInterval(int interval); /*! @brief Returns whether the specified extension is available. * * This function returns whether the specified - * [client API extension](@ref context_glext) is supported by the current - * OpenGL or OpenGL ES context. It searches both for OpenGL and OpenGL ES - * extension and platform-specific context creation API extensions. + * [API extension](@ref context_glext) is supported by the current OpenGL or + * OpenGL ES context. It searches both for client API extension and context + * creation API extensions. * * A context must be current on the calling thread. Calling this function * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. @@ -3618,6 +3944,10 @@ GLFWAPI void glfwSwapInterval(int interval); * frequently. The extension strings will not change during the lifetime of * a context, so there is no danger in doing this. * + * This function does not apply to Vulkan. If you are using Vulkan, see @ref + * glfwGetRequiredInstanceExtensions, `vkEnumerateInstanceExtensionProperties` + * and `vkEnumerateDeviceExtensionProperties` instead. + * * @param[in] extension The ASCII encoded name of the extension. * @return `GLFW_TRUE` if the extension is available, or `GLFW_FALSE` * otherwise. @@ -3640,13 +3970,17 @@ GLFWAPI int glfwExtensionSupported(const char* extension); /*! @brief Returns the address of the specified function for the current * context. * - * This function returns the address of the specified + * This function returns the address of the specified OpenGL or OpenGL ES * [core or extension function](@ref context_glext), if it is supported * by the current context. * * A context must be current on the calling thread. Calling this function * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. * + * This function does not apply to Vulkan. If you are rendering with Vulkan, + * see @ref glfwGetInstanceProcAddress, `vkGetInstanceProcAddr` and + * `vkGetDeviceProcAddr` instead. + * * @param[in] procname The ASCII encoded name of the function. * @return The address of the function, or `NULL` if an * [error](@ref error_handling) occurred. @@ -3743,7 +4077,7 @@ GLFWAPI int glfwVulkanSupported(void); * * @ingroup vulkan */ -GLFWAPI const char** glfwGetRequiredInstanceExtensions(int* count); +GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count); #if defined(VK_VERSION_1_0) @@ -3857,7 +4191,7 @@ GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhys * @remarks If an error occurs before the creation call is made, GLFW returns * the Vulkan error code most appropriate for the error. Appropriate use of * @ref glfwVulkanSupported and @ref glfwGetRequiredInstanceExtensions should - * elminiate almost all occurences of these errors. + * eliminate almost all occurrences of these errors. * * @thread_safety This function may be called from any thread. For * synchronization details of Vulkan objects, see the Vulkan specification. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d44f873c9..5042aba38 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,42 +7,44 @@ set(common_SOURCES 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_tls.h) + posix_tls.h nsgl_context.h) set(glfw_SOURCES ${common_SOURCES} cocoa_init.m cocoa_joystick.m - cocoa_monitor.m cocoa_window.m cocoa_time.c posix_tls.c) + cocoa_monitor.m cocoa_window.m cocoa_time.c posix_tls.c + nsgl_context.m) elseif (_GLFW_WIN32) - set(glfw_HEADERS ${common_HEADERS} win32_platform.h win32_joystick.h) + set(glfw_HEADERS ${common_HEADERS} win32_platform.h win32_joystick.h + wgl_context.h egl_context.h) set(glfw_SOURCES ${common_SOURCES} win32_init.c win32_joystick.c - win32_monitor.c win32_time.c win32_tls.c win32_window.c) + win32_monitor.c win32_time.c win32_tls.c win32_window.c + wgl_context.c egl_context.c) elseif (_GLFW_X11) set(glfw_HEADERS ${common_HEADERS} x11_platform.h xkb_unicode.h - linux_joystick.h posix_time.h posix_tls.h) + linux_joystick.h posix_time.h posix_tls.h glx_context.h + egl_context.h) set(glfw_SOURCES ${common_SOURCES} x11_init.c x11_monitor.c x11_window.c - xkb_unicode.c linux_joystick.c posix_time.c posix_tls.c) + xkb_unicode.c linux_joystick.c posix_time.c posix_tls.c + glx_context.c egl_context.c) elseif (_GLFW_WAYLAND) set(glfw_HEADERS ${common_HEADERS} wl_platform.h linux_joystick.h - posix_time.h posix_tls.h xkb_unicode.h) + posix_time.h posix_tls.h xkb_unicode.h egl_context.h) set(glfw_SOURCES ${common_SOURCES} wl_init.c wl_monitor.c wl_window.c - linux_joystick.c posix_time.c posix_tls.c xkb_unicode.c) + linux_joystick.c posix_time.c posix_tls.c xkb_unicode.c + egl_context.c) + + 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) elseif (_GLFW_MIR) set(glfw_HEADERS ${common_HEADERS} mir_platform.h linux_joystick.h - posix_time.h posix_tls.h xkb_unicode.h) + posix_time.h posix_tls.h xkb_unicode.h egl_context.h) set(glfw_SOURCES ${common_SOURCES} mir_init.c mir_monitor.c mir_window.c - linux_joystick.c posix_time.c posix_tls.c xkb_unicode.c) -endif() - -if (_GLFW_EGL) - list(APPEND glfw_HEADERS ${common_HEADERS} egl_context.h) - list(APPEND glfw_SOURCES ${common_SOURCES} egl_context.c) -elseif (_GLFW_NSGL) - list(APPEND glfw_HEADERS ${common_HEADERS} nsgl_context.h) - list(APPEND glfw_SOURCES ${common_SOURCES} nsgl_context.m) -elseif (_GLFW_WGL) - list(APPEND glfw_HEADERS ${common_HEADERS} wgl_context.h) - list(APPEND glfw_SOURCES ${common_SOURCES} wgl_context.c) -elseif (_GLFW_X11) - list(APPEND glfw_HEADERS ${common_HEADERS} glx_context.h) - list(APPEND glfw_SOURCES ${common_SOURCES} glx_context.c) + linux_joystick.c posix_time.c posix_tls.c xkb_unicode.c + egl_context.c) endif() if (APPLE) diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 0d990a8de..9b6434d5b 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -265,9 +265,6 @@ void _glfwPlatformTerminate(void) _glfw.ns.delegate = nil; } - [_glfw.ns.autoreleasePool release]; - _glfw.ns.autoreleasePool = nil; - [_glfw.ns.cursor release]; _glfw.ns.cursor = nil; @@ -276,14 +273,14 @@ void _glfwPlatformTerminate(void) _glfwTerminateNSGL(); _glfwTerminateJoysticksNS(); _glfwTerminateThreadLocalStoragePOSIX(); + + [_glfw.ns.autoreleasePool release]; + _glfw.ns.autoreleasePool = nil; } const char* _glfwPlatformGetVersionString(void) { - return _GLFW_VERSION_NUMBER " Cocoa" -#if defined(_GLFW_NSGL) - " NSGL" -#endif + return _GLFW_VERSION_NUMBER " Cocoa NSGL" #if defined(_GLFW_USE_CHDIR) " chdir" #endif diff --git a/src/cocoa_joystick.h b/src/cocoa_joystick.h index f1ea1532f..48d82b85b 100644 --- a/src/cocoa_joystick.h +++ b/src/cocoa_joystick.h @@ -56,7 +56,7 @@ typedef struct _GLFWjoydeviceNS // typedef struct _GLFWjoystickNS { - _GLFWjoydeviceNS devices[GLFW_JOYSTICK_LAST + 1]; + _GLFWjoydeviceNS js[GLFW_JOYSTICK_LAST + 1]; IOHIDManagerRef managerRef; } _GLFWjoystickNS; diff --git a/src/cocoa_joystick.m b/src/cocoa_joystick.m index 5c584f6dc..674004235 100644 --- a/src/cocoa_joystick.m +++ b/src/cocoa_joystick.m @@ -38,9 +38,8 @@ #include -//------------------------------------------------------------------------ // Joystick element information -//------------------------------------------------------------------------ +// typedef struct _GLFWjoyelementNS { IOHIDElementRef elementRef; @@ -58,7 +57,7 @@ static void getElementsCFArrayHandler(const void* value, void* parameter); // Adds an element to the specified joystick // -static void addJoystickElement(_GLFWjoydeviceNS* joystick, +static void addJoystickElement(_GLFWjoydeviceNS* js, IOHIDElementRef elementRef) { IOHIDElementType elementType; @@ -91,10 +90,10 @@ static void addJoystickElement(_GLFWjoydeviceNS* joystick, case kHIDUsage_GD_Slider: case kHIDUsage_GD_Dial: case kHIDUsage_GD_Wheel: - elementsArray = joystick->axisElements; + elementsArray = js->axisElements; break; case kHIDUsage_GD_Hatswitch: - elementsArray = joystick->hatElements; + elementsArray = js->hatElements; break; } @@ -102,7 +101,7 @@ static void addJoystickElement(_GLFWjoydeviceNS* joystick, } case kHIDPage_Button: - elementsArray = joystick->buttonElements; + elementsArray = js->buttonElements; break; default: break; @@ -134,15 +133,15 @@ static void getElementsCFArrayHandler(const void* value, void* parameter) // Returns the value of the specified element of the specified joystick // -static long getElementValue(_GLFWjoydeviceNS* joystick, _GLFWjoyelementNS* element) +static long getElementValue(_GLFWjoydeviceNS* js, _GLFWjoyelementNS* element) { IOReturn result = kIOReturnSuccess; IOHIDValueRef valueRef; long value = 0; - if (joystick && element && joystick->deviceRef) + if (js && element && js->deviceRef) { - result = IOHIDDeviceGetValue(joystick->deviceRef, + result = IOHIDDeviceGetValue(js->deviceRef, element->elementRef, &valueRef); @@ -164,87 +163,101 @@ static long getElementValue(_GLFWjoydeviceNS* joystick, _GLFWjoyelementNS* eleme // Removes the specified joystick // -static void removeJoystick(_GLFWjoydeviceNS* joystick) +static void removeJoystick(_GLFWjoydeviceNS* js) { int i; - if (!joystick->present) + if (!js->present) return; - for (i = 0; i < CFArrayGetCount(joystick->axisElements); i++) - free((void*) CFArrayGetValueAtIndex(joystick->axisElements, i)); - CFArrayRemoveAllValues(joystick->axisElements); - CFRelease(joystick->axisElements); + for (i = 0; i < CFArrayGetCount(js->axisElements); i++) + free((void*) CFArrayGetValueAtIndex(js->axisElements, i)); + CFArrayRemoveAllValues(js->axisElements); + CFRelease(js->axisElements); - for (i = 0; i < CFArrayGetCount(joystick->buttonElements); i++) - free((void*) CFArrayGetValueAtIndex(joystick->buttonElements, i)); - CFArrayRemoveAllValues(joystick->buttonElements); - CFRelease(joystick->buttonElements); + for (i = 0; i < CFArrayGetCount(js->buttonElements); i++) + free((void*) CFArrayGetValueAtIndex(js->buttonElements, i)); + CFArrayRemoveAllValues(js->buttonElements); + CFRelease(js->buttonElements); - for (i = 0; i < CFArrayGetCount(joystick->hatElements); i++) - free((void*) CFArrayGetValueAtIndex(joystick->hatElements, i)); - CFArrayRemoveAllValues(joystick->hatElements); - CFRelease(joystick->hatElements); + for (i = 0; i < CFArrayGetCount(js->hatElements); i++) + free((void*) CFArrayGetValueAtIndex(js->hatElements, i)); + CFArrayRemoveAllValues(js->hatElements); + CFRelease(js->hatElements); - free(joystick->axes); - free(joystick->buttons); + free(js->axes); + free(js->buttons); - memset(joystick, 0, sizeof(_GLFWjoydeviceNS)); + memset(js, 0, sizeof(_GLFWjoydeviceNS)); + + _glfwInputJoystickChange(js - _glfw.ns_js.js, GLFW_DISCONNECTED); } -// Polls for joystick events and updates GLFW state +// Polls for joystick axis events and updates GLFW state // -static GLFWbool pollJoystickEvents(_GLFWjoydeviceNS* joystick) +static GLFWbool pollJoystickAxisEvents(_GLFWjoydeviceNS* js) +{ + CFIndex i; + + if (!js->present) + return GLFW_FALSE; + + for (i = 0; i < CFArrayGetCount(js->axisElements); i++) + { + _GLFWjoyelementNS* axis = (_GLFWjoyelementNS*) + CFArrayGetValueAtIndex(js->axisElements, i); + + long value = getElementValue(js, axis); + long readScale = axis->maxReport - axis->minReport; + + if (readScale == 0) + js->axes[i] = value; + else + js->axes[i] = (2.f * (value - axis->minReport) / readScale) - 1.f; + } + + return GLFW_TRUE; +} + +// Polls for joystick button events and updates GLFW state +// +static GLFWbool pollJoystickButtonEvents(_GLFWjoydeviceNS* js) { CFIndex i; int buttonIndex = 0; - if (!joystick->present) + if (!js->present) return GLFW_FALSE; - for (i = 0; i < CFArrayGetCount(joystick->buttonElements); i++) + for (i = 0; i < CFArrayGetCount(js->buttonElements); i++) { _GLFWjoyelementNS* button = (_GLFWjoyelementNS*) - CFArrayGetValueAtIndex(joystick->buttonElements, i); + CFArrayGetValueAtIndex(js->buttonElements, i); - if (getElementValue(joystick, button)) - joystick->buttons[buttonIndex++] = GLFW_PRESS; + if (getElementValue(js, button)) + js->buttons[buttonIndex++] = GLFW_PRESS; else - joystick->buttons[buttonIndex++] = GLFW_RELEASE; + js->buttons[buttonIndex++] = GLFW_RELEASE; } - for (i = 0; i < CFArrayGetCount(joystick->axisElements); i++) - { - _GLFWjoyelementNS* axis = (_GLFWjoyelementNS*) - CFArrayGetValueAtIndex(joystick->axisElements, i); - - long value = getElementValue(joystick, axis); - long readScale = axis->maxReport - axis->minReport; - - if (readScale == 0) - joystick->axes[i] = value; - else - joystick->axes[i] = (2.f * (value - axis->minReport) / readScale) - 1.f; - } - - for (i = 0; i < CFArrayGetCount(joystick->hatElements); i++) + for (i = 0; i < CFArrayGetCount(js->hatElements); i++) { _GLFWjoyelementNS* hat = (_GLFWjoyelementNS*) - CFArrayGetValueAtIndex(joystick->hatElements, i); + CFArrayGetValueAtIndex(js->hatElements, i); // Bit fields of button presses for each direction, including nil const int directions[9] = { 1, 3, 2, 6, 4, 12, 8, 9, 0 }; - long j, value = getElementValue(joystick, hat); + long j, value = getElementValue(js, hat); if (value < 0 || value > 8) value = 8; for (j = 0; j < 4; j++) { if (directions[value] & (1 << j)) - joystick->buttons[buttonIndex++] = GLFW_PRESS; + js->buttons[buttonIndex++] = GLFW_PRESS; else - joystick->buttons[buttonIndex++] = GLFW_RELEASE; + js->buttons[buttonIndex++] = GLFW_RELEASE; } } @@ -258,49 +271,43 @@ static void matchCallback(void* context, void* sender, IOHIDDeviceRef deviceRef) { - _GLFWjoydeviceNS* joystick; + _GLFWjoydeviceNS* js; int joy; for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) { - joystick = _glfw.ns_js.devices + joy; - - if (!joystick->present) - continue; - - if (joystick->deviceRef == deviceRef) + if (_glfw.ns_js.js[joy].present && _glfw.ns_js.js[joy].deviceRef == deviceRef) return; } for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) { - joystick = _glfw.ns_js.devices + joy; - - if (!joystick->present) + if (!_glfw.ns_js.js[joy].present) break; } if (joy > GLFW_JOYSTICK_LAST) return; - joystick->present = GLFW_TRUE; - joystick->deviceRef = deviceRef; + js = _glfw.ns_js.js + joy; + js->present = GLFW_TRUE; + js->deviceRef = deviceRef; CFStringRef name = IOHIDDeviceGetProperty(deviceRef, CFSTR(kIOHIDProductKey)); if (name) { CFStringGetCString(name, - joystick->name, - sizeof(joystick->name), + js->name, + sizeof(js->name), kCFStringEncodingUTF8); } else - strncpy(joystick->name, "Unknown", sizeof(joystick->name)); + strncpy(js->name, "Unknown", sizeof(js->name)); - joystick->axisElements = CFArrayCreateMutable(NULL, 0, NULL); - joystick->buttonElements = CFArrayCreateMutable(NULL, 0, NULL); - joystick->hatElements = CFArrayCreateMutable(NULL, 0, NULL); + js->axisElements = CFArrayCreateMutable(NULL, 0, NULL); + js->buttonElements = CFArrayCreateMutable(NULL, 0, NULL); + js->hatElements = CFArrayCreateMutable(NULL, 0, NULL); CFArrayRef arrayRef = IOHIDDeviceCopyMatchingElements(deviceRef, NULL, @@ -309,14 +316,15 @@ static void matchCallback(void* context, CFArrayApplyFunction(arrayRef, range, getElementsCFArrayHandler, - (void*) joystick); + (void*) js); CFRelease(arrayRef); - joystick->axes = calloc(CFArrayGetCount(joystick->axisElements), - sizeof(float)); - joystick->buttons = calloc(CFArrayGetCount(joystick->buttonElements) + - CFArrayGetCount(joystick->hatElements) * 4, 1); + js->axes = calloc(CFArrayGetCount(js->axisElements), sizeof(float)); + js->buttons = calloc(CFArrayGetCount(js->buttonElements) + + CFArrayGetCount(js->hatElements) * 4, 1); + + _glfwInputJoystickChange(joy, GLFW_CONNECTED); } // Callback for user-initiated joystick removal @@ -330,10 +338,9 @@ static void removeCallback(void* context, for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) { - _GLFWjoydeviceNS* joystick = _glfw.ns_js.devices + joy; - if (joystick->deviceRef == deviceRef) + if (_glfw.ns_js.js[joy].deviceRef == deviceRef) { - removeJoystick(joystick); + removeJoystick(_glfw.ns_js.js + joy); break; } } @@ -451,10 +458,10 @@ void _glfwTerminateJoysticksNS(void) { int joy; - for (joy = 0; joy <= GLFW_JOYSTICK_LAST; joy++) + for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) { - _GLFWjoydeviceNS* joystick = _glfw.ns_js.devices + joy; - removeJoystick(joystick); + _GLFWjoydeviceNS* js = _glfw.ns_js.js + joy; + removeJoystick(js); } CFRelease(_glfw.ns_js.managerRef); @@ -468,37 +475,37 @@ void _glfwTerminateJoysticksNS(void) int _glfwPlatformJoystickPresent(int joy) { - _GLFWjoydeviceNS* joystick = _glfw.ns_js.devices + joy; - return pollJoystickEvents(joystick); + _GLFWjoydeviceNS* js = _glfw.ns_js.js + joy; + return js->present; } const float* _glfwPlatformGetJoystickAxes(int joy, int* count) { - _GLFWjoydeviceNS* joystick = _glfw.ns_js.devices + joy; - if (!pollJoystickEvents(joystick)) + _GLFWjoydeviceNS* js = _glfw.ns_js.js + joy; + if (!pollJoystickAxisEvents(js)) return NULL; - *count = (int) CFArrayGetCount(joystick->axisElements); - return joystick->axes; + *count = (int) CFArrayGetCount(js->axisElements); + return js->axes; } const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count) { - _GLFWjoydeviceNS* joystick = _glfw.ns_js.devices + joy; - if (!pollJoystickEvents(joystick)) + _GLFWjoydeviceNS* js = _glfw.ns_js.js + joy; + if (!pollJoystickButtonEvents(js)) return NULL; - *count = (int) CFArrayGetCount(joystick->buttonElements) + - (int) CFArrayGetCount(joystick->hatElements) * 4; - return joystick->buttons; + *count = (int) CFArrayGetCount(js->buttonElements) + + (int) CFArrayGetCount(js->hatElements) * 4; + return js->buttons; } const char* _glfwPlatformGetJoystickName(int joy) { - _GLFWjoydeviceNS* joystick = _glfw.ns_js.devices + joy; - if (!pollJoystickEvents(joystick)) + _GLFWjoydeviceNS* js = _glfw.ns_js.js + joy; + if (!js->present) return NULL; - return joystick->name; + return js->name; } diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index a2025e1da..95eae62bc 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -41,12 +41,7 @@ typedef void* id; #include "posix_tls.h" #include "cocoa_joystick.h" - -#if defined(_GLFW_NSGL) - #include "nsgl_context.h" -#else - #error "The Cocoa backend depends on NSGL platform support" -#endif +#include "nsgl_context.h" #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) #define _glfw_dlclose(handle) dlclose(handle) @@ -58,6 +53,9 @@ typedef void* id; #define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorNS ns #define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorNS ns +#define _GLFW_EGL_CONTEXT_STATE +#define _GLFW_EGL_LIBRARY_CONTEXT_STATE + // Cocoa-specific per-window data // @@ -66,7 +64,6 @@ typedef struct _GLFWwindowNS id object; id delegate; id view; - unsigned int modifierFlags; // The total sum of the distances the cursor has been warped // since the last cursor motion event was processed @@ -119,8 +116,7 @@ typedef struct _GLFWcursorNS // typedef struct _GLFWtimeNS { - double base; - double resolution; + uint64_t frequency; } _GLFWtimeNS; diff --git a/src/cocoa_time.c b/src/cocoa_time.c index 1a644868b..f8db04e97 100644 --- a/src/cocoa_time.c +++ b/src/cocoa_time.c @@ -29,14 +29,6 @@ #include -// Return raw time -// -static uint64_t getRawTime(void) -{ - return mach_absolute_time(); -} - - ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// @@ -48,8 +40,7 @@ void _glfwInitTimerNS(void) mach_timebase_info_data_t info; mach_timebase_info(&info); - _glfw.ns_time.resolution = (double) info.numer / (info.denom * 1.0e9); - _glfw.ns_time.base = getRawTime(); + _glfw.ns_time.frequency = (info.denom * 1e9) / info.numer; } @@ -57,15 +48,13 @@ void _glfwInitTimerNS(void) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -double _glfwPlatformGetTime(void) +uint64_t _glfwPlatformGetTimerValue(void) { - return (double) (getRawTime() - _glfw.ns_time.base) * - _glfw.ns_time.resolution; + return mach_absolute_time(); } -void _glfwPlatformSetTime(double time) +uint64_t _glfwPlatformGetTimerFrequency(void) { - _glfw.ns_time.base = getRawTime() - - (uint64_t) (time / _glfw.ns_time.resolution); + return _glfw.ns_time.frequency; } diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 4b95384cd..95ef9aebf 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -26,6 +26,7 @@ #include "internal.h" +#include #include // Needed for _NSGetProgname @@ -55,6 +56,26 @@ static NSCursor* getStandardCursor(int shape) return nil; } +// Returns the style mask corresponding to the window settings +// +static NSUInteger getStyleMask(_GLFWwindow* window) +{ + NSUInteger styleMask = 0; + + if (window->monitor || !window->decorated) + styleMask |= NSBorderlessWindowMask; + else + { + styleMask |= NSTitledWindowMask | NSClosableWindowMask | + NSMiniaturizableWindowMask; + + if (window->resizable) + styleMask |= NSResizableWindowMask; + } + + return styleMask; +} + // Center the cursor in the view of the window // static void centerCursor(_GLFWwindow *window) @@ -72,9 +93,9 @@ static float transformY(float y) return CGDisplayBounds(CGMainDisplayID()).size.height - y; } -// Enter full screen mode +// Make the specified window and its video mode active on its monitor // -static GLFWbool enterFullscreenMode(_GLFWwindow* window) +static GLFWbool acquireMonitor(_GLFWwindow* window) { const GLFWbool status = _glfwSetVideoModeNS(window->monitor, &window->videoMode); const CGRect bounds = CGDisplayBounds(window->monitor->ns.displayID); @@ -84,13 +105,19 @@ static GLFWbool enterFullscreenMode(_GLFWwindow* window) bounds.size.height); [window->ns.object setFrame:frame display:YES]; + + _glfwInputMonitorWindowChange(window->monitor, window); return status; } -// Leave full screen mode +// Remove the window and restore the original video mode // -static void leaveFullscreenMode(_GLFWwindow* window) +static void releaseMonitor(_GLFWwindow* window) { + if (window->monitor->window != window) + return; + + _glfwInputMonitorWindowChange(window->monitor, NULL); _glfwRestoreVideoModeNS(window->monitor); } @@ -122,6 +149,33 @@ static int translateKey(unsigned int key) return _glfw.ns.publicKeys[key]; } +// Translate a GLFW keycode to a Cocoa modifier flag +// +static NSUInteger translateKeyToModifierFlag(int key) +{ + switch (key) + { + case GLFW_KEY_LEFT_SHIFT: + case GLFW_KEY_RIGHT_SHIFT: + return NSShiftKeyMask; + case GLFW_KEY_LEFT_CONTROL: + case GLFW_KEY_RIGHT_CONTROL: + return NSControlKeyMask; + case GLFW_KEY_LEFT_ALT: + case GLFW_KEY_RIGHT_ALT: + return NSAlternateKeyMask; + case GLFW_KEY_LEFT_SUPER: + case GLFW_KEY_RIGHT_SUPER: + return NSCommandKeyMask; + } + + return 0; +} + +// Defines a constant for empty ranges in NSTextInputClient +// +static const NSRange kEmptyRange = { NSNotFound, 0 }; + //------------------------------------------------------------------------ // Delegate for window related notifications @@ -155,7 +209,7 @@ static int translateKey(unsigned int key) - (void)windowDidResize:(NSNotification *)notification { - if (window->context.api != GLFW_NO_API) + if (window->context.client != GLFW_NO_API) [window->context.nsgl.object update]; if (_glfw.cursorWindow == window && @@ -173,7 +227,7 @@ static int translateKey(unsigned int key) - (void)windowDidMove:(NSNotification *)notification { - if (window->context.api != GLFW_NO_API) + if (window->context.client != GLFW_NO_API) [window->context.nsgl.object update]; if (_glfw.cursorWindow == window && @@ -190,7 +244,7 @@ static int translateKey(unsigned int key) - (void)windowDidMiniaturize:(NSNotification *)notification { if (window->monitor) - leaveFullscreenMode(window); + releaseMonitor(window); _glfwInputWindowIconify(window, GLFW_TRUE); } @@ -198,7 +252,7 @@ static int translateKey(unsigned int key) - (void)windowDidDeminiaturize:(NSNotification *)notification { if (window->monitor) - enterFullscreenMode(window); + acquireMonitor(window); _glfwInputWindowIconify(window, GLFW_FALSE); } @@ -272,10 +326,11 @@ static int translateKey(unsigned int key) // Content view class for the GLFW window //------------------------------------------------------------------------ -@interface GLFWContentView : NSView +@interface GLFWContentView : NSView { _GLFWwindow* window; NSTrackingArea* trackingArea; + NSMutableAttributedString* markedText; } - (id)initWithGlfwWindow:(_GLFWwindow *)initWindow; @@ -305,6 +360,7 @@ static int translateKey(unsigned int key) { window = initWindow; trackingArea = nil; + markedText = [[NSMutableAttributedString alloc] init]; [self updateTrackingAreas]; [self registerForDraggedTypes:[NSArray arrayWithObjects: @@ -314,9 +370,10 @@ static int translateKey(unsigned int key) return self; } --(void)dealloc +- (void)dealloc { [trackingArea release]; + [markedText release]; [super dealloc]; } @@ -481,18 +538,7 @@ static int translateKey(unsigned int key) _glfwInputKey(window, key, [event keyCode], GLFW_PRESS, mods); - NSString* characters = [event characters]; - NSUInteger i, length = [characters length]; - const int plain = !(mods & GLFW_MOD_SUPER); - - for (i = 0; i < length; i++) - { - const unichar codepoint = [characters characterAtIndex:i]; - if ((codepoint & 0xff00) == 0xf700) - continue; - - _glfwInputChar(window, codepoint, mods, plain); - } + [self interpretKeyEvents:[NSArray arrayWithObject:event]]; } - (void)flagsChanged:(NSEvent *)event @@ -502,21 +548,18 @@ static int translateKey(unsigned int key) [event modifierFlags] & NSDeviceIndependentModifierFlagsMask; const int key = translateKey([event keyCode]); const int mods = translateFlags(modifierFlags); + const NSUInteger keyFlag = translateKeyToModifierFlag(key); - if (modifierFlags == window->ns.modifierFlags) + if (keyFlag & modifierFlags) { if (window->keys[key] == GLFW_PRESS) action = GLFW_RELEASE; else action = GLFW_PRESS; } - else if (modifierFlags > window->ns.modifierFlags) - action = GLFW_PRESS; else action = GLFW_RELEASE; - window->ns.modifierFlags = modifierFlags; - _glfwInputKey(window, key, [event keyCode], action, mods); } @@ -597,6 +640,92 @@ static int translateKey(unsigned int key) [self setNeedsDisplay:YES]; } +- (BOOL)hasMarkedText +{ + return [markedText length] > 0; +} + +- (NSRange)markedRange +{ + if ([markedText length] > 0) + return NSMakeRange(0, [markedText length] - 1); + else + return kEmptyRange; +} + +- (NSRange)selectedRange +{ + return kEmptyRange; +} + +- (void)setMarkedText:(id)string + selectedRange:(NSRange)selectedRange + replacementRange:(NSRange)replacementRange +{ + if ([string isKindOfClass:[NSAttributedString class]]) + [markedText initWithAttributedString:string]; + else + [markedText initWithString:string]; +} + +- (void)unmarkText +{ + [[markedText mutableString] setString:@""]; +} + +- (NSArray*)validAttributesForMarkedText +{ + return [NSArray array]; +} + +- (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range + actualRange:(NSRangePointer)actualRange +{ + return nil; +} + +- (NSUInteger)characterIndexForPoint:(NSPoint)point +{ + return 0; +} + +- (NSRect)firstRectForCharacterRange:(NSRange)range + actualRange:(NSRangePointer)actualRange +{ + int xpos, ypos; + _glfwPlatformGetWindowPos(window, &xpos, &ypos); + const NSRect contentRect = [window->ns.view frame]; + return NSMakeRect(xpos, transformY(ypos + contentRect.size.height), 0.0, 0.0); +} + +- (void)insertText:(id)string replacementRange:(NSRange)replacementRange +{ + NSString* characters; + NSEvent* event = [NSApp currentEvent]; + const int mods = translateFlags([event modifierFlags]); + const int plain = !(mods & GLFW_MOD_SUPER); + + if ([string isKindOfClass:[NSAttributedString class]]) + characters = [string string]; + else + characters = (NSString*) string; + + NSUInteger i, length = [characters length]; + + for (i = 0; i < length; i++) + { + const unichar codepoint = [characters characterAtIndex:i]; + if ((codepoint & 0xff00) == 0xf700) + continue; + + _glfwInputChar(window, codepoint, mods, plain); + } +} + +- (void)doCommandBySelector:(SEL)selector +{ +} + @end @@ -802,22 +931,9 @@ static GLFWbool createWindow(_GLFWwindow* window, return GLFW_FALSE; } - unsigned int styleMask = 0; - - if (wndconfig->monitor || !wndconfig->decorated) - styleMask = NSBorderlessWindowMask; - else - { - styleMask = NSTitledWindowMask | NSClosableWindowMask | - NSMiniaturizableWindowMask; - - if (wndconfig->resizable) - styleMask |= NSResizableWindowMask; - } - NSRect contentRect; - if (wndconfig->monitor) + if (window->monitor) { GLFWvidmode mode; int xpos, ypos; @@ -832,7 +948,7 @@ static GLFWbool createWindow(_GLFWwindow* window, window->ns.object = [[GLFWWindow alloc] initWithContentRect:contentRect - styleMask:styleMask + styleMask:getStyleMask(window) backing:NSBackingStoreBuffered defer:NO]; @@ -842,19 +958,20 @@ static GLFWbool createWindow(_GLFWwindow* window, return GLFW_FALSE; } - if (wndconfig->resizable) - [window->ns.object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; - - if (wndconfig->monitor) - { + if (window->monitor) [window->ns.object setLevel:NSMainMenuWindowLevel + 1]; - } else { [window->ns.object center]; + if (wndconfig->resizable) + [window->ns.object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; + if (wndconfig->floating) [window->ns.object setLevel:NSFloatingWindowLevel]; + + if (wndconfig->maximized) + [window->ns.object zoom:nil]; } window->ns.view = [[GLFWContentView alloc] initWithGlfwWindow:window]; @@ -863,12 +980,7 @@ static GLFWbool createWindow(_GLFWwindow* window, [window->ns.view setWantsBestResolutionOpenGLSurface:YES]; #endif /*_GLFW_USE_RETINA*/ - if (wndconfig->transparent) - { - [window->ns.object setOpaque:NO]; - [window->ns.object setBackgroundColor:[NSColor clearColor]]; - } - + [window->ns.object makeFirstResponder:window->ns.view]; [window->ns.object setTitle:[NSString stringWithUTF8String:wndconfig->title]]; [window->ns.object setDelegate:window->ns.delegate]; [window->ns.object setAcceptsMouseMovedEvents:YES]; @@ -894,16 +1006,25 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!createWindow(window, wndconfig)) return GLFW_FALSE; - if (ctxconfig->api != GLFW_NO_API) + if (ctxconfig->client != GLFW_NO_API) { - if (!_glfwCreateContextNSGL(window, ctxconfig, fbconfig)) + if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) + { + if (!_glfwCreateContextNSGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else + { + _glfwInputError(GLFW_API_UNAVAILABLE, "Cocoa: EGL not available"); return GLFW_FALSE; + } } - if (wndconfig->monitor) + if (window->monitor) { _glfwPlatformShowWindow(window); - if (!enterFullscreenMode(window)) + _glfwPlatformFocusWindow(window); + if (!acquireMonitor(window)) return GLFW_FALSE; } @@ -915,10 +1036,10 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) [window->ns.object orderOut:nil]; if (window->monitor) - leaveFullscreenMode(window); + releaseMonitor(window); - if (window->context.api != GLFW_NO_API) - _glfwDestroyContextNSGL(window); + if (window->context.client != GLFW_NO_API) + window->context.destroyContext(window); [window->ns.object setDelegate:nil]; [window->ns.delegate release]; @@ -929,6 +1050,9 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) [window->ns.object close]; window->ns.object = nil; + + [_glfw.ns.autoreleasePool drain]; + _glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init]; } void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char *title) @@ -936,6 +1060,12 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char *title) [window->ns.object setTitle:[NSString stringWithUTF8String:title]]; } +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, + int count, const GLFWimage* images) +{ + // Regular windows do not have icons +} + void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) { // Code from Matt Reagan on the Cocoa-dev mailing list. @@ -987,7 +1117,10 @@ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) { if (window->monitor) - enterFullscreenMode(window); + { + if (window->monitor->window == window) + acquireMonitor(window); + } else [window->ns.object setContentSize:NSMakeSize(width, height)]; } @@ -1002,7 +1135,7 @@ void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, [window->ns.object setContentMinSize:NSMakeSize(minwidth, minheight)]; if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE) - [window->ns.object setContentMaxSize:NSMakeSize(0, 0)]; + [window->ns.object setContentMaxSize:NSMakeSize(DBL_MAX, DBL_MAX)]; else [window->ns.object setContentMaxSize:NSMakeSize(maxwidth, maxheight)]; } @@ -1052,10 +1185,29 @@ void _glfwPlatformIconifyWindow(_GLFWwindow* window) void _glfwPlatformRestoreWindow(_GLFWwindow* window) { - [window->ns.object deminiaturize:nil]; + if ([window->ns.object isMiniaturized]) + [window->ns.object deminiaturize:nil]; + else if ([window->ns.object isZoomed]) + [window->ns.object zoom:nil]; +} + +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ + if (![window->ns.object isZoomed]) + [window->ns.object zoom:nil]; } void _glfwPlatformShowWindow(_GLFWwindow* window) +{ + [window->ns.object orderFront:nil]; +} + +void _glfwPlatformHideWindow(_GLFWwindow* window) +{ + [window->ns.object orderOut:nil]; +} + +void _glfwPlatformFocusWindow(_GLFWwindow* window) { // Make us the active application // HACK: This has been moved here from initializeAppKit to prevent @@ -1066,14 +1218,101 @@ void _glfwPlatformShowWindow(_GLFWwindow* window) [window->ns.object makeKeyAndOrderFront:nil]; } -void _glfwPlatformUnhideWindow(_GLFWwindow* window) +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) { - [window->ns.object orderFront:nil]; -} + if (window->monitor == monitor) + { + if (monitor) + { + if (monitor->window == window) + acquireMonitor(window); + } + else + { + const NSRect contentRect = + NSMakeRect(xpos, transformY(ypos + height), width, height); + const NSRect frameRect = + [window->ns.object frameRectForContentRect:contentRect + styleMask:getStyleMask(window)]; -void _glfwPlatformHideWindow(_GLFWwindow* window) -{ - [window->ns.object orderOut:nil]; + [window->ns.object setFrame:frameRect display:YES]; + } + + return; + } + + if (window->monitor) + releaseMonitor(window); + + _glfwInputWindowMonitorChange(window, monitor); + + const NSUInteger styleMask = getStyleMask(window); + [window->ns.object setStyleMask:styleMask]; + [window->ns.object makeFirstResponder:window->ns.view]; + + NSRect contentRect; + + if (monitor) + { + GLFWvidmode mode; + + _glfwPlatformGetVideoMode(window->monitor, &mode); + _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); + + contentRect = NSMakeRect(xpos, transformY(ypos + mode.height), + mode.width, mode.height); + } + else + { + contentRect = NSMakeRect(xpos, transformY(ypos + height), + width, height); + } + + NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect + styleMask:styleMask]; + [window->ns.object setFrame:frameRect display:YES]; + + if (monitor) + { + [window->ns.object setLevel:NSMainMenuWindowLevel + 1]; + [window->ns.object setHasShadow:NO]; + + acquireMonitor(window); + } + else + { + if (window->numer != GLFW_DONT_CARE && + window->denom != GLFW_DONT_CARE) + { + [window->ns.object setContentAspectRatio:NSMakeSize(window->numer, + window->denom)]; + } + + if (window->minwidth != GLFW_DONT_CARE && + window->minheight != GLFW_DONT_CARE) + { + [window->ns.object setContentMinSize:NSMakeSize(window->minwidth, + window->minheight)]; + } + + if (window->maxwidth != GLFW_DONT_CARE && + window->maxheight != GLFW_DONT_CARE) + { + [window->ns.object setContentMaxSize:NSMakeSize(window->maxwidth, + window->maxheight)]; + } + + if (window->floating) + [window->ns.object setLevel:NSFloatingWindowLevel]; + else + [window->ns.object setLevel:NSNormalWindowLevel]; + + [window->ns.object setHasShadow:YES]; + } } int _glfwPlatformWindowFocused(_GLFWwindow* window) @@ -1091,6 +1330,11 @@ int _glfwPlatformWindowVisible(_GLFWwindow* window) return [window->ns.object isVisible]; } +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + return [window->ns.object isZoomed]; +} + void _glfwPlatformPollEvents(void) { for (;;) @@ -1123,6 +1367,19 @@ void _glfwPlatformWaitEvents(void) _glfwPlatformPollEvents(); } +void _glfwPlatformWaitEventsTimeout(double timeout) +{ + NSDate* date = [NSDate dateWithTimeIntervalSinceNow:timeout]; + NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:date + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (event) + [NSApp sendEvent:event]; + + _glfwPlatformPollEvents(); +} + void _glfwPlatformPostEmptyEvent(void) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; @@ -1351,7 +1608,7 @@ const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) return _glfw.ns.clipboardString; } -char** _glfwPlatformGetRequiredInstanceExtensions(int* count) +char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count) { *count = 0; return NULL; @@ -1359,7 +1616,7 @@ char** _glfwPlatformGetRequiredInstanceExtensions(int* count) int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, - unsigned int queuefamily) + uint32_t queuefamily) { return GLFW_FALSE; } diff --git a/src/context.c b/src/context.c index c6a36c16a..e3733fef0 100644 --- a/src/context.c +++ b/src/context.c @@ -57,7 +57,7 @@ static GLFWbool parseVersionString(int* api, int* major, int* minor, int* rev) if (!version) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Failed to retrieve context version string"); + "Client API version string retrieval is broken"); return GLFW_FALSE; } @@ -76,7 +76,7 @@ static GLFWbool parseVersionString(int* api, int* major, int* minor, int* rev) if (!sscanf(version, "%d.%d.%d", major, minor, rev)) { _glfwInputError(GLFW_PLATFORM_ERROR, - "No version found in context version string"); + "No version found in client API version string"); return GLFW_FALSE; } @@ -90,15 +90,26 @@ static GLFWbool parseVersionString(int* api, int* major, int* minor, int* rev) GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) { - if (ctxconfig->api != GLFW_NO_API && - ctxconfig->api != GLFW_OPENGL_API && - ctxconfig->api != GLFW_OPENGL_ES_API) + if (ctxconfig->source != GLFW_NATIVE_CONTEXT_API && + ctxconfig->source != GLFW_EGL_CONTEXT_API) { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid client API"); + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid context creation API %i", + ctxconfig->source); return GLFW_FALSE; } - if (ctxconfig->api == GLFW_OPENGL_API) + if (ctxconfig->client != GLFW_NO_API && + ctxconfig->client != GLFW_OPENGL_API && + ctxconfig->client != GLFW_OPENGL_ES_API) + { + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid client API %i", + ctxconfig->client); + return GLFW_FALSE; + } + + if (ctxconfig->client == GLFW_OPENGL_API) { if ((ctxconfig->major < 1 || ctxconfig->minor < 0) || (ctxconfig->major == 1 && ctxconfig->minor > 5) || @@ -123,7 +134,8 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) ctxconfig->profile != GLFW_OPENGL_COMPAT_PROFILE) { _glfwInputError(GLFW_INVALID_ENUM, - "Invalid OpenGL profile"); + "Invalid OpenGL profile %i", + ctxconfig->profile); return GLFW_FALSE; } @@ -147,7 +159,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) return GLFW_FALSE; } } - else if (ctxconfig->api == GLFW_OPENGL_ES_API) + else if (ctxconfig->client == GLFW_OPENGL_ES_API) { if (ctxconfig->major < 1 || ctxconfig->minor < 0 || (ctxconfig->major == 1 && ctxconfig->minor > 1) || @@ -171,7 +183,8 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) ctxconfig->robustness != GLFW_LOSE_CONTEXT_ON_RESET) { _glfwInputError(GLFW_INVALID_ENUM, - "Invalid context robustness mode"); + "Invalid context robustness mode %i", + ctxconfig->robustness); return GLFW_FALSE; } } @@ -182,7 +195,8 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) ctxconfig->release != GLFW_RELEASE_BEHAVIOR_FLUSH) { _glfwInputError(GLFW_INVALID_ENUM, - "Invalid context release behavior"); + "Invalid context release behavior %i", + ctxconfig->release); return GLFW_FALSE; } } @@ -367,8 +381,13 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig) glfwGetProcAddress("glGetIntegerv"); window->context.GetString = (PFNGLGETSTRINGPROC) glfwGetProcAddress("glGetString"); + if (!window->context.GetIntegerv || !window->context.GetString) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Entry point retrieval is broken"); + return GLFW_FALSE; + } - if (!parseVersionString(&window->context.api, + if (!parseVersionString(&window->context.client, &window->context.major, &window->context.minor, &window->context.revision)) @@ -376,6 +395,26 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig) return GLFW_FALSE; } + window->context.source = ctxconfig->source; + + if (window->context.major < ctxconfig->major || + (window->context.major == ctxconfig->major && + window->context.minor < ctxconfig->minor)) + { + // The desired OpenGL version is greater than the actual version + // This only happens if the machine lacks {GLX|WGL}_ARB_create_context + // /and/ the user has requested an OpenGL version greater than 1.0 + + // For API consistency, we emulate the behavior of the + // {GLX|WGL}_ARB_create_context extension and fail here + + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "Requested client API version %i.%i, got version %i.%i", + ctxconfig->major, ctxconfig->minor, + window->context.major, window->context.minor); + return GLFW_FALSE; + } + if (window->context.major >= 3) { // OpenGL 3.0+ uses a different function for extension string retrieval @@ -392,7 +431,7 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig) } } - if (window->context.api == GLFW_OPENGL_API) + if (window->context.client == GLFW_OPENGL_API) { // Read back context flags (OpenGL 3.0 and above) if (window->context.major >= 3) @@ -490,29 +529,7 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig) { PFNGLCLEARPROC glClear = (PFNGLCLEARPROC) glfwGetProcAddress("glClear"); glClear(GL_COLOR_BUFFER_BIT); - _glfwPlatformSwapBuffers(window); - } - - return GLFW_TRUE; -} - -GLFWbool _glfwIsValidContext(const _GLFWctxconfig* ctxconfig) -{ - _GLFWwindow* window = _glfwPlatformGetCurrentContext(); - - if (window->context.major < ctxconfig->major || - (window->context.major == ctxconfig->major && - window->context.minor < ctxconfig->minor)) - { - // The desired OpenGL version is greater than the actual version - // This only happens if the machine lacks {GLX|WGL}_ARB_create_context - // /and/ the user has requested an OpenGL version greater than 1.0 - - // For API consistency, we emulate the behavior of the - // {GLX|WGL}_ARB_create_context extension and fail here - - _glfwInputError(GLFW_VERSION_UNAVAILABLE, NULL); - return GLFW_FALSE; + window->context.swapBuffers(window); } return GLFW_TRUE; @@ -552,16 +569,24 @@ GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFWwindow* previous = _glfwPlatformGetCurrentContext(); _GLFW_REQUIRE_INIT(); - if (window && window->context.api == GLFW_NO_API) + if (window && window->context.client == GLFW_NO_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return; } - _glfwPlatformMakeContextCurrent(window); + if (previous) + { + if (!window || window->context.source != previous->context.source) + previous->context.makeContextCurrent(NULL); + } + + if (window) + window->context.makeContextCurrent(window); } GLFWAPI GLFWwindow* glfwGetCurrentContext(void) @@ -573,37 +598,40 @@ GLFWAPI GLFWwindow* glfwGetCurrentContext(void) GLFWAPI void glfwSwapBuffers(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT(); - if (window->context.api == GLFW_NO_API) + if (window->context.client == GLFW_NO_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return; } - _glfwPlatformSwapBuffers(window); + window->context.swapBuffers(window); } GLFWAPI void glfwSwapInterval(int interval) { + _GLFWwindow* window; + _GLFW_REQUIRE_INIT(); - if (!_glfwPlatformGetCurrentContext()) + window = _glfwPlatformGetCurrentContext(); + if (!window) { _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL); return; } - _glfwPlatformSwapInterval(interval); + window->context.swapInterval(interval); } GLFWAPI int glfwExtensionSupported(const char* extension) { _GLFWwindow* window; - assert(extension); + assert(extension != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); @@ -616,7 +644,7 @@ GLFWAPI int glfwExtensionSupported(const char* extension) if (*extension == '\0') { - _glfwInputError(GLFW_INVALID_VALUE, NULL); + _glfwInputError(GLFW_INVALID_VALUE, "Extension name is empty string"); return GLFW_FALSE; } @@ -636,7 +664,7 @@ GLFWAPI int glfwExtensionSupported(const char* extension) if (!en) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Failed to retrieve extension string %i", i); + "Extension string retrieval is broken"); return GLFW_FALSE; } @@ -653,7 +681,7 @@ GLFWAPI int glfwExtensionSupported(const char* extension) if (!extensions) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Failed to retrieve extension string"); + "Extension string retrieval is broken"); return GLFW_FALSE; } @@ -662,21 +690,23 @@ GLFWAPI int glfwExtensionSupported(const char* extension) } // Check if extension is in the platform-specific string - return _glfwPlatformExtensionSupported(extension); + return window->context.extensionSupported(extension); } GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname) { - assert(procname); + _GLFWwindow* window; + assert(procname != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - if (!_glfwPlatformGetCurrentContext()) + window = _glfwPlatformGetCurrentContext(); + if (!window) { _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL); return NULL; } - return _glfwPlatformGetProcAddress(procname); + return window->context.getProcAddress(procname); } diff --git a/src/egl_context.c b/src/egl_context.c index 579e17f10..062030276 100644 --- a/src/egl_context.c +++ b/src/egl_context.c @@ -68,9 +68,9 @@ static const char* getErrorString(EGLint error) return "A NativeWindowType argument does not refer to a valid native window"; case EGL_CONTEXT_LOST: return "The application must destroy all contexts and reinitialise"; + default: + return "ERROR: UNKNOWN EGL ERROR"; } - - return "UNKNOWN EGL ERROR"; } // Returns the specified attribute of the specified EGLConfig @@ -125,7 +125,7 @@ static GLFWbool chooseFBConfigs(const _GLFWctxconfig* ctxconfig, continue; #endif // _GLFW_X11 - if (ctxconfig->api == GLFW_OPENGL_ES_API) + if (ctxconfig->client == GLFW_OPENGL_ES_API) { if (ctxconfig->major == 1) { @@ -138,7 +138,7 @@ static GLFWbool chooseFBConfigs(const _GLFWctxconfig* ctxconfig, continue; } } - else if (ctxconfig->api == GLFW_OPENGL_API) + else if (ctxconfig->client == GLFW_OPENGL_API) { if (!(getConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_BIT)) continue; @@ -155,18 +155,122 @@ static GLFWbool chooseFBConfigs(const _GLFWctxconfig* ctxconfig, u->samples = getConfigAttrib(n, EGL_SAMPLES); u->doublebuffer = GLFW_TRUE; - u->egl = n; + u->handle = (uintptr_t) n; usableCount++; } closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); if (closest) - *result = closest->egl; + *result = (EGLConfig) closest->handle; free(nativeConfigs); free(usableConfigs); - return closest ? GLFW_TRUE : GLFW_FALSE; + return closest != NULL; +} + +static void makeContextCurrent(_GLFWwindow* window) +{ + if (window) + { + if (!eglMakeCurrent(_glfw.egl.display, + window->context.egl.surface, + window->context.egl.surface, + window->context.egl.handle)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "EGL: Failed to make context current: %s", + getErrorString(eglGetError())); + return; + } + } + else + { + if (!eglMakeCurrent(_glfw.egl.display, + EGL_NO_SURFACE, + EGL_NO_SURFACE, + EGL_NO_CONTEXT)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "EGL: Failed to clear current context: %s", + getErrorString(eglGetError())); + return; + } + } + + _glfwPlatformSetCurrentContext(window); +} + +static void swapBuffers(_GLFWwindow* window) +{ + if (window != _glfwPlatformGetCurrentContext()) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "EGL: The context must be current on the calling thread when swapping buffers"); + return; + } + + eglSwapBuffers(_glfw.egl.display, window->context.egl.surface); +} + +static void swapInterval(int interval) +{ + eglSwapInterval(_glfw.egl.display, interval); +} + +static int extensionSupported(const char* extension) +{ + const char* extensions = eglQueryString(_glfw.egl.display, EGL_EXTENSIONS); + if (extensions) + { + if (_glfwStringInExtensionString(extension, extensions)) + return GLFW_TRUE; + } + + return GLFW_FALSE; +} + +static GLFWglproc getProcAddress(const char* procname) +{ + _GLFWwindow* window = _glfwPlatformGetCurrentContext(); + + if (window->context.egl.client) + { + GLFWglproc proc = (GLFWglproc) _glfw_dlsym(window->context.egl.client, + procname); + if (proc) + return proc; + } + + return eglGetProcAddress(procname); +} + +static void destroyContext(_GLFWwindow* window) +{ +#if defined(_GLFW_X11) + // NOTE: Do not unload libGL.so.1 while the X11 display is still open, + // as it will make XCloseDisplay segfault + if (window->context.client != GLFW_OPENGL_API) +#endif // _GLFW_X11 + { + if (window->context.egl.client) + { + _glfw_dlclose(window->context.egl.client); + window->context.egl.client = NULL; + } + } + + if (window->context.egl.surface) + { + eglDestroySurface(_glfw.egl.display, window->context.egl.surface); + window->context.egl.surface = EGL_NO_SURFACE; + } + + if (window->context.egl.handle) + { + eglDestroyContext(_glfw.egl.display, window->context.egl.handle); + window->context.egl.handle = EGL_NO_CONTEXT; + } } @@ -200,10 +304,7 @@ GLFWbool _glfwInitEGL(void) } if (!_glfw.egl.handle) - { - _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: Failed to load EGL"); return GLFW_FALSE; - } _glfw.egl.GetConfigAttrib = (PFNEGLGETCONFIGATTRIBPROC) _glfw_dlsym(_glfw.egl.handle, "eglGetConfigAttrib"); @@ -244,6 +345,8 @@ GLFWbool _glfwInitEGL(void) _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: Failed to get EGL display: %s", getErrorString(eglGetError())); + + _glfwTerminateEGL(); return GLFW_FALSE; } @@ -252,13 +355,17 @@ GLFWbool _glfwInitEGL(void) _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: Failed to initialize EGL: %s", getErrorString(eglGetError())); + + _glfwTerminateEGL(); return GLFW_FALSE; } _glfw.egl.KHR_create_context = - _glfwPlatformExtensionSupported("EGL_KHR_create_context"); + extensionSupported("EGL_KHR_create_context"); _glfw.egl.KHR_create_context_no_error = - _glfwPlatformExtensionSupported("EGL_KHR_create_context_no_error"); + extensionSupported("EGL_KHR_create_context_no_error"); + _glfw.egl.KHR_gl_colorspace = + extensionSupported("EGL_KHR_gl_colorspace"); return GLFW_TRUE; } @@ -267,9 +374,14 @@ GLFWbool _glfwInitEGL(void) // void _glfwTerminateEGL(void) { - if (_glfw.egl.handle) + if (_glfw.egl.display) { eglTerminate(_glfw.egl.display); + _glfw.egl.display = EGL_NO_DISPLAY; + } + + if (_glfw.egl.handle) + { _glfw_dlclose(_glfw.egl.handle); _glfw.egl.handle = NULL; } @@ -288,10 +400,16 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { - int attribs[40]; + EGLint attribs[40]; EGLConfig config; EGLContext share = NULL; + if (!_glfw.egl.display) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: API not available"); + return GLFW_FALSE; + } + if (ctxconfig->share) share = ctxconfig->share->context.egl.handle; @@ -302,7 +420,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, return GLFW_FALSE; } - if (ctxconfig->api == GLFW_OPENGL_ES_API) + if (ctxconfig->client == GLFW_OPENGL_ES_API) { if (!eglBindAPI(EGL_OPENGL_ES_API)) { @@ -327,7 +445,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, { int index = 0, mask = 0, flags = 0; - if (ctxconfig->api == GLFW_OPENGL_API) + if (ctxconfig->client == GLFW_OPENGL_API) { if (ctxconfig->forward) flags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR; @@ -381,7 +499,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, { int index = 0; - if (ctxconfig->api == GLFW_OPENGL_ES_API) + if (ctxconfig->client == GLFW_OPENGL_ES_API) setEGLattrib(EGL_CONTEXT_CLIENT_VERSION, ctxconfig->major); setEGLattrib(EGL_NONE, EGL_NONE); @@ -401,11 +519,26 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, return GLFW_FALSE; } + // Set up attributes for surface creation + { + int index = 0; + + if (fbconfig->sRGB) + { + if (_glfw.egl.KHR_gl_colorspace) + { + setEGLattrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR); + } + } + + setEGLattrib(EGL_NONE, EGL_NONE); + } + window->context.egl.surface = eglCreateWindowSurface(_glfw.egl.display, config, _GLFW_EGL_NATIVE_WINDOW, - NULL); + attribs); if (window->context.egl.surface == EGL_NO_SURFACE) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -455,7 +588,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, NULL }; - if (ctxconfig->api == GLFW_OPENGL_ES_API) + if (ctxconfig->client == GLFW_OPENGL_ES_API) { if (ctxconfig->major == 1) sonames = es1sonames; @@ -480,41 +613,18 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, } } + window->context.makeContextCurrent = makeContextCurrent; + window->context.swapBuffers = swapBuffers; + window->context.swapInterval = swapInterval; + window->context.extensionSupported = extensionSupported; + window->context.getProcAddress = getProcAddress; + window->context.destroyContext = destroyContext; + return GLFW_TRUE; } #undef setEGLattrib -// Destroy the OpenGL context -// -void _glfwDestroyContextEGL(_GLFWwindow* window) -{ -#if defined(_GLFW_X11) - // NOTE: Do not unload libGL.so.1 while the X11 display is still open, - // as it will make XCloseDisplay segfault - if (window->context.api != GLFW_OPENGL_API) -#endif // _GLFW_X11 - { - if (window->context.egl.client) - { - _glfw_dlclose(window->context.egl.client); - window->context.egl.client = NULL; - } - } - - if (window->context.egl.surface) - { - eglDestroySurface(_glfw.egl.display, window->context.egl.surface); - window->context.egl.surface = EGL_NO_SURFACE; - } - - if (window->context.egl.handle) - { - eglDestroyContext(_glfw.egl.display, window->context.egl.handle); - window->context.egl.handle = EGL_NO_CONTEXT; - } -} - // Returns the Visual and depth of the chosen EGLConfig // #if defined(_GLFW_X11) @@ -558,75 +668,6 @@ GLFWbool _glfwChooseVisualEGL(const _GLFWctxconfig* ctxconfig, #endif // _GLFW_X11 -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -void _glfwPlatformMakeContextCurrent(_GLFWwindow* window) -{ - if (window) - { - eglMakeCurrent(_glfw.egl.display, - window->context.egl.surface, - window->context.egl.surface, - window->context.egl.handle); - } - else - { - eglMakeCurrent(_glfw.egl.display, - EGL_NO_SURFACE, - EGL_NO_SURFACE, - EGL_NO_CONTEXT); - } - - _glfwPlatformSetCurrentContext(window); -} - -void _glfwPlatformSwapBuffers(_GLFWwindow* window) -{ - if (window != _glfwPlatformGetCurrentContext()) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "EGL: The context must be current on the calling thread when swapping buffers"); - return; - } - - eglSwapBuffers(_glfw.egl.display, window->context.egl.surface); -} - -void _glfwPlatformSwapInterval(int interval) -{ - eglSwapInterval(_glfw.egl.display, interval); -} - -int _glfwPlatformExtensionSupported(const char* extension) -{ - const char* extensions = eglQueryString(_glfw.egl.display, EGL_EXTENSIONS); - if (extensions) - { - if (_glfwStringInExtensionString(extension, extensions)) - return GLFW_TRUE; - } - - return GLFW_FALSE; -} - -GLFWglproc _glfwPlatformGetProcAddress(const char* procname) -{ - _GLFWwindow* window = _glfwPlatformGetCurrentContext(); - - if (window->context.egl.client) - { - GLFWglproc proc = (GLFWglproc) _glfw_dlsym(window->context.egl.client, - procname); - if (proc) - return proc; - } - - return eglGetProcAddress(procname); -} - - ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// ////////////////////////////////////////////////////////////////////////// @@ -642,7 +683,7 @@ GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* handle) _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_CONTEXT); - if (window->context.api == GLFW_NO_API) + if (window->context.client == GLFW_NO_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return EGL_NO_CONTEXT; @@ -656,7 +697,7 @@ GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* handle) _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_SURFACE); - if (window->context.api == GLFW_NO_API) + if (window->context.client == GLFW_NO_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return EGL_NO_SURFACE; diff --git a/src/egl_context.h b/src/egl_context.h index c73e1e02a..aed8a255d 100644 --- a/src/egl_context.h +++ b/src/egl_context.h @@ -104,6 +104,8 @@ typedef MirEGLNativeWindowType EGLNativeWindowType; #define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30fd #define EGL_CONTEXT_FLAGS_KHR 0x30fc #define EGL_CONTEXT_OPENGL_NO_ERROR_KHR 0x31b3 +#define EGL_GL_COLORSPACE_KHR 0x309d +#define EGL_GL_COLORSPACE_SRGB_KHR 0x3089 typedef int EGLint; typedef unsigned int EGLBoolean; @@ -147,9 +149,8 @@ typedef GLFWglproc (EGLAPIENTRY * PFNEGLGETPROCADDRESSPROC)(const char*); #define eglQueryString _glfw.egl.QueryString #define eglGetProcAddress _glfw.egl.GetProcAddress -#define _GLFW_PLATFORM_FBCONFIG EGLConfig egl -#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextEGL egl -#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryEGL egl +#define _GLFW_EGL_CONTEXT_STATE _GLFWcontextEGL egl +#define _GLFW_EGL_LIBRARY_CONTEXT_STATE _GLFWlibraryEGL egl // EGL-specific per-context data @@ -174,6 +175,7 @@ typedef struct _GLFWlibraryEGL GLFWbool KHR_create_context; GLFWbool KHR_create_context_no_error; + GLFWbool KHR_gl_colorspace; void* handle; @@ -202,7 +204,6 @@ void _glfwTerminateEGL(void); GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); -void _glfwDestroyContextEGL(_GLFWwindow* window); #if defined(_GLFW_X11) GLFWbool _glfwChooseVisualEGL(const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, diff --git a/src/glfw_config.h.in b/src/glfw_config.h.in index 6de975091..aed7f7a91 100644 --- a/src/glfw_config.h.in +++ b/src/glfw_config.h.in @@ -45,23 +45,12 @@ // Define this to 1 if building GLFW for Mir #cmakedefine _GLFW_MIR -// Define this to 1 if building GLFW for EGL -#cmakedefine _GLFW_EGL -// Define this to 1 if building GLFW for GLX -#cmakedefine _GLFW_GLX -// Define this to 1 if building GLFW for WGL -#cmakedefine _GLFW_WGL -// Define this to 1 if building GLFW for NSGL -#cmakedefine _GLFW_NSGL - // Define this to 1 if building as a shared library / dynamic library / DLL #cmakedefine _GLFW_BUILD_DLL // Define this to 1 to force use of high-performance GPU on hybrid systems #cmakedefine _GLFW_USE_HYBRID_HPG -// Define this to 1 if the XInput X11 extension is available -#cmakedefine _GLFW_HAS_XINPUT // Define this to 1 if the Xxf86vm X11 extension is available #cmakedefine _GLFW_HAS_XF86VM diff --git a/src/glx_context.c b/src/glx_context.c index 84c0c7015..1760582b8 100644 --- a/src/glx_context.c +++ b/src/glx_context.c @@ -115,18 +115,18 @@ static GLFWbool chooseFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* result if (_glfw.glx.ARB_framebuffer_sRGB || _glfw.glx.EXT_framebuffer_sRGB) u->sRGB = getFBConfigAttrib(n, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB); - u->glx = n; + u->handle = (uintptr_t) n; usableCount++; } closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); if (closest) - *result = closest->glx; + *result = (GLXFBConfig) closest->handle; XFree(nativeConfigs); free(usableConfigs); - return closest ? GLFW_TRUE : GLFW_FALSE; + return closest != NULL; } // Create the OpenGL context using legacy API @@ -142,6 +142,96 @@ static GLXContext createLegacyContext(_GLFWwindow* window, True); } +static void makeContextCurrent(_GLFWwindow* window) +{ + if (window) + { + if (!glXMakeCurrent(_glfw.x11.display, + window->context.glx.window, + window->context.glx.handle)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "GLX: Failed to make context current"); + return; + } + } + else + { + if (!glXMakeCurrent(_glfw.x11.display, None, NULL)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "GLX: Failed to clear current context"); + return; + } + } + + _glfwPlatformSetCurrentContext(window); +} + +static void swapBuffers(_GLFWwindow* window) +{ + glXSwapBuffers(_glfw.x11.display, window->context.glx.window); +} + +static void swapInterval(int interval) +{ + _GLFWwindow* window = _glfwPlatformGetCurrentContext(); + + if (_glfw.glx.EXT_swap_control) + { + _glfw.glx.SwapIntervalEXT(_glfw.x11.display, + window->context.glx.window, + interval); + } + else if (_glfw.glx.MESA_swap_control) + _glfw.glx.SwapIntervalMESA(interval); + else if (_glfw.glx.SGI_swap_control) + { + if (interval > 0) + _glfw.glx.SwapIntervalSGI(interval); + } +} + +static int extensionSupported(const char* extension) +{ + const char* extensions = + glXQueryExtensionsString(_glfw.x11.display, _glfw.x11.screen); + if (extensions) + { + if (_glfwStringInExtensionString(extension, extensions)) + return GLFW_TRUE; + } + + return GLFW_FALSE; +} + +static GLFWglproc getProcAddress(const char* procname) +{ + if (_glfw.glx.GetProcAddress) + return _glfw.glx.GetProcAddress((const GLubyte*) procname); + else if (_glfw.glx.GetProcAddressARB) + return _glfw.glx.GetProcAddressARB((const GLubyte*) procname); + else + return dlsym(_glfw.glx.handle, procname); +} + +// Destroy the OpenGL context +// +static void destroyContext(_GLFWwindow* window) +{ + if (window->context.glx.window) + { + glXDestroyWindow(_glfw.x11.display, window->context.glx.window); + window->context.glx.window = None; + } + + if (window->context.glx.handle) + { + glXDestroyContext(_glfw.x11.display, window->context.glx.handle); + window->context.glx.handle = NULL; + } +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// @@ -163,7 +253,6 @@ GLFWbool _glfwInitGLX(void) NULL }; - for (i = 0; sonames[i]; i++) { _glfw.glx.handle = dlopen(sonames[i], RTLD_LAZY | RTLD_GLOBAL); @@ -230,61 +319,61 @@ GLFWbool _glfwInitGLX(void) return GLFW_FALSE; } - if (_glfwPlatformExtensionSupported("GLX_EXT_swap_control")) + if (extensionSupported("GLX_EXT_swap_control")) { _glfw.glx.SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) - _glfwPlatformGetProcAddress("glXSwapIntervalEXT"); + getProcAddress("glXSwapIntervalEXT"); if (_glfw.glx.SwapIntervalEXT) _glfw.glx.EXT_swap_control = GLFW_TRUE; } - if (_glfwPlatformExtensionSupported("GLX_SGI_swap_control")) + if (extensionSupported("GLX_SGI_swap_control")) { _glfw.glx.SwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC) - _glfwPlatformGetProcAddress("glXSwapIntervalSGI"); + getProcAddress("glXSwapIntervalSGI"); if (_glfw.glx.SwapIntervalSGI) _glfw.glx.SGI_swap_control = GLFW_TRUE; } - if (_glfwPlatformExtensionSupported("GLX_MESA_swap_control")) + if (extensionSupported("GLX_MESA_swap_control")) { _glfw.glx.SwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC) - _glfwPlatformGetProcAddress("glXSwapIntervalMESA"); + getProcAddress("glXSwapIntervalMESA"); if (_glfw.glx.SwapIntervalMESA) _glfw.glx.MESA_swap_control = GLFW_TRUE; } - if (_glfwPlatformExtensionSupported("GLX_ARB_multisample")) + if (extensionSupported("GLX_ARB_multisample")) _glfw.glx.ARB_multisample = GLFW_TRUE; - if (_glfwPlatformExtensionSupported("GLX_ARB_framebuffer_sRGB")) + if (extensionSupported("GLX_ARB_framebuffer_sRGB")) _glfw.glx.ARB_framebuffer_sRGB = GLFW_TRUE; - if (_glfwPlatformExtensionSupported("GLX_EXT_framebuffer_sRGB")) + if (extensionSupported("GLX_EXT_framebuffer_sRGB")) _glfw.glx.EXT_framebuffer_sRGB = GLFW_TRUE; - if (_glfwPlatformExtensionSupported("GLX_ARB_create_context")) + if (extensionSupported("GLX_ARB_create_context")) { _glfw.glx.CreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) - _glfwPlatformGetProcAddress("glXCreateContextAttribsARB"); + getProcAddress("glXCreateContextAttribsARB"); if (_glfw.glx.CreateContextAttribsARB) _glfw.glx.ARB_create_context = GLFW_TRUE; } - if (_glfwPlatformExtensionSupported("GLX_ARB_create_context_robustness")) + if (extensionSupported("GLX_ARB_create_context_robustness")) _glfw.glx.ARB_create_context_robustness = GLFW_TRUE; - if (_glfwPlatformExtensionSupported("GLX_ARB_create_context_profile")) + if (extensionSupported("GLX_ARB_create_context_profile")) _glfw.glx.ARB_create_context_profile = GLFW_TRUE; - if (_glfwPlatformExtensionSupported("GLX_EXT_create_context_es2_profile")) + if (extensionSupported("GLX_EXT_create_context_es2_profile")) _glfw.glx.EXT_create_context_es2_profile = GLFW_TRUE; - if (_glfwPlatformExtensionSupported("GLX_ARB_context_flush_control")) + if (extensionSupported("GLX_ARB_context_flush_control")) _glfw.glx.ARB_context_flush_control = GLFW_TRUE; return GLFW_TRUE; @@ -331,7 +420,7 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, return GLFW_FALSE; } - if (ctxconfig->api == GLFW_OPENGL_ES_API) + if (ctxconfig->client == GLFW_OPENGL_ES_API) { if (!_glfw.glx.ARB_create_context || !_glfw.glx.ARB_create_context_profile || @@ -370,7 +459,7 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, { int index = 0, mask = 0, flags = 0; - if (ctxconfig->api == GLFW_OPENGL_API) + if (ctxconfig->client == GLFW_OPENGL_API) { if (ctxconfig->forward) flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; @@ -455,7 +544,7 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, if (!window->context.glx.handle) { if (_glfw.x11.errorCode == _glfw.glx.errorBase + GLXBadProfileARB && - ctxconfig->api == GLFW_OPENGL_API && + ctxconfig->client == GLFW_OPENGL_API && ctxconfig->profile == GLFW_OPENGL_ANY_PROFILE && ctxconfig->forward == GLFW_FALSE) { @@ -483,28 +572,18 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, return GLFW_FALSE; } + window->context.makeContextCurrent = makeContextCurrent; + window->context.swapBuffers = swapBuffers; + window->context.swapInterval = swapInterval; + window->context.extensionSupported = extensionSupported; + window->context.getProcAddress = getProcAddress; + window->context.destroyContext = destroyContext; + return GLFW_TRUE; } #undef setGLXattrib -// Destroy the OpenGL context -// -void _glfwDestroyContextGLX(_GLFWwindow* window) -{ - if (window->context.glx.window) - { - glXDestroyWindow(_glfw.x11.display, window->context.glx.window); - window->context.glx.window = None; - } - - if (window->context.glx.handle) - { - glXDestroyContext(_glfw.x11.display, window->context.glx.handle); - window->context.glx.handle = NULL; - } -} - // Returns the Visual and depth of the chosen GLXFBConfig // GLFWbool _glfwChooseVisualGLX(const _GLFWctxconfig* ctxconfig, @@ -537,72 +616,6 @@ GLFWbool _glfwChooseVisualGLX(const _GLFWctxconfig* ctxconfig, } -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -void _glfwPlatformMakeContextCurrent(_GLFWwindow* window) -{ - if (window) - { - glXMakeCurrent(_glfw.x11.display, - window->context.glx.window, - window->context.glx.handle); - } - else - glXMakeCurrent(_glfw.x11.display, None, NULL); - - _glfwPlatformSetCurrentContext(window); -} - -void _glfwPlatformSwapBuffers(_GLFWwindow* window) -{ - glXSwapBuffers(_glfw.x11.display, window->context.glx.window); -} - -void _glfwPlatformSwapInterval(int interval) -{ - _GLFWwindow* window = _glfwPlatformGetCurrentContext(); - - if (_glfw.glx.EXT_swap_control) - { - _glfw.glx.SwapIntervalEXT(_glfw.x11.display, - window->context.glx.window, - interval); - } - else if (_glfw.glx.MESA_swap_control) - _glfw.glx.SwapIntervalMESA(interval); - else if (_glfw.glx.SGI_swap_control) - { - if (interval > 0) - _glfw.glx.SwapIntervalSGI(interval); - } -} - -int _glfwPlatformExtensionSupported(const char* extension) -{ - const char* extensions = - glXQueryExtensionsString(_glfw.x11.display, _glfw.x11.screen); - if (extensions) - { - if (_glfwStringInExtensionString(extension, extensions)) - return GLFW_TRUE; - } - - return GLFW_FALSE; -} - -GLFWglproc _glfwPlatformGetProcAddress(const char* procname) -{ - if (_glfw.glx.GetProcAddress) - return _glfw.glx.GetProcAddress((const GLubyte*) procname); - else if (_glfw.glx.GetProcAddressARB) - return _glfw.glx.GetProcAddressARB((const GLubyte*) procname); - else - return dlsym(_glfw.glx.handle, procname); -} - - ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// ////////////////////////////////////////////////////////////////////////// @@ -612,7 +625,7 @@ GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* handle) _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - if (window->context.api == GLFW_NO_API) + if (window->context.client == GLFW_NO_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return NULL; @@ -626,7 +639,7 @@ GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* handle) _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(None); - if (window->context.api == GLFW_NO_API) + if (window->context.client == GLFW_NO_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return None; diff --git a/src/glx_context.h b/src/glx_context.h index dc54c7973..c60186a64 100644 --- a/src/glx_context.h +++ b/src/glx_context.h @@ -108,7 +108,6 @@ typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow); #define glXCreateWindow _glfw.glx.CreateWindow #define glXDestroyWindow _glfw.glx.DestroyWindow -#define _GLFW_PLATFORM_FBCONFIG GLXFBConfig glx #define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextGLX glx #define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryGLX glx diff --git a/src/init.c b/src/init.c index f245c7c14..3674dcf01 100644 --- a/src/init.c +++ b/src/init.c @@ -74,9 +74,9 @@ static const char* getErrorString(int error) return "The requested format is unavailable"; case GLFW_NO_WINDOW_CONTEXT: return "The specified window has no context"; + default: + return "ERROR: UNKNOWN GLFW ERROR"; } - - return "ERROR: UNKNOWN ERROR TOKEN PASSED TO glfwErrorString"; } @@ -135,6 +135,8 @@ GLFWAPI int glfwInit(void) _glfw.monitors = _glfwPlatformGetMonitors(&_glfw.monitorCount); _glfwInitialized = GLFW_TRUE; + _glfw.timerOffset = _glfwPlatformGetTimerValue(); + // Not all window hints have zero as their default value glfwDefaultWindowHints(); diff --git a/src/input.c b/src/input.c index 01184c324..287dab3cc 100644 --- a/src/input.c +++ b/src/input.c @@ -44,7 +44,7 @@ static void setCursorMode(_GLFWwindow* window, int newMode) newMode != GLFW_CURSOR_HIDDEN && newMode != GLFW_CURSOR_DISABLED) { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid cursor mode"); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid cursor mode %i", newMode); return; } @@ -220,6 +220,12 @@ void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths) window->callbacks.drop((GLFWwindow*) window, count, paths); } +void _glfwInputJoystickChange(int joy, int event) +{ + if (_glfw.callbacks.joystick) + _glfw.callbacks.joystick(joy, event); +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// @@ -240,7 +246,7 @@ GLFWbool _glfwIsPrintable(int key) GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(0); @@ -253,7 +259,7 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode) case GLFW_STICKY_MOUSE_BUTTONS: return window->stickyMouseButtons; default: - _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode"); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode %i", mode); return 0; } } @@ -261,7 +267,7 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode) GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT(); @@ -277,7 +283,7 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value) setStickyMouseButtons(window, value ? GLFW_TRUE : GLFW_FALSE); break; default: - _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode"); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode %i", mode); break; } } @@ -291,13 +297,13 @@ GLFWAPI const char* glfwGetKeyName(int key, int scancode) GLFWAPI int glfwGetKey(GLFWwindow* handle, int key) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_RELEASE); if (key < 0 || key > GLFW_KEY_LAST) { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid key"); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key); return GLFW_RELEASE; } @@ -314,14 +320,13 @@ GLFWAPI int glfwGetKey(GLFWwindow* handle, int key) GLFWAPI int glfwGetMouseButton(GLFWwindow* handle, int button) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_RELEASE); if (button < 0 || button > GLFW_MOUSE_BUTTON_LAST) { - _glfwInputError(GLFW_INVALID_ENUM, - "Invalid mouse button"); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid mouse button %i", button); return GLFW_RELEASE; } @@ -338,7 +343,7 @@ GLFWAPI int glfwGetMouseButton(GLFWwindow* handle, int button) GLFWAPI void glfwGetCursorPos(GLFWwindow* handle, double* xpos, double* ypos) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); if (xpos) *xpos = 0; @@ -361,7 +366,7 @@ GLFWAPI void glfwGetCursorPos(GLFWwindow* handle, double* xpos, double* ypos) GLFWAPI void glfwSetCursorPos(GLFWwindow* handle, double xpos, double ypos) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT(); @@ -385,7 +390,7 @@ GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot) { _GLFWcursor* cursor; - assert(image); + assert(image != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); @@ -415,7 +420,7 @@ GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape) shape != GLFW_HRESIZE_CURSOR && shape != GLFW_VRESIZE_CURSOR) { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid standard cursor"); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid standard cursor %i", shape); return NULL; } @@ -471,7 +476,7 @@ GLFWAPI void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle) { _GLFWwindow* window = (_GLFWwindow*) windowHandle; _GLFWcursor* cursor = (_GLFWcursor*) cursorHandle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT(); @@ -483,7 +488,7 @@ GLFWAPI void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle) GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.key, cbfun); @@ -493,7 +498,7 @@ GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun) GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* handle, GLFWcharfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.character, cbfun); @@ -503,7 +508,7 @@ GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* handle, GLFWcharfun cbfun) GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* handle, GLFWcharmodsfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.charmods, cbfun); @@ -514,7 +519,7 @@ GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* handle, GLFWmousebuttonfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.mouseButton, cbfun); @@ -525,7 +530,7 @@ GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* handle, GLFWcursorposfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.cursorPos, cbfun); @@ -536,7 +541,7 @@ GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* handle, GLFWcursorenterfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.cursorEnter, cbfun); @@ -547,7 +552,7 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* handle, GLFWscrollfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.scroll, cbfun); @@ -557,7 +562,7 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* handle, GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.drop, cbfun); @@ -570,7 +575,7 @@ GLFWAPI int glfwJoystickPresent(int joy) if (joy < 0 || joy > GLFW_JOYSTICK_LAST) { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick"); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", joy); return 0; } @@ -579,14 +584,14 @@ GLFWAPI int glfwJoystickPresent(int joy) GLFWAPI const float* glfwGetJoystickAxes(int joy, int* count) { - assert(count); + assert(count != NULL); *count = 0; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); if (joy < 0 || joy > GLFW_JOYSTICK_LAST) { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick"); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", joy); return NULL; } @@ -595,14 +600,14 @@ GLFWAPI const float* glfwGetJoystickAxes(int joy, int* count) GLFWAPI const unsigned char* glfwGetJoystickButtons(int joy, int* count) { - assert(count); + assert(count != NULL); *count = 0; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); if (joy < 0 || joy > GLFW_JOYSTICK_LAST) { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick"); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", joy); return NULL; } @@ -615,19 +620,26 @@ GLFWAPI const char* glfwGetJoystickName(int joy) if (joy < 0 || joy > GLFW_JOYSTICK_LAST) { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick"); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", joy); return NULL; } return _glfwPlatformGetJoystickName(joy); } +GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(_glfw.callbacks.joystick, cbfun); + return cbfun; +} + GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); - assert(string); + assert(string != NULL); _GLFW_REQUIRE_INIT(); _glfwPlatformSetClipboardString(window, string); @@ -636,7 +648,7 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string) GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); return _glfwPlatformGetClipboardString(window); @@ -645,7 +657,8 @@ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle) GLFWAPI double glfwGetTime(void) { _GLFW_REQUIRE_INIT_OR_RETURN(0.0); - return _glfwPlatformGetTime(); + return (double) (_glfwPlatformGetTimerValue() - _glfw.timerOffset) / + _glfwPlatformGetTimerFrequency(); } GLFWAPI void glfwSetTime(double time) @@ -654,10 +667,23 @@ GLFWAPI void glfwSetTime(double time) if (time != time || time < 0.0 || time > 18446744073.0) { - _glfwInputError(GLFW_INVALID_VALUE, "Invalid time"); + _glfwInputError(GLFW_INVALID_VALUE, "Invalid time %f", time); return; } - _glfwPlatformSetTime(time); + _glfw.timerOffset = _glfwPlatformGetTimerValue() - + (uint64_t) (time * _glfwPlatformGetTimerFrequency()); +} + +GLFWAPI uint64_t glfwGetTimerValue(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(0); + return _glfwPlatformGetTimerValue(); +} + +GLFWAPI uint64_t glfwGetTimerFrequency(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(0); + return _glfwPlatformGetTimerFrequency(); } diff --git a/src/internal.h b/src/internal.h index 8003b64aa..f5d4f06f8 100644 --- a/src/internal.h +++ b/src/internal.h @@ -47,15 +47,6 @@ #define GLFW_INCLUDE_NONE #include "../include/GLFW/glfw3.h" -#include - -#if defined(_MSC_VER) && (_MSC_VER < 1600) -typedef unsigned __int64 GLFWuint64; -#else - #include -typedef uint64_t GLFWuint64; -#endif - typedef int GLFWbool; typedef struct _GLFWwndconfig _GLFWwndconfig; @@ -67,6 +58,13 @@ typedef struct _GLFWlibrary _GLFWlibrary; typedef struct _GLFWmonitor _GLFWmonitor; typedef struct _GLFWcursor _GLFWcursor; +typedef void (* _GLFWmakecontextcurrentfun)(_GLFWwindow*); +typedef void (* _GLFWswapbuffersfun)(_GLFWwindow*); +typedef void (* _GLFWswapintervalfun)(int); +typedef int (* _GLFWextensionsupportedfun)(const char*); +typedef GLFWglproc (* _GLFWgetprocaddressfun)(const char*); +typedef void (* _GLFWdestroycontextfun)(_GLFWwindow*); + #define GL_VERSION 0x1f02 #define GL_NONE 0 #define GL_COLOR_BUFFER_BIT 0x00004000 @@ -100,9 +98,9 @@ typedef const GLubyte* (APIENTRY * PFNGLGETSTRINGIPROC)(GLenum,GLuint); typedef void* VkInstance; typedef void* VkPhysicalDevice; -typedef GLFWuint64 VkSurfaceKHR; -typedef unsigned int VkFlags; -typedef unsigned int VkBool32; +typedef uint64_t VkSurfaceKHR; +typedef uint32_t VkFlags; +typedef uint32_t VkBool32; typedef enum VkStructureType { @@ -147,12 +145,12 @@ typedef struct VkAllocationCallbacks VkAllocationCallbacks; typedef struct VkExtensionProperties { char extensionName[256]; - unsigned int specVersion; + uint32_t specVersion; } VkExtensionProperties; typedef void (APIENTRY * PFN_vkVoidFunction)(void); typedef PFN_vkVoidFunction (APIENTRY * PFN_vkGetInstanceProcAddr)(VkInstance,const char*); -typedef VkResult (APIENTRY * PFN_vkEnumerateInstanceExtensionProperties)(const char*,unsigned int*,VkExtensionProperties*); +typedef VkResult (APIENTRY * PFN_vkEnumerateInstanceExtensionProperties)(const char*,uint32_t*,VkExtensionProperties*); #define vkEnumerateInstanceExtensionProperties _glfw.vk.EnumerateInstanceExtensionProperties #define vkGetInstanceProcAddr _glfw.vk.GetInstanceProcAddr @@ -255,8 +253,7 @@ struct _GLFWwndconfig GLFWbool focused; GLFWbool autoIconify; GLFWbool floating; - GLFWbool transparent; - _GLFWmonitor* monitor; + GLFWbool maximized; }; @@ -268,7 +265,8 @@ struct _GLFWwndconfig */ struct _GLFWctxconfig { - int api; + int client; + int source; int major; int minor; GLFWbool forward; @@ -306,10 +304,7 @@ struct _GLFWfbconfig int samples; GLFWbool sRGB; GLFWbool doublebuffer; - int transparent; - - // This is defined in the context API's context.h - _GLFW_PLATFORM_FBCONFIG; + uintptr_t handle; }; @@ -317,7 +312,8 @@ struct _GLFWfbconfig */ struct _GLFWcontext { - int api; + int client; + int source; int major, minor, revision; GLFWbool forward, debug, noerror; int profile; @@ -328,8 +324,17 @@ struct _GLFWcontext PFNGLGETINTEGERVPROC GetIntegerv; PFNGLGETSTRINGPROC GetString; + _GLFWmakecontextcurrentfun makeContextCurrent; + _GLFWswapbuffersfun swapBuffers; + _GLFWswapintervalfun swapInterval; + _GLFWextensionsupportedfun extensionSupported; + _GLFWgetprocaddressfun getProcAddress; + _GLFWdestroycontextfun destroyContext; + // This is defined in the context API's context.h _GLFW_PLATFORM_CONTEXT_STATE; + // This is defined in egl_context.h + _GLFW_EGL_CONTEXT_STATE; }; @@ -351,6 +356,10 @@ struct _GLFWwindow _GLFWmonitor* monitor; _GLFWcursor* cursor; + int minwidth, minheight; + int maxwidth, maxheight; + int numer, denom; + // Window input state GLFWbool stickyKeys; GLFWbool stickyMouseButtons; @@ -393,6 +402,9 @@ struct _GLFWmonitor // Physical dimensions in millimeters. int widthMM, heightMM; + // The window whose video mode is current on this monitor + _GLFWwindow* window; + GLFWvidmode* modes; int modeCount; GLFWvidmode currentMode; @@ -436,11 +448,13 @@ struct _GLFWlibrary _GLFWmonitor** monitors; int monitorCount; + uint64_t timerOffset; + struct { GLFWbool available; void* handle; char** extensions; - int extensionCount; + uint32_t extensionCount; PFN_vkEnumerateInstanceExtensionProperties EnumerateInstanceExtensionProperties; PFN_vkGetInstanceProcAddr GetInstanceProcAddr; GLFWbool KHR_surface; @@ -453,6 +467,7 @@ struct _GLFWlibrary struct { GLFWmonitorfun monitor; + GLFWjoystickfun joystick; } callbacks; // This is defined in the window API's platform.h @@ -465,6 +480,8 @@ struct _GLFWlibrary _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE; // This is defined in the platform's tls.h _GLFW_PLATFORM_LIBRARY_TLS_STATE; + // This is defined in egl_context.h + _GLFW_EGL_LIBRARY_CONTEXT_STATE; }; @@ -600,15 +617,15 @@ const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count); */ const char* _glfwPlatformGetJoystickName(int joy); -/*! @copydoc glfwGetTime +/*! @copydoc glfwGetTimerValue * @ingroup platform */ -double _glfwPlatformGetTime(void); +uint64_t _glfwPlatformGetTimerValue(void); -/*! @copydoc glfwSetTime +/*! @copydoc glfwGetTimerFrequency * @ingroup platform */ -void _glfwPlatformSetTime(double time); +uint64_t _glfwPlatformGetTimerFrequency(void); /*! @ingroup platform */ @@ -626,6 +643,11 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window); */ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title); +/*! @copydoc glfwSetWindowIcon + * @ingroup platform + */ +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count, const GLFWimage* images); + /*! @copydoc glfwGetWindowPos * @ingroup platform */ @@ -676,20 +698,31 @@ void _glfwPlatformIconifyWindow(_GLFWwindow* window); */ void _glfwPlatformRestoreWindow(_GLFWwindow* window); +/*! @copydoc glfwMaximizeWindow + * @ingroup platform + */ +void _glfwPlatformMaximizeWindow(_GLFWwindow* window); + /*! @copydoc glfwShowWindow * @ingroup platform */ void _glfwPlatformShowWindow(_GLFWwindow* window); -/*! @ingroup platform - */ -void _glfwPlatformUnhideWindow(_GLFWwindow* window); - /*! @copydoc glfwHideWindow * @ingroup platform */ void _glfwPlatformHideWindow(_GLFWwindow* window); +/*! @copydoc glfwFocusWindow + * @ingroup platform + */ +void _glfwPlatformFocusWindow(_GLFWwindow* window); + +/*! @copydoc glfwSetWindowMonitor + * @ingroup platform + */ +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); + /*! @brief Returns whether the window is focused. * @ingroup platform */ @@ -705,6 +738,11 @@ int _glfwPlatformWindowIconified(_GLFWwindow* window); */ int _glfwPlatformWindowVisible(_GLFWwindow* window); +/*! @brief Returns whether the window is maximized. + * @ingroup platform + */ +int _glfwPlatformWindowMaximized(_GLFWwindow* window); + /*! @copydoc glfwPollEvents * @ingroup platform */ @@ -715,16 +753,16 @@ void _glfwPlatformPollEvents(void); */ void _glfwPlatformWaitEvents(void); +/*! @copydoc glfwWaitEventsTimeout + * @ingroup platform + */ +void _glfwPlatformWaitEventsTimeout(double timeout); + /*! @copydoc glfwPostEmptyEvent * @ingroup platform */ void _glfwPlatformPostEmptyEvent(void); -/*! @copydoc glfwMakeContextCurrent - * @ingroup platform - */ -void _glfwPlatformMakeContextCurrent(_GLFWwindow* window); - /*! @ingroup platform */ void _glfwPlatformSetCurrentContext(_GLFWwindow* context); @@ -734,26 +772,6 @@ void _glfwPlatformSetCurrentContext(_GLFWwindow* context); */ _GLFWwindow* _glfwPlatformGetCurrentContext(void); -/*! @copydoc glfwSwapBuffers - * @ingroup platform - */ -void _glfwPlatformSwapBuffers(_GLFWwindow* window); - -/*! @copydoc glfwSwapInterval - * @ingroup platform - */ -void _glfwPlatformSwapInterval(int interval); - -/*! @copydoc glfwExtensionSupported - * @ingroup platform - */ -int _glfwPlatformExtensionSupported(const char* extension); - -/*! @copydoc glfwGetProcAddress - * @ingroup platform - */ -GLFWglproc _glfwPlatformGetProcAddress(const char* procname); - /*! @copydoc glfwCreateCursor * @ingroup platform */ @@ -776,11 +794,11 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor); /*! @ingroup platform */ -char** _glfwPlatformGetRequiredInstanceExtensions(int* count); +char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count); /*! @ingroup platform */ -int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, unsigned int queuefamily); +int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); /*! @ingroup platform */ @@ -842,6 +860,8 @@ void _glfwInputWindowDamage(_GLFWwindow* window); */ void _glfwInputWindowCloseRequest(_GLFWwindow* window); +void _glfwInputWindowMonitorChange(_GLFWwindow* window, _GLFWmonitor* monitor); + /*! @brief Notifies shared code of a physical key event. * @param[in] window The window that received the event. * @param[in] key The key that was pressed or released. @@ -900,6 +920,10 @@ void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered); */ void _glfwInputMonitorChange(void); +/*! @ingroup event + */ +void _glfwInputMonitorWindowChange(_GLFWmonitor* monitor, _GLFWwindow* window); + /*! @brief Notifies shared code of an error. * @param[in] error The error code most suitable for the error. * @param[in] format The `printf` style format string of the error @@ -920,6 +944,13 @@ void _glfwInputError(int error, const char* format, ...); */ void _glfwInputDrop(_GLFWwindow* window, int count, const char** names); +/*! @brief Notifies shared code of a joystick connection/disconnection event. + * @param[in] joy The joystick that was connected or disconnected. + * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. + * @ingroup event + */ +void _glfwInputJoystickChange(int joy, int event); + //======================================================================== // Utility functions @@ -980,15 +1011,6 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig); */ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig); -/*! @brief Checks whether the current context fulfils the specified hard - * constraints. - * @param[in] ctxconfig The desired context attributes. - * @return `GLFW_TRUE` if the context fulfils the hard constraints, or - * `GLFW_FALSE` otherwise. - * @ingroup utility - */ -GLFWbool _glfwIsValidContext(const _GLFWctxconfig* ctxconfig); - /*! @ingroup utility */ void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size); diff --git a/src/linux_joystick.c b/src/linux_joystick.c index e3f8bb38c..e3e03cf17 100644 --- a/src/linux_joystick.c +++ b/src/linux_joystick.c @@ -45,9 +45,9 @@ // Attempt to open the specified joystick device // +#if defined(__linux__) static GLFWbool openJoystickDevice(const char* path) { -#if defined(__linux__) char axisCount, buttonCount; char name[256]; int joy, fd, version; @@ -100,34 +100,18 @@ static GLFWbool openJoystickDevice(const char* path) ioctl(fd, JSIOCGBUTTONS, &buttonCount); js->buttonCount = (int) buttonCount; js->buttons = calloc(buttonCount, 1); -#endif // __linux__ + + _glfwInputJoystickChange(joy, GLFW_CONNECTED); return GLFW_TRUE; } +#endif // __linux__ // Polls for and processes events the specified joystick // static GLFWbool pollJoystickEvents(_GLFWjoystickLinux* js) { #if defined(__linux__) - ssize_t offset = 0; - char buffer[16384]; - - const ssize_t size = read(_glfw.linux_js.inotify, buffer, sizeof(buffer)); - - while (size > offset) - { - regmatch_t match; - const struct inotify_event* e = (struct inotify_event*) (buffer + offset); - - if (regexec(&_glfw.linux_js.regex, e->name, 1, &match, 0) == 0) - { - char path[20]; - snprintf(path, sizeof(path), "/dev/input/%s", e->name); - openJoystickDevice(path); - } - - offset += sizeof(struct inotify_event) + e->len; - } + _glfwPollJoystickEvents(); if (!js->present) return GLFW_FALSE; @@ -149,6 +133,9 @@ static GLFWbool pollJoystickEvents(_GLFWjoystickLinux* js) free(js->path); memset(js, 0, sizeof(_GLFWjoystickLinux)); + + _glfwInputJoystickChange(js - _glfw.linux_js.js, + GLFW_DISCONNECTED); } break; @@ -168,12 +155,14 @@ static GLFWbool pollJoystickEvents(_GLFWjoystickLinux* js) // Lexically compare joysticks, used by quicksort // +#if defined(__linux__) static int compareJoysticks(const void* fp, const void* sp) { const _GLFWjoystickLinux* fj = fp; const _GLFWjoystickLinux* sj = sp; return strcmp(fj->path, sj->path); } +#endif // __linux__ ////////////////////////////////////////////////////////////////////////// @@ -285,6 +274,31 @@ void _glfwTerminateJoysticksLinux(void) #endif // __linux__ } +void _glfwPollJoystickEvents(void) +{ +#if defined(__linux__) + ssize_t offset = 0; + char buffer[16384]; + + const ssize_t size = read(_glfw.linux_js.inotify, buffer, sizeof(buffer)); + + while (size > offset) + { + regmatch_t match; + const struct inotify_event* e = (struct inotify_event*) (buffer + offset); + + if (regexec(&_glfw.linux_js.regex, e->name, 1, &match, 0) == 0) + { + char path[20]; + snprintf(path, sizeof(path), "/dev/input/%s", e->name); + openJoystickDevice(path); + } + + offset += sizeof(struct inotify_event) + e->len; + } +#endif +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// diff --git a/src/linux_joystick.h b/src/linux_joystick.h index 6f34d5760..c949e81d6 100644 --- a/src/linux_joystick.h +++ b/src/linux_joystick.h @@ -64,4 +64,6 @@ typedef struct _GLFWjoylistLinux GLFWbool _glfwInitJoysticksLinux(void); void _glfwTerminateJoysticksLinux(void); +void _glfwPollJoystickEvents(void); + #endif // _glfw3_linux_joystick_h_ diff --git a/src/mir_init.c b/src/mir_init.c index 0ca57a663..3f649670d 100644 --- a/src/mir_init.c +++ b/src/mir_init.c @@ -26,6 +26,7 @@ #include "internal.h" +#include #include #include diff --git a/src/mir_platform.h b/src/mir_platform.h index 5d328c208..66c6776db 100644 --- a/src/mir_platform.h +++ b/src/mir_platform.h @@ -51,12 +51,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceMirPresentationSupportKHR)(Vk #include "posix_time.h" #include "linux_joystick.h" #include "xkb_unicode.h" - -#if defined(_GLFW_EGL) - #include "egl_context.h" -#else - #error "The Mir backend depends on EGL platform support" -#endif +#include "egl_context.h" #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) #define _glfw_dlclose(handle) dlclose(handle) @@ -70,6 +65,9 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceMirPresentationSupportKHR)(Vk #define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryMir mir #define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorMir mir +#define _GLFW_PLATFORM_CONTEXT_STATE +#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE + // Mir-specific Event Queue // diff --git a/src/mir_window.c b/src/mir_window.c index 88875f976..ddc70fa63 100644 --- a/src/mir_window.c +++ b/src/mir_window.c @@ -160,10 +160,15 @@ static void handlePointerButton(_GLFWwindow* window, int pressed, const MirPointerEvent* pointer_event) { - MirPointerButton button = mir_pointer_event_buttons (pointer_event); int mods = mir_pointer_event_modifiers(pointer_event); const int publicMods = mirModToGLFWMod(mods); - int publicButton; + MirPointerButton button = mir_pointer_button_primary; + static uint32_t oldButtonStates = 0; + uint32_t newButtonStates = mir_pointer_event_buttons(pointer_event); + int publicButton = GLFW_MOUSE_BUTTON_LEFT; + + // XOR our old button states our new states to figure out what was added or removed + button = newButtonStates ^ oldButtonStates; switch (button) { @@ -188,6 +193,8 @@ static void handlePointerButton(_GLFWwindow* window, break; } + oldButtonStates = newButtonStates; + _glfwInputMouseClick(window, publicButton, pressed, publicMods); } @@ -345,16 +352,10 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { - if (ctxconfig->api != GLFW_NO_API) - { - if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) - return GLFW_FALSE; - } - - if (wndconfig->monitor) + if (window->monitor) { GLFWvidmode mode; - _glfwPlatformGetVideoMode(wndconfig->monitor, &mode); + _glfwPlatformGetVideoMode(window->monitor, &mode); mir_surface_set_state(window->mir.surface, mir_surface_state_fullscreen); @@ -377,6 +378,12 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, window->mir.window = mir_buffer_stream_get_egl_native_window( mir_surface_get_buffer_stream(window->mir.surface)); + if (ctxconfig->client != GLFW_NO_API) + { + if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + return GLFW_TRUE; } @@ -388,7 +395,8 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) window->mir.surface = NULL; } - _glfwDestroyContextEGL(window); + if (window->context.client != GLFW_NO_API) + window->context.destroyContext(window); } void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) @@ -403,6 +411,13 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) mir_surface_spec_release(spec); } +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, + int count, const GLFWimage* images) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) { MirSurfaceSpec* spec; @@ -467,6 +482,12 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window) mir_surface_set_state(window->mir.surface, mir_surface_state_restored); } +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + void _glfwPlatformHideWindow(_GLFWwindow* window) { MirSurfaceSpec* spec; @@ -489,7 +510,17 @@ void _glfwPlatformShowWindow(_GLFWwindow* window) mir_surface_spec_release(spec); } -void _glfwPlatformUnhideWindow(_GLFWwindow* window) +void _glfwPlatformFocusWindow(_GLFWwindow* window) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) { _glfwInputError(GLFW_PLATFORM_ERROR, "Mir: Unsupported function %s", __PRETTY_FUNCTION__); @@ -514,6 +545,13 @@ int _glfwPlatformWindowVisible(_GLFWwindow* window) return mir_surface_get_visibility(window->mir.surface) == mir_surface_visibility_exposed; } +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); + return GLFW_FALSE; +} + void _glfwPlatformPollEvents(void) { EventNode* node = NULL; @@ -537,6 +575,24 @@ void _glfwPlatformWaitEvents(void) _glfwPlatformPollEvents(); } +void _glfwPlatformWaitEventsTimeout(double timeout) +{ + pthread_mutex_lock(&_glfw.mir.event_mutex); + + if (emptyEventQueue(_glfw.mir.event_queue)) + { + struct timespec time; + clock_gettime(CLOCK_REALTIME, &time); + time.tv_sec += (long) timeout; + time.tv_nsec += (long) ((timeout - (long) timeout) * 1e9); + pthread_cond_timedwait(&_glfw.mir.event_cond, &_glfw.mir.event_mutex, &time); + } + + pthread_mutex_unlock(&_glfw.mir.event_mutex); + + _glfwPlatformPollEvents(); +} + void _glfwPlatformPostEmptyEvent(void) { } @@ -653,11 +709,6 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) mir_wait_for(mir_surface_configure_cursor(window->mir.surface, cursor->mir.conf)); if (cursor->mir.custom_cursor) { - /* FIXME Bug https://bugs.launchpad.net/mir/+bug/1477285 - Requires a triple buffer swap to get the cursor buffer on top! (since mir is tripled buffered) - */ - mir_buffer_stream_swap_buffers_sync(cursor->mir.custom_cursor); - mir_buffer_stream_swap_buffers_sync(cursor->mir.custom_cursor); mir_buffer_stream_swap_buffers_sync(cursor->mir.custom_cursor); } } @@ -706,7 +757,7 @@ const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) return NULL; } -char** _glfwPlatformGetRequiredInstanceExtensions(int* count) +char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count) { char** extensions; @@ -725,7 +776,7 @@ char** _glfwPlatformGetRequiredInstanceExtensions(int* count) int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, - unsigned int queuefamily) + uint32_t queuefamily) { PFN_vkGetPhysicalDeviceMirPresentationSupportKHR vkGetPhysicalDeviceMirPresentationSupportKHR = (PFN_vkGetPhysicalDeviceMirPresentationSupportKHR) @@ -762,8 +813,8 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, memset(&sci, 0, sizeof(sci)); sci.sType = VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR; - sci.display = _glfw.mir.connection; - sci.surface = window->mir.surface; + sci.connection = _glfw.mir.connection; + sci.mirSurface = window->mir.surface; err = vkCreateMirSurfaceKHR(instance, &sci, allocator, surface); if (err) diff --git a/src/monitor.c b/src/monitor.c index 1aebf0ba8..f63aff9be 100644 --- a/src/monitor.c +++ b/src/monitor.c @@ -126,7 +126,11 @@ void _glfwInputMonitorChange(void) for (window = _glfw.windowListHead; window; window = window->next) { if (window->monitor == monitors[i]) - window->monitor = NULL; + { + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + _glfwPlatformSetWindowMonitor(window, NULL, 0, 0, width, height, 0); + } } if (_glfw.callbacks.monitor) @@ -158,6 +162,11 @@ void _glfwInputMonitorChange(void) _glfwFreeMonitors(monitors, monitorCount); } +void _glfwInputMonitorWindowChange(_GLFWmonitor* monitor, _GLFWwindow* window) +{ + monitor->window = window; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// @@ -294,7 +303,7 @@ void _glfwSplitBPP(int bpp, int* red, int* green, int* blue) GLFWAPI GLFWmonitor** glfwGetMonitors(int* count) { - assert(count); + assert(count != NULL); *count = 0; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); @@ -316,7 +325,7 @@ GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void) GLFWAPI void glfwGetMonitorPos(GLFWmonitor* handle, int* xpos, int* ypos) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - assert(monitor); + assert(monitor != NULL); if (xpos) *xpos = 0; @@ -331,7 +340,7 @@ GLFWAPI void glfwGetMonitorPos(GLFWmonitor* handle, int* xpos, int* ypos) GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* handle, int* widthMM, int* heightMM) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - assert(monitor); + assert(monitor != NULL); if (widthMM) *widthMM = 0; @@ -349,7 +358,7 @@ GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* handle, int* widthMM, int* GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* handle) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - assert(monitor); + assert(monitor != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); return monitor->name; @@ -365,9 +374,9 @@ GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun) GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* handle, int* count) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - assert(monitor); + assert(monitor != NULL); - assert(count); + assert(count != NULL); *count = 0; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); @@ -382,7 +391,7 @@ GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* handle, int* count) GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* handle) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - assert(monitor); + assert(monitor != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); @@ -400,7 +409,7 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma) if (gamma != gamma || gamma <= 0.f || gamma > FLT_MAX) { - _glfwInputError(GLFW_INVALID_VALUE, "Invalid gamma value"); + _glfwInputError(GLFW_INVALID_VALUE, "Invalid gamma value %f", gamma); return; } @@ -431,7 +440,7 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma) GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* handle) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - assert(monitor); + assert(monitor != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); @@ -444,9 +453,9 @@ GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* handle) GLFWAPI void glfwSetGammaRamp(GLFWmonitor* handle, const GLFWgammaramp* ramp) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - assert(monitor); + assert(monitor != NULL); - assert(ramp); + assert(ramp != NULL); _GLFW_REQUIRE_INIT(); diff --git a/src/nsgl_context.h b/src/nsgl_context.h index 6a0a22fd0..ab4cf3dfe 100644 --- a/src/nsgl_context.h +++ b/src/nsgl_context.h @@ -27,7 +27,6 @@ #ifndef _glfw3_nsgl_context_h_ #define _glfw3_nsgl_context_h_ -#define _GLFW_PLATFORM_FBCONFIG #define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextNSGL nsgl #define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryNSGL nsgl diff --git a/src/nsgl_context.m b/src/nsgl_context.m index afbef6cdd..122ef0bf8 100644 --- a/src/nsgl_context.m +++ b/src/nsgl_context.m @@ -27,6 +27,63 @@ #include "internal.h" +static void makeContextCurrent(_GLFWwindow* window) +{ + if (window) + [window->context.nsgl.object makeCurrentContext]; + else + [NSOpenGLContext clearCurrentContext]; + + _glfwPlatformSetCurrentContext(window); +} + +static void swapBuffers(_GLFWwindow* window) +{ + // ARP appears to be unnecessary, but this is future-proof + [window->context.nsgl.object flushBuffer]; +} + +static void swapInterval(int interval) +{ + _GLFWwindow* window = _glfwPlatformGetCurrentContext(); + + GLint sync = interval; + [window->context.nsgl.object setValues:&sync + forParameter:NSOpenGLCPSwapInterval]; +} + +static int extensionSupported(const char* extension) +{ + // There are no NSGL extensions + return GLFW_FALSE; +} + +static GLFWglproc getProcAddress(const char* procname) +{ + CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, + procname, + kCFStringEncodingASCII); + + GLFWglproc symbol = CFBundleGetFunctionPointerForName(_glfw.nsgl.framework, + symbolName); + + CFRelease(symbolName); + + return symbol; +} + +// Destroy the OpenGL context +// +static void destroyContext(_GLFWwindow* window) +{ + [window->context.nsgl.pixelFormat release]; + window->context.nsgl.pixelFormat = nil; + + [window->context.nsgl.object release]; + window->context.nsgl.object = nil; +} + + ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// @@ -61,7 +118,7 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, { unsigned int attributeCount = 0; - if (ctxconfig->api == GLFW_OPENGL_ES_API) + if (ctxconfig->client == GLFW_OPENGL_ES_API) { _glfwInputError(GLFW_API_UNAVAILABLE, "NSGL: OpenGL ES is not available on OS X"); @@ -222,70 +279,17 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, } [window->context.nsgl.object setView:window->ns.view]; + + window->context.makeContextCurrent = makeContextCurrent; + window->context.swapBuffers = swapBuffers; + window->context.swapInterval = swapInterval; + window->context.extensionSupported = extensionSupported; + window->context.getProcAddress = getProcAddress; + window->context.destroyContext = destroyContext; + return GLFW_TRUE; } -// Destroy the OpenGL context -// -void _glfwDestroyContextNSGL(_GLFWwindow* window) -{ - [window->context.nsgl.pixelFormat release]; - window->context.nsgl.pixelFormat = nil; - - [window->context.nsgl.object release]; - window->context.nsgl.object = nil; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -void _glfwPlatformMakeContextCurrent(_GLFWwindow* window) -{ - if (window) - [window->context.nsgl.object makeCurrentContext]; - else - [NSOpenGLContext clearCurrentContext]; - - _glfwPlatformSetCurrentContext(window); -} - -void _glfwPlatformSwapBuffers(_GLFWwindow* window) -{ - // ARP appears to be unnecessary, but this is future-proof - [window->context.nsgl.object flushBuffer]; -} - -void _glfwPlatformSwapInterval(int interval) -{ - _GLFWwindow* window = _glfwPlatformGetCurrentContext(); - - GLint sync = interval; - [window->context.nsgl.object setValues:&sync - forParameter:NSOpenGLCPSwapInterval]; -} - -int _glfwPlatformExtensionSupported(const char* extension) -{ - // There are no NSGL extensions - return GLFW_FALSE; -} - -GLFWglproc _glfwPlatformGetProcAddress(const char* procname) -{ - CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, - procname, - kCFStringEncodingASCII); - - GLFWglproc symbol = CFBundleGetFunctionPointerForName(_glfw.nsgl.framework, - symbolName); - - CFRelease(symbolName); - - return symbol; -} - ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// @@ -296,7 +300,7 @@ GLFWAPI id glfwGetNSGLContext(GLFWwindow* handle) _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(nil); - if (window->context.api == GLFW_NO_API) + if (window->context.client == GLFW_NO_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return NULL; diff --git a/src/posix_time.c b/src/posix_time.c index ac664124e..7b2d1ab29 100644 --- a/src/posix_time.c +++ b/src/posix_time.c @@ -30,28 +30,6 @@ #include #include -// Return raw time -// -static uint64_t getRawTime(void) -{ -#if defined(CLOCK_MONOTONIC) - if (_glfw.posix_time.monotonic) - { - struct timespec ts; - - clock_gettime(CLOCK_MONOTONIC, &ts); - return (uint64_t) ts.tv_sec * (uint64_t) 1000000000 + (uint64_t) ts.tv_nsec; - } - else -#endif - { - struct timeval tv; - - gettimeofday(&tv, NULL); - return (uint64_t) tv.tv_sec * (uint64_t) 1000000 + (uint64_t) tv.tv_usec; - } -} - ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// @@ -67,15 +45,14 @@ void _glfwInitTimerPOSIX(void) if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { _glfw.posix_time.monotonic = GLFW_TRUE; - _glfw.posix_time.resolution = 1e-9; + _glfw.posix_time.frequency = 1000000000; } else #endif { - _glfw.posix_time.resolution = 1e-6; + _glfw.posix_time.monotonic = GLFW_FALSE; + _glfw.posix_time.frequency = 1000000; } - - _glfw.posix_time.base = getRawTime(); } @@ -83,15 +60,26 @@ void _glfwInitTimerPOSIX(void) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -double _glfwPlatformGetTime(void) +uint64_t _glfwPlatformGetTimerValue(void) { - return (double) (getRawTime() - _glfw.posix_time.base) * - _glfw.posix_time.resolution; +#if defined(CLOCK_MONOTONIC) + if (_glfw.posix_time.monotonic) + { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t) ts.tv_sec * (uint64_t) 1000000000 + (uint64_t) ts.tv_nsec; + } + else +#endif + { + struct timeval tv; + gettimeofday(&tv, NULL); + return (uint64_t) tv.tv_sec * (uint64_t) 1000000 + (uint64_t) tv.tv_usec; + } } -void _glfwPlatformSetTime(double time) +uint64_t _glfwPlatformGetTimerFrequency(void) { - _glfw.posix_time.base = getRawTime() - - (uint64_t) (time / _glfw.posix_time.resolution); + return _glfw.posix_time.frequency; } diff --git a/src/posix_time.h b/src/posix_time.h index b3061fda9..237db3a99 100644 --- a/src/posix_time.h +++ b/src/posix_time.h @@ -38,8 +38,7 @@ typedef struct _GLFWtimePOSIX { GLFWbool monotonic; - double resolution; - uint64_t base; + uint64_t frequency; } _GLFWtimePOSIX; diff --git a/src/vulkan.c b/src/vulkan.c index b7b67fe57..fb62a18fd 100644 --- a/src/vulkan.c +++ b/src/vulkan.c @@ -40,7 +40,7 @@ void _glfwInitVulkan(void) { VkResult err; VkExtensionProperties* ep; - unsigned int i, count; + uint32_t i, count; #if defined(_GLFW_WIN32) const char* name = "vulkan-1.dll"; #else @@ -61,7 +61,7 @@ void _glfwInitVulkan(void) } _glfw.vk.EnumerateInstanceExtensionProperties = (PFN_vkEnumerateInstanceExtensionProperties) - vkGetInstanceProcAddr(0, "vkEnumerateInstanceExtensionProperties"); + vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceExtensionProperties"); if (!_glfw.vk.EnumerateInstanceExtensionProperties) { _glfwInputError(GLFW_API_UNAVAILABLE, @@ -120,7 +120,7 @@ void _glfwInitVulkan(void) void _glfwTerminateVulkan(void) { - int i; + uint32_t i; for (i = 0; i < _glfw.vk.extensionCount; i++) free(_glfw.vk.extensions[i]); @@ -181,7 +181,7 @@ const char* _glfwGetVulkanResultString(VkResult result) case VK_ERROR_VALIDATION_FAILED_EXT: return "A validation layer found an error"; default: - return "ERROR: UNKNOWN VULKAN ERROR TOKEN"; + return "ERROR: UNKNOWN VULKAN ERROR"; } } @@ -196,7 +196,7 @@ GLFWAPI int glfwVulkanSupported(void) return _glfw.vk.available; } -GLFWAPI const char** glfwGetRequiredInstanceExtensions(int* count) +GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count) { *count = 0; @@ -262,9 +262,9 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, VkSurfaceKHR* surface) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); - assert(surface); + assert(surface != NULL); *surface = VK_NULL_HANDLE; _GLFW_REQUIRE_INIT_OR_RETURN(VK_ERROR_INITIALIZATION_FAILED); diff --git a/src/wgl_context.c b/src/wgl_context.c index 9f9a04118..55399cb1c 100644 --- a/src/wgl_context.c +++ b/src/wgl_context.c @@ -32,55 +32,6 @@ #include -// Initialize WGL-specific extensions -// -static void loadExtensions(void) -{ - // Functions for WGL_EXT_extension_string - // NOTE: These are needed by _glfwPlatformExtensionSupported - _glfw.wgl.GetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC) - wglGetProcAddress("wglGetExtensionsStringEXT"); - _glfw.wgl.GetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC) - wglGetProcAddress("wglGetExtensionsStringARB"); - - // Functions for WGL_ARB_create_context - _glfw.wgl.CreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) - wglGetProcAddress("wglCreateContextAttribsARB"); - - // Functions for WGL_EXT_swap_control - _glfw.wgl.SwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) - wglGetProcAddress("wglSwapIntervalEXT"); - - // Functions for WGL_ARB_pixel_format - _glfw.wgl.GetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC) - wglGetProcAddress("wglGetPixelFormatAttribivARB"); - - // This needs to include every extension used below except for - // WGL_ARB_extensions_string and WGL_EXT_extensions_string - _glfw.wgl.ARB_multisample = - _glfwPlatformExtensionSupported("WGL_ARB_multisample"); - _glfw.wgl.ARB_framebuffer_sRGB = - _glfwPlatformExtensionSupported("WGL_ARB_framebuffer_sRGB"); - _glfw.wgl.EXT_framebuffer_sRGB = - _glfwPlatformExtensionSupported("WGL_EXT_framebuffer_sRGB"); - _glfw.wgl.ARB_create_context = - _glfwPlatformExtensionSupported("WGL_ARB_create_context"); - _glfw.wgl.ARB_create_context_profile = - _glfwPlatformExtensionSupported("WGL_ARB_create_context_profile"); - _glfw.wgl.EXT_create_context_es2_profile = - _glfwPlatformExtensionSupported("WGL_EXT_create_context_es2_profile"); - _glfw.wgl.ARB_create_context_robustness = - _glfwPlatformExtensionSupported("WGL_ARB_create_context_robustness"); - _glfw.wgl.EXT_swap_control = - _glfwPlatformExtensionSupported("WGL_EXT_swap_control"); - _glfw.wgl.ARB_pixel_format = - _glfwPlatformExtensionSupported("WGL_ARB_pixel_format"); - _glfw.wgl.ARB_context_flush_control = - _glfwPlatformExtensionSupported("WGL_ARB_context_flush_control"); - - _glfw.wgl.extensionsLoaded = GLFW_TRUE; -} - // Returns the specified attribute of the specified pixel format // static int getPixelFormatAttrib(_GLFWwindow* window, int pixelFormat, int attrib) @@ -236,10 +187,7 @@ static GLFWbool choosePixelFormat(_GLFWwindow* window, u->doublebuffer = GLFW_TRUE; } - // always able to create transparent windows on Windows - u->transparent = desired->transparent; - - u->wgl = n; + u->handle = n; usableCount++; } @@ -262,7 +210,7 @@ static GLFWbool choosePixelFormat(_GLFWwindow* window, return GLFW_FALSE; } - *result = closest->wgl; + *result = (int) closest->handle; free(usableConfigs); return GLFW_TRUE; @@ -283,6 +231,157 @@ static GLFWbool isCompositionEnabled(void) return enabled; } +static void makeContextCurrent(_GLFWwindow* window) +{ + if (window) + { + if (wglMakeCurrent(window->context.wgl.dc, window->context.wgl.handle)) + _glfwPlatformSetCurrentContext(window); + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "WGL: Failed to make context current"); + _glfwPlatformSetCurrentContext(NULL); + } + } + else + { + if (!wglMakeCurrent(NULL, NULL)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "WGL: Failed to clear current context"); + } + + _glfwPlatformSetCurrentContext(NULL); + } +} + +static void swapBuffers(_GLFWwindow* window) +{ + // HACK: Use DwmFlush when desktop composition is enabled + if (isCompositionEnabled() && !window->monitor) + { + int count = abs(window->context.wgl.interval); + while (count--) + _glfw_DwmFlush(); + } + + SwapBuffers(window->context.wgl.dc); +} + +static void swapInterval(int interval) +{ + _GLFWwindow* window = _glfwPlatformGetCurrentContext(); + + window->context.wgl.interval = interval; + + // HACK: Disable WGL swap interval when desktop composition is enabled to + // avoid interfering with DWM vsync + if (isCompositionEnabled() && !window->monitor) + interval = 0; + + if (_glfw.wgl.EXT_swap_control) + _glfw.wgl.SwapIntervalEXT(interval); +} + +static int extensionSupported(const char* extension) +{ + const char* extensions; + + _GLFWwindow* window = _glfwPlatformGetCurrentContext(); + + if (_glfw.wgl.GetExtensionsStringEXT) + { + extensions = _glfw.wgl.GetExtensionsStringEXT(); + if (extensions) + { + if (_glfwStringInExtensionString(extension, extensions)) + return GLFW_TRUE; + } + } + + if (_glfw.wgl.GetExtensionsStringARB) + { + extensions = _glfw.wgl.GetExtensionsStringARB(window->context.wgl.dc); + if (extensions) + { + if (_glfwStringInExtensionString(extension, extensions)) + return GLFW_TRUE; + } + } + + return GLFW_FALSE; +} + +static GLFWglproc getProcAddress(const char* procname) +{ + const GLFWglproc proc = (GLFWglproc) wglGetProcAddress(procname); + if (proc) + return proc; + + return (GLFWglproc) GetProcAddress(_glfw.wgl.instance, procname); +} + +// Destroy the OpenGL context +// +static void destroyContext(_GLFWwindow* window) +{ + if (window->context.wgl.handle) + { + wglDeleteContext(window->context.wgl.handle); + window->context.wgl.handle = NULL; + } +} + +// Initialize WGL-specific extensions +// +static void loadExtensions(void) +{ + // Functions for WGL_EXT_extension_string + // NOTE: These are needed by extensionSupported + _glfw.wgl.GetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC) + wglGetProcAddress("wglGetExtensionsStringEXT"); + _glfw.wgl.GetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC) + wglGetProcAddress("wglGetExtensionsStringARB"); + + // Functions for WGL_ARB_create_context + _glfw.wgl.CreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) + wglGetProcAddress("wglCreateContextAttribsARB"); + + // Functions for WGL_EXT_swap_control + _glfw.wgl.SwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) + wglGetProcAddress("wglSwapIntervalEXT"); + + // Functions for WGL_ARB_pixel_format + _glfw.wgl.GetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC) + wglGetProcAddress("wglGetPixelFormatAttribivARB"); + + // This needs to include every extension used below except for + // WGL_ARB_extensions_string and WGL_EXT_extensions_string + _glfw.wgl.ARB_multisample = + extensionSupported("WGL_ARB_multisample"); + _glfw.wgl.ARB_framebuffer_sRGB = + extensionSupported("WGL_ARB_framebuffer_sRGB"); + _glfw.wgl.EXT_framebuffer_sRGB = + extensionSupported("WGL_EXT_framebuffer_sRGB"); + _glfw.wgl.ARB_create_context = + extensionSupported("WGL_ARB_create_context"); + _glfw.wgl.ARB_create_context_profile = + extensionSupported("WGL_ARB_create_context_profile"); + _glfw.wgl.EXT_create_context_es2_profile = + extensionSupported("WGL_EXT_create_context_es2_profile"); + _glfw.wgl.ARB_create_context_robustness = + extensionSupported("WGL_ARB_create_context_robustness"); + _glfw.wgl.EXT_swap_control = + extensionSupported("WGL_EXT_swap_control"); + _glfw.wgl.ARB_pixel_format = + extensionSupported("WGL_ARB_pixel_format"); + _glfw.wgl.ARB_context_flush_control = + extensionSupported("WGL_ARB_context_flush_control"); + + _glfw.wgl.extensionsLoaded = GLFW_TRUE; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// @@ -339,7 +438,7 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, PIXELFORMATDESCRIPTOR pfd; HGLRC share = NULL; - if (ctxconfig->api == GLFW_NO_API) + if (ctxconfig->client == GLFW_NO_API) return GLFW_TRUE; if (ctxconfig->share) @@ -375,7 +474,7 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, { int index = 0, mask = 0, flags = 0; - if (ctxconfig->api == GLFW_OPENGL_API) + if (ctxconfig->client == GLFW_OPENGL_API) { if (ctxconfig->forward) flags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; @@ -477,22 +576,18 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, } } + window->context.makeContextCurrent = makeContextCurrent; + window->context.swapBuffers = swapBuffers; + window->context.swapInterval = swapInterval; + window->context.extensionSupported = extensionSupported; + window->context.getProcAddress = getProcAddress; + window->context.destroyContext = destroyContext; + return GLFW_TRUE; } #undef setWGLattrib -// Destroy the OpenGL context -// -void _glfwDestroyContextWGL(_GLFWwindow* window) -{ - if (window->context.wgl.handle) - { - wglDeleteContext(window->context.wgl.handle); - window->context.wgl.handle = NULL; - } -} - // Analyzes the specified context for possible recreation // int _glfwAnalyzeContextWGL(_GLFWwindow* window, @@ -504,10 +599,10 @@ int _glfwAnalyzeContextWGL(_GLFWwindow* window, if (_glfw.wgl.extensionsLoaded) return _GLFW_RECREATION_NOT_NEEDED; - _glfwPlatformMakeContextCurrent(window); + makeContextCurrent(window); loadExtensions(); - if (ctxconfig->api == GLFW_OPENGL_API) + if (ctxconfig->client == GLFW_OPENGL_API) { if (ctxconfig->forward) { @@ -590,87 +685,6 @@ int _glfwAnalyzeContextWGL(_GLFWwindow* window, } -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -void _glfwPlatformMakeContextCurrent(_GLFWwindow* window) -{ - if (window) - wglMakeCurrent(window->context.wgl.dc, window->context.wgl.handle); - else - wglMakeCurrent(NULL, NULL); - - _glfwPlatformSetCurrentContext(window); -} - -void _glfwPlatformSwapBuffers(_GLFWwindow* window) -{ - // HACK: Use DwmFlush when desktop composition is enabled - if (isCompositionEnabled() && !window->monitor) - { - int count = abs(window->context.wgl.interval); - while (count--) - _glfw_DwmFlush(); - } - - SwapBuffers(window->context.wgl.dc); -} - -void _glfwPlatformSwapInterval(int interval) -{ - _GLFWwindow* window = _glfwPlatformGetCurrentContext(); - - window->context.wgl.interval = interval; - - // HACK: Disable WGL swap interval when desktop composition is enabled to - // avoid interfering with DWM vsync - if (isCompositionEnabled() && !window->monitor) - interval = 0; - - if (_glfw.wgl.EXT_swap_control) - _glfw.wgl.SwapIntervalEXT(interval); -} - -int _glfwPlatformExtensionSupported(const char* extension) -{ - const char* extensions; - - _GLFWwindow* window = _glfwPlatformGetCurrentContext(); - - if (_glfw.wgl.GetExtensionsStringEXT) - { - extensions = _glfw.wgl.GetExtensionsStringEXT(); - if (extensions) - { - if (_glfwStringInExtensionString(extension, extensions)) - return GLFW_TRUE; - } - } - - if (_glfw.wgl.GetExtensionsStringARB) - { - extensions = _glfw.wgl.GetExtensionsStringARB(window->context.wgl.dc); - if (extensions) - { - if (_glfwStringInExtensionString(extension, extensions)) - return GLFW_TRUE; - } - } - - return GLFW_FALSE; -} - -GLFWglproc _glfwPlatformGetProcAddress(const char* procname) -{ - const GLFWglproc proc = (GLFWglproc) wglGetProcAddress(procname); - if (proc) - return proc; - - return (GLFWglproc) GetProcAddress(_glfw.wgl.instance, procname); -} - - ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// ////////////////////////////////////////////////////////////////////////// @@ -680,7 +694,7 @@ GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* handle) _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - if (window->context.api == GLFW_NO_API) + if (window->context.client == GLFW_NO_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return NULL; diff --git a/src/wgl_context.h b/src/wgl_context.h index bc8968b4e..f1e653c8d 100644 --- a/src/wgl_context.h +++ b/src/wgl_context.h @@ -96,7 +96,6 @@ typedef BOOL (WINAPI * WGLSHARELISTS_T)(HGLRC,HGLRC); #define _GLFW_RECREATION_REQUIRED 1 #define _GLFW_RECREATION_IMPOSSIBLE 2 -#define _GLFW_PLATFORM_FBCONFIG int wgl #define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextWGL wgl #define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryWGL wgl @@ -149,7 +148,6 @@ void _glfwTerminateWGL(void); GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); -void _glfwDestroyContextWGL(_GLFWwindow* window); int _glfwAnalyzeContextWGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); diff --git a/src/win32_init.c b/src/win32_init.c index 57acdb3ae..0bfe34c7c 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -30,6 +30,8 @@ #include #include +#include +DEFINE_GUID(GUID_DEVINTERFACE_HID,0x4d1e55b2,0xf16f,0x11cf,0x88,0xcb,0x00,0x11,0x11,0x00,0x00,0x30); #if defined(_GLFW_USE_HYBRID_HPG) || defined(_GLFW_USE_OPTIMUS_HPG) @@ -69,12 +71,6 @@ static GLFWbool loadLibraries(void) return GLFW_FALSE; } - _glfw.win32.winmm.joyGetDevCaps = (JOYGETDEVCAPS_T) - GetProcAddress(_glfw.win32.winmm.instance, "joyGetDevCapsW"); - _glfw.win32.winmm.joyGetPos = (JOYGETPOS_T) - GetProcAddress(_glfw.win32.winmm.instance, "joyGetPos"); - _glfw.win32.winmm.joyGetPosEx = (JOYGETPOSEX_T) - GetProcAddress(_glfw.win32.winmm.instance, "joyGetPosEx"); _glfw.win32.winmm.timeGetTime = (TIMEGETTIME_T) GetProcAddress(_glfw.win32.winmm.instance, "timeGetTime"); @@ -90,6 +86,40 @@ static GLFWbool loadLibraries(void) _glfw.win32.user32.ChangeWindowMessageFilterEx = (CHANGEWINDOWMESSAGEFILTEREX_T) GetProcAddress(_glfw.win32.user32.instance, "ChangeWindowMessageFilterEx"); + _glfw.win32.dinput8.instance = LoadLibraryA("dinput8.dll"); + if (_glfw.win32.dinput8.instance) + { + _glfw.win32.dinput8.DirectInput8Create = (DIRECTINPUT8CREATE_T) + GetProcAddress(_glfw.win32.dinput8.instance, "DirectInput8Create"); + } + + { + int i; + const char* names[] = + { + "xinput1_4.dll", + "xinput1_3.dll", + "xinput9_1_0.dll", + "xinput1_2.dll", + "xinput1_1.dll", + NULL + }; + + for (i = 0; names[i]; i++) + { + _glfw.win32.xinput.instance = LoadLibraryA(names[i]); + if (_glfw.win32.xinput.instance) + { + _glfw.win32.xinput.XInputGetCapabilities = (XINPUTGETCAPABILITIES_T) + GetProcAddress(_glfw.win32.xinput.instance, "XInputGetCapabilities"); + _glfw.win32.xinput.XInputGetState = (XINPUTGETSTATE_T) + GetProcAddress(_glfw.win32.xinput.instance, "XInputGetState"); + + break; + } + } + } + _glfw.win32.dwmapi.instance = LoadLibraryA("dwmapi.dll"); if (_glfw.win32.dwmapi.instance) { @@ -113,6 +143,12 @@ static GLFWbool loadLibraries(void) // static void freeLibraries(void) { + if (_glfw.win32.xinput.instance) + FreeLibrary(_glfw.win32.xinput.instance); + + if (_glfw.win32.dinput8.instance) + FreeLibrary(_glfw.win32.dinput8.instance); + if (_glfw.win32.winmm.instance) FreeLibrary(_glfw.win32.winmm.instance); @@ -196,6 +232,7 @@ static void createKeyTables(void) _glfw.win32.publicKeys[0x151] = GLFW_KEY_PAGE_DOWN; _glfw.win32.publicKeys[0x149] = GLFW_KEY_PAGE_UP; _glfw.win32.publicKeys[0x045] = GLFW_KEY_PAUSE; + _glfw.win32.publicKeys[0x146] = GLFW_KEY_PAUSE; _glfw.win32.publicKeys[0x039] = GLFW_KEY_SPACE; _glfw.win32.publicKeys[0x00F] = GLFW_KEY_TAB; _glfw.win32.publicKeys[0x03A] = GLFW_KEY_CAPS_LOCK; @@ -272,7 +309,7 @@ static HWND createHelperWindow(void) L"GLFW helper window", WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, 0, 1, 1, - NULL, NULL, + HWND_MESSAGE, NULL, GetModuleHandleW(NULL), NULL); if (!window) @@ -282,7 +319,20 @@ static HWND createHelperWindow(void) return NULL; } - return window; + // Register for HID device notifications + { + DEV_BROADCAST_DEVICEINTERFACE_W dbi; + ZeroMemory(&dbi, sizeof(dbi)); + dbi.dbcc_size = sizeof(dbi); + dbi.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + dbi.dbcc_classguid = GUID_DEVINTERFACE_HID; + + RegisterDeviceNotificationW(window, + (DEV_BROADCAST_HDR*) &dbi, + DEVICE_NOTIFY_WINDOW_HANDLE); + } + + return window; } @@ -371,14 +421,10 @@ int _glfwPlatformInit(void) _glfwPlatformPollEvents(); -#if defined(_GLFW_WGL) if (!_glfwInitWGL()) return GLFW_FALSE; -#elif defined(_GLFW_EGL) - if (!_glfwInitEGL()) - return GLFW_FALSE; -#endif + _glfwInitEGL(); _glfwInitTimerWin32(); _glfwInitJoysticksWin32(); @@ -399,11 +445,8 @@ void _glfwPlatformTerminate(void) free(_glfw.win32.clipboardString); -#if defined(_GLFW_WGL) _glfwTerminateWGL(); -#elif defined(_GLFW_EGL) _glfwTerminateEGL(); -#endif _glfwTerminateJoysticksWin32(); _glfwTerminateThreadLocalStorageWin32(); @@ -413,12 +456,7 @@ void _glfwPlatformTerminate(void) const char* _glfwPlatformGetVersionString(void) { - return _GLFW_VERSION_NUMBER " Win32" -#if defined(_GLFW_WGL) - " WGL" -#elif defined(_GLFW_EGL) - " EGL" -#endif + return _GLFW_VERSION_NUMBER " Win32 WGL EGL" #if defined(__MINGW32__) " MinGW" #elif defined(_MSC_VER) diff --git a/src/win32_joystick.c b/src/win32_joystick.c index 854dfe7d3..0268e0514 100644 --- a/src/win32_joystick.c +++ b/src/win32_joystick.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 Win32 - www.glfw.org +// GLFW 3.1 Win32 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2010 Camilla Berglund +// Copyright (c) 2006-2015 Camilla Berglund // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -27,22 +27,623 @@ #include "internal.h" -#include +#include +#include -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// +#define _GLFW_PRESENCE_ONLY 1 +#define _GLFW_UPDATE_STATE 2 -// Convert axis value to the [-1,1] range +#define _GLFW_TYPE_AXIS 0 +#define _GLFW_TYPE_SLIDER 1 +#define _GLFW_TYPE_BUTTON 2 +#define _GLFW_TYPE_POV 3 + +// Data produced with DirectInput device object enumeration // -static float normalizeAxis(DWORD pos, DWORD min, DWORD max) +typedef struct _GLFWobjenumWin32 { - float fpos = (float) pos; - float fmin = (float) min; - float fmax = (float) max; + IDirectInputDevice8W* device; + _GLFWjoyobjectWin32* objects; + int objectCount; + int axisCount; + int sliderCount; + int buttonCount; + int povCount; +} _GLFWobjenumWin32; - return (2.f * (fpos - fmin) / (fmax - fmin)) - 1.f; +// Define only the necessary GUIDs (it's bad enough that we're exporting these) +// +DEFINE_GUID(IID_IDirectInput8W,0xbf798031,0x483a,0x4da2,0xaa,0x99,0x5d,0x64,0xed,0x36,0x97,0x00); +DEFINE_GUID(GUID_XAxis,0xa36d02e0,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_YAxis,0xa36d02e1,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_ZAxis,0xa36d02e2,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_RxAxis,0xa36d02f4,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_RyAxis,0xa36d02f5,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_RzAxis,0xa36d02e3,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_Slider,0xa36d02e4,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_Button,0xa36d02f0,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_POV,0xa36d02f2,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); + +// Object data array for our clone of c_dfDIJoystick +// Generated with https://github.com/elmindreda/c_dfDIJoystick2 +// +static DIOBJECTDATAFORMAT _glfwObjectDataFormats[] = +{ + { &GUID_XAxis,DIJOFS_X,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_YAxis,DIJOFS_Y,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_ZAxis,DIJOFS_Z,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_RxAxis,DIJOFS_RX,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_RyAxis,DIJOFS_RY,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_RzAxis,DIJOFS_RZ,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_Slider,DIJOFS_SLIDER(0),DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_Slider,DIJOFS_SLIDER(1),DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_POV,DIJOFS_POV(0),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { &GUID_POV,DIJOFS_POV(1),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { &GUID_POV,DIJOFS_POV(2),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { &GUID_POV,DIJOFS_POV(3),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(0),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(1),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(2),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(3),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(4),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(5),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(6),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(7),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(8),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(9),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(10),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(11),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(12),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(13),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(14),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(15),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(16),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(17),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(18),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(19),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(20),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(21),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(22),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(23),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(24),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(25),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(26),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(27),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(28),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(29),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(30),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(31),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, +}; + +// Our clone of c_dfDIJoystick +// +static const DIDATAFORMAT _glfwDataFormat = +{ + sizeof(DIDATAFORMAT), + sizeof(DIOBJECTDATAFORMAT), + DIDFT_ABSAXIS, + sizeof(DIJOYSTATE), + sizeof(_glfwObjectDataFormats) / sizeof(DIOBJECTDATAFORMAT), + _glfwObjectDataFormats +}; + +// Returns a description fitting the specified XInput capabilities +// +static const char* getDeviceDescription(const XINPUT_CAPABILITIES* xic) +{ + switch (xic->SubType) + { + case XINPUT_DEVSUBTYPE_WHEEL: + return "XInput Wheel"; + case XINPUT_DEVSUBTYPE_ARCADE_STICK: + return "XInput Arcade Stick"; + case XINPUT_DEVSUBTYPE_FLIGHT_STICK: + return "XInput Flight Stick"; + case XINPUT_DEVSUBTYPE_DANCE_PAD: + return "XInput Dance Pad"; + case XINPUT_DEVSUBTYPE_GUITAR: + return "XInput Guitar"; + case XINPUT_DEVSUBTYPE_DRUM_KIT: + return "XInput Drum Kit"; + case XINPUT_DEVSUBTYPE_GAMEPAD: + { + if (xic->Flags & XINPUT_CAPS_WIRELESS) + return "Wireless Xbox 360 Controller"; + else + return "Xbox 360 Controller"; + } + } + + return "Unknown XInput Device"; +} + +// Lexically compare device objects +// +static int compareJoystickObjects(const void* first, const void* second) +{ + const _GLFWjoyobjectWin32* fo = first; + const _GLFWjoyobjectWin32* so = second; + + if (fo->type != so->type) + return fo->type - so->type; + + return fo->offset - so->offset; +} + +// Checks whether the specified device supports XInput +// Technique from FDInputJoystickManager::IsXInputDeviceFast in ZDoom +// +static GLFWbool supportsXInput(const GUID* guid) +{ + UINT i, count; + RAWINPUTDEVICELIST* ridl; + GLFWbool result = GLFW_FALSE; + + if (GetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST)) != 0) + return GLFW_FALSE; + + ridl = calloc(count, sizeof(RAWINPUTDEVICELIST)); + + if (GetRawInputDeviceList(ridl, &count, sizeof(RAWINPUTDEVICELIST)) == -1) + { + free(ridl); + return GLFW_FALSE; + } + + for (i = 0; i < count; i++) + { + RID_DEVICE_INFO rdi; + char name[256]; + UINT size; + + if (ridl[i].dwType != RIM_TYPEHID) + continue; + + ZeroMemory(&rdi, sizeof(rdi)); + rdi.cbSize = sizeof(rdi); + size = sizeof(rdi); + + if ((INT) GetRawInputDeviceInfoA(ridl[i].hDevice, + RIDI_DEVICEINFO, + &rdi, &size) == -1) + { + continue; + } + + if (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) != guid->Data1) + continue; + + memset(name, 0, sizeof(name)); + size = sizeof(name); + + if ((INT) GetRawInputDeviceInfoA(ridl[i].hDevice, + RIDI_DEVICENAME, + name, &size) == -1) + { + break; + } + + name[sizeof(name) - 1] = '\0'; + if (strstr(name, "IG_")) + { + result = GLFW_TRUE; + break; + } + } + + free(ridl); + return result; +} + +// Frees all resources associated with the specified joystick +// +static void closeJoystick(_GLFWjoystickWin32* js) +{ + if (js->device) + { + IDirectInputDevice8_Unacquire(js->device); + IDirectInputDevice8_Release(js->device); + } + + free(js->name); + free(js->axes); + free(js->buttons); + free(js->objects); + memset(js, 0, sizeof(_GLFWjoystickWin32)); + + _glfwInputJoystickChange((int) (js - _glfw.win32_js), GLFW_DISCONNECTED); +} + +// DirectInput device object enumeration callback +// Insights gleaned from SDL2 +// +static BOOL CALLBACK deviceObjectCallback(const DIDEVICEOBJECTINSTANCEW* doi, + void* user) +{ + _GLFWobjenumWin32* data = user; + _GLFWjoyobjectWin32* object = data->objects + data->objectCount; + + if (DIDFT_GETTYPE(doi->dwType) & DIDFT_AXIS) + { + DIPROPRANGE dipr; + + if (memcmp(&doi->guidType, &GUID_Slider, sizeof(GUID)) == 0) + object->offset = DIJOFS_SLIDER(data->sliderCount); + else if (memcmp(&doi->guidType, &GUID_XAxis, sizeof(GUID)) == 0) + object->offset = DIJOFS_X; + else if (memcmp(&doi->guidType, &GUID_YAxis, sizeof(GUID)) == 0) + object->offset = DIJOFS_Y; + else if (memcmp(&doi->guidType, &GUID_ZAxis, sizeof(GUID)) == 0) + object->offset = DIJOFS_Z; + else if (memcmp(&doi->guidType, &GUID_RxAxis, sizeof(GUID)) == 0) + object->offset = DIJOFS_RX; + else if (memcmp(&doi->guidType, &GUID_RyAxis, sizeof(GUID)) == 0) + object->offset = DIJOFS_RY; + else if (memcmp(&doi->guidType, &GUID_RzAxis, sizeof(GUID)) == 0) + object->offset = DIJOFS_RZ; + else + return DIENUM_CONTINUE; + + ZeroMemory(&dipr, sizeof(dipr)); + dipr.diph.dwSize = sizeof(dipr); + dipr.diph.dwHeaderSize = sizeof(dipr.diph); + dipr.diph.dwObj = doi->dwType; + dipr.diph.dwHow = DIPH_BYID; + dipr.lMin = -32768; + dipr.lMax = 32767; + + if (FAILED(IDirectInputDevice8_SetProperty(data->device, + DIPROP_RANGE, + &dipr.diph))) + { + return DIENUM_CONTINUE; + } + + if (memcmp(&doi->guidType, &GUID_Slider, sizeof(GUID)) == 0) + { + object->type = _GLFW_TYPE_SLIDER; + data->sliderCount++; + } + else + { + object->type = _GLFW_TYPE_AXIS; + data->axisCount++; + } + } + else if (DIDFT_GETTYPE(doi->dwType) & DIDFT_BUTTON) + { + object->offset = DIJOFS_BUTTON(data->buttonCount); + object->type = _GLFW_TYPE_BUTTON; + data->buttonCount++; + } + else if (DIDFT_GETTYPE(doi->dwType) & DIDFT_POV) + { + object->offset = DIJOFS_POV(data->povCount); + object->type = _GLFW_TYPE_POV; + data->povCount++; + } + + data->objectCount++; + return DIENUM_CONTINUE; +} + +// DirectInput device enumeration callback +// +static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) +{ + int joy = 0; + DIDEVCAPS dc; + DIPROPDWORD dipd; + IDirectInputDevice8* device; + _GLFWobjenumWin32 data; + _GLFWjoystickWin32* js; + + for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + { + if (memcmp(&_glfw.win32_js[joy].guid, &di->guidInstance, sizeof(GUID)) == 0) + return DIENUM_CONTINUE; + } + + for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + { + if (!_glfw.win32_js[joy].present) + break; + } + + if (joy > GLFW_JOYSTICK_LAST) + return DIENUM_STOP; + + if (supportsXInput(&di->guidProduct)) + return DIENUM_CONTINUE; + + if (FAILED(IDirectInput8_CreateDevice(_glfw.win32.dinput8.api, + &di->guidInstance, + &device, + NULL))) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "DI: Failed to create device"); + return DIENUM_CONTINUE; + } + + if (FAILED(IDirectInputDevice8_SetDataFormat(device, &_glfwDataFormat))) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "DI: Failed to set device data format"); + + IDirectInputDevice8_Release(device); + return DIENUM_CONTINUE; + } + + ZeroMemory(&dc, sizeof(dc)); + dc.dwSize = sizeof(dc); + + if (FAILED(IDirectInputDevice8_GetCapabilities(device, &dc))) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "DI: Failed to query device capabilities"); + + IDirectInputDevice8_Release(device); + return DIENUM_CONTINUE; + } + + ZeroMemory(&dipd, sizeof(dipd)); + dipd.diph.dwSize = sizeof(dipd); + dipd.diph.dwHeaderSize = sizeof(dipd.diph); + dipd.diph.dwHow = DIPH_DEVICE; + dipd.dwData = DIPROPAXISMODE_ABS; + + if (FAILED(IDirectInputDevice8_SetProperty(device, + DIPROP_AXISMODE, + &dipd.diph))) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "DI: Failed to set device axis mode"); + + IDirectInputDevice8_Release(device); + return DIENUM_CONTINUE; + } + + memset(&data, 0, sizeof(data)); + data.device = device; + data.objects = calloc(dc.dwAxes + dc.dwButtons + dc.dwPOVs, + sizeof(_GLFWjoyobjectWin32)); + + if (FAILED(IDirectInputDevice8_EnumObjects(device, + deviceObjectCallback, + &data, + DIDFT_AXIS | DIDFT_BUTTON | DIDFT_POV))) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "DI: Failed to enumerate device objects"); + + IDirectInputDevice8_Release(device); + free(data.objects); + return DIENUM_CONTINUE; + } + + qsort(data.objects, data.objectCount, + sizeof(_GLFWjoyobjectWin32), + compareJoystickObjects); + + js = _glfw.win32_js + joy; + js->device = device; + js->guid = di->guidInstance; + js->axisCount = data.axisCount + data.sliderCount; + js->axes = calloc(js->axisCount, sizeof(float)); + js->buttonCount += data.buttonCount + data.povCount * 4; + js->buttons = calloc(js->buttonCount, 1); + js->objects = data.objects; + js->objectCount = data.objectCount; + js->name = _glfwCreateUTF8FromWideStringWin32(di->tszInstanceName); + js->present = GLFW_TRUE; + + _glfwInputJoystickChange(joy, GLFW_CONNECTED); + return DIENUM_CONTINUE; +} + +// Attempt to open the specified joystick device +// TODO: Pack state arrays for non-gamepad devices +// +static GLFWbool openXinputDevice(DWORD index) +{ + int joy; + XINPUT_CAPABILITIES xic; + _GLFWjoystickWin32* js; + + for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + { + if (_glfw.win32_js[joy].present && + _glfw.win32_js[joy].device == NULL && + _glfw.win32_js[joy].index == index) + { + return GLFW_FALSE; + } + } + + for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + { + if (!_glfw.win32_js[joy].present) + break; + } + + if (joy > GLFW_JOYSTICK_LAST) + return GLFW_FALSE; + + if (_glfw_XInputGetCapabilities(index, 0, &xic) != ERROR_SUCCESS) + return GLFW_FALSE; + + js = _glfw.win32_js + joy; + js->axisCount = 6; + js->axes = calloc(js->axisCount, sizeof(float)); + js->buttonCount = 14; + js->buttons = calloc(js->buttonCount, 1); + js->present = GLFW_TRUE; + js->name = strdup(getDeviceDescription(&xic)); + js->index = index; + + _glfwInputJoystickChange(joy, GLFW_CONNECTED); + + return GLFW_TRUE; +} + +// Polls for and processes events the specified joystick +// +static GLFWbool pollJoystickState(_GLFWjoystickWin32* js, int mode) +{ + if (!js->present) + return GLFW_FALSE; + + if (js->device) + { + int i, j, ai = 0, bi = 0; + HRESULT result; + DIJOYSTATE state; + + IDirectInputDevice8_Poll(js->device); + result = IDirectInputDevice8_GetDeviceState(js->device, + sizeof(state), + &state); + if (result == DIERR_NOTACQUIRED || result == DIERR_INPUTLOST) + { + IDirectInputDevice8_Acquire(js->device); + IDirectInputDevice8_Poll(js->device); + result = IDirectInputDevice8_GetDeviceState(js->device, + sizeof(state), + &state); + } + + if (FAILED(result)) + { + closeJoystick(js); + return GLFW_FALSE; + } + + if (mode == _GLFW_PRESENCE_ONLY) + return GLFW_TRUE; + + for (i = 0; i < js->objectCount; i++) + { + const void* data = (char*) &state + js->objects[i].offset; + + switch (js->objects[i].type) + { + case _GLFW_TYPE_AXIS: + case _GLFW_TYPE_SLIDER: + { + js->axes[ai++] = (*((LONG*) data) + 0.5f) / 32767.5f; + break; + } + + case _GLFW_TYPE_BUTTON: + { + if (*((BYTE*) data) & 0x80) + js->buttons[bi++] = GLFW_PRESS; + else + js->buttons[bi++] = GLFW_RELEASE; + + break; + } + + case _GLFW_TYPE_POV: + { + const int directions[9] = { 1, 3, 2, 6, 4, 12, 8, 9, 0 }; + // Screams of horror are appropriate at this point + int value = LOWORD(*(DWORD*) data) / (45 * DI_DEGREES); + if (value < 0 || value > 8) + value = 8; + + for (j = 0; j < 4; j++) + { + if (directions[value] & (1 << j)) + js->buttons[bi++] = GLFW_PRESS; + else + js->buttons[bi++] = GLFW_RELEASE; + } + + break; + } + } + } + + return GLFW_TRUE; + } + else + { + int i; + DWORD result; + XINPUT_STATE xis; + const WORD buttons[14] = + { + XINPUT_GAMEPAD_A, + XINPUT_GAMEPAD_B, + XINPUT_GAMEPAD_X, + XINPUT_GAMEPAD_Y, + XINPUT_GAMEPAD_LEFT_SHOULDER, + XINPUT_GAMEPAD_RIGHT_SHOULDER, + XINPUT_GAMEPAD_BACK, + XINPUT_GAMEPAD_START, + XINPUT_GAMEPAD_LEFT_THUMB, + XINPUT_GAMEPAD_RIGHT_THUMB, + XINPUT_GAMEPAD_DPAD_UP, + XINPUT_GAMEPAD_DPAD_RIGHT, + XINPUT_GAMEPAD_DPAD_DOWN, + XINPUT_GAMEPAD_DPAD_LEFT + }; + + result = _glfw_XInputGetState(js->index, &xis); + if (result != ERROR_SUCCESS) + { + if (result == ERROR_DEVICE_NOT_CONNECTED) + closeJoystick(js); + + return GLFW_FALSE; + } + + if (mode == _GLFW_PRESENCE_ONLY) + return GLFW_TRUE; + + if (sqrt((double) (xis.Gamepad.sThumbLX * xis.Gamepad.sThumbLX + + xis.Gamepad.sThumbLY * xis.Gamepad.sThumbLY)) > + (double) XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) + { + js->axes[0] = (xis.Gamepad.sThumbLX + 0.5f) / 32767.f; + js->axes[1] = (xis.Gamepad.sThumbLY + 0.5f) / 32767.f; + } + else + { + js->axes[0] = 0.f; + js->axes[1] = 0.f; + } + + if (sqrt((double) (xis.Gamepad.sThumbRX * xis.Gamepad.sThumbRX + + xis.Gamepad.sThumbRY * xis.Gamepad.sThumbRY)) > + (double) XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) + { + js->axes[2] = (xis.Gamepad.sThumbRX + 0.5f) / 32767.f; + js->axes[3] = (xis.Gamepad.sThumbRY + 0.5f) / 32767.f; + } + else + { + js->axes[2] = 0.f; + js->axes[3] = 0.f; + } + + if (xis.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) + js->axes[4] = xis.Gamepad.bLeftTrigger / 127.5f - 1.f; + else + js->axes[4] = -1.f; + + if (xis.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) + js->axes[5] = xis.Gamepad.bRightTrigger / 127.5f - 1.f; + else + js->axes[5] = -1.f; + + for (i = 0; i < 14; i++) + js->buttons[i] = (xis.Gamepad.wButtons & buttons[i]) ? 1 : 0; + + return GLFW_TRUE; + } } @@ -54,16 +655,70 @@ static float normalizeAxis(DWORD pos, DWORD min, DWORD max) // void _glfwInitJoysticksWin32(void) { + if (_glfw.win32.dinput8.instance) + { + if (FAILED(_glfw_DirectInput8Create(GetModuleHandle(NULL), + DIRECTINPUT_VERSION, + &IID_IDirectInput8W, + (void**) &_glfw.win32.dinput8.api, + NULL))) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "DI: Failed to create interface"); + } + } + + _glfwDetectJoystickConnectionWin32(); } // Close all opened joystick handles // void _glfwTerminateJoysticksWin32(void) { - int i; + int joy; - for (i = 0; i < GLFW_JOYSTICK_LAST; i++) - free(_glfw.win32_js[i].name); + for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + closeJoystick(_glfw.win32_js + joy); + + if (_glfw.win32.dinput8.api) + IDirectInput8_Release(_glfw.win32.dinput8.api); +} + +// Checks for new joysticks after DBT_DEVICEARRIVAL +// +void _glfwDetectJoystickConnectionWin32(void) +{ + if (_glfw.win32.xinput.instance) + { + DWORD i; + + for (i = 0; i < XUSER_MAX_COUNT; i++) + openXinputDevice(i); + } + + if (_glfw.win32.dinput8.api) + { + if (FAILED(IDirectInput8_EnumDevices(_glfw.win32.dinput8.api, + DI8DEVCLASS_GAMECTRL, + deviceCallback, + NULL, + DIEDFL_ALLDEVICES))) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Failed to enumerate DirectInput8 devices"); + return; + } + } +} + +// Checks for joystick disconnection after DBT_DEVICEREMOVECOMPLETE +// +void _glfwDetectJoystickDisconnectionWin32(void) +{ + int joy; + + for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + pollJoystickState(_glfw.win32_js + joy, _GLFW_PRESENCE_ONLY); } @@ -73,105 +728,36 @@ void _glfwTerminateJoysticksWin32(void) int _glfwPlatformJoystickPresent(int joy) { - JOYINFO ji; - - if (_glfw_joyGetPos(joy, &ji) != JOYERR_NOERROR) - return GLFW_FALSE; - - return GLFW_TRUE; + _GLFWjoystickWin32* js = _glfw.win32_js + joy; + return pollJoystickState(js, _GLFW_PRESENCE_ONLY); } const float* _glfwPlatformGetJoystickAxes(int joy, int* count) { - JOYCAPS jc; - JOYINFOEX ji; - float* axes = _glfw.win32_js[joy].axes; - - if (_glfw_joyGetDevCaps(joy, &jc, sizeof(JOYCAPS)) != JOYERR_NOERROR) + _GLFWjoystickWin32* js = _glfw.win32_js + joy; + if (!pollJoystickState(js, _GLFW_UPDATE_STATE)) return NULL; - ji.dwSize = sizeof(JOYINFOEX); - ji.dwFlags = JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | - JOY_RETURNR | JOY_RETURNU | JOY_RETURNV; - if (_glfw_joyGetPosEx(joy, &ji) != JOYERR_NOERROR) - return NULL; - - axes[(*count)++] = normalizeAxis(ji.dwXpos, jc.wXmin, jc.wXmax); - axes[(*count)++] = normalizeAxis(ji.dwYpos, jc.wYmin, jc.wYmax); - - if (jc.wCaps & JOYCAPS_HASZ) - axes[(*count)++] = normalizeAxis(ji.dwZpos, jc.wZmin, jc.wZmax); - - if (jc.wCaps & JOYCAPS_HASR) - axes[(*count)++] = normalizeAxis(ji.dwRpos, jc.wRmin, jc.wRmax); - - if (jc.wCaps & JOYCAPS_HASU) - axes[(*count)++] = normalizeAxis(ji.dwUpos, jc.wUmin, jc.wUmax); - - if (jc.wCaps & JOYCAPS_HASV) - axes[(*count)++] = normalizeAxis(ji.dwVpos, jc.wVmin, jc.wVmax); - - return axes; + *count = js->axisCount; + return js->axes; } const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count) { - JOYCAPS jc; - JOYINFOEX ji; - unsigned char* buttons = _glfw.win32_js[joy].buttons; - - if (_glfw_joyGetDevCaps(joy, &jc, sizeof(JOYCAPS)) != JOYERR_NOERROR) + _GLFWjoystickWin32* js = _glfw.win32_js + joy; + if (!pollJoystickState(js, _GLFW_UPDATE_STATE)) return NULL; - ji.dwSize = sizeof(JOYINFOEX); - ji.dwFlags = JOY_RETURNBUTTONS | JOY_RETURNPOV; - if (_glfw_joyGetPosEx(joy, &ji) != JOYERR_NOERROR) - return NULL; - - while (*count < (int) jc.wNumButtons) - { - buttons[*count] = (unsigned char) - (ji.dwButtons & (1UL << *count) ? GLFW_PRESS : GLFW_RELEASE); - (*count)++; - } - - // Virtual buttons - Inject data from hats - // Each hat is exposed as 4 buttons which exposes 8 directions with - // concurrent button presses - // NOTE: this API exposes only one hat - - if ((jc.wCaps & JOYCAPS_HASPOV) && (jc.wCaps & JOYCAPS_POV4DIR)) - { - int i, value = ji.dwPOV / 100 / 45; - - // Bit fields of button presses for each direction, including nil - const int directions[9] = { 1, 3, 2, 6, 4, 12, 8, 9, 0 }; - - if (value < 0 || value > 8) - value = 8; - - for (i = 0; i < 4; i++) - { - if (directions[value] & (1 << i)) - buttons[(*count)++] = GLFW_PRESS; - else - buttons[(*count)++] = GLFW_RELEASE; - } - } - - return buttons; + *count = js->buttonCount; + return js->buttons; } const char* _glfwPlatformGetJoystickName(int joy) { - JOYCAPS jc; - - if (_glfw_joyGetDevCaps(joy, &jc, sizeof(JOYCAPS)) != JOYERR_NOERROR) + _GLFWjoystickWin32* js = _glfw.win32_js + joy; + if (!pollJoystickState(js, _GLFW_PRESENCE_ONLY)) return NULL; - free(_glfw.win32_js[joy].name); - _glfw.win32_js[joy].name = _glfwCreateUTF8FromWideStringWin32(jc.szPname); - - return _glfw.win32_js[joy].name; + return js->name; } diff --git a/src/win32_joystick.h b/src/win32_joystick.h index ea999cd87..c6faeda0d 100644 --- a/src/win32_joystick.h +++ b/src/win32_joystick.h @@ -30,18 +30,35 @@ #define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE \ _GLFWjoystickWin32 win32_js[GLFW_JOYSTICK_LAST + 1] +// Spoo +// +typedef struct _GLFWjoyobjectWin32 +{ + int offset; + int type; +} _GLFWjoyobjectWin32; // Win32-specific per-joystick data // typedef struct _GLFWjoystickWin32 { - float axes[6]; - unsigned char buttons[36]; // 32 buttons plus one hat - char* name; + GLFWbool present; + float* axes; + int axisCount; + unsigned char* buttons; + int buttonCount; + _GLFWjoyobjectWin32* objects; + int objectCount; + char* name; + IDirectInputDevice8W* device; + DWORD index; + GUID guid; } _GLFWjoystickWin32; void _glfwInitJoysticksWin32(void); void _glfwTerminateJoysticksWin32(void); +void _glfwDetectJoystickConnectionWin32(void); +void _glfwDetectJoystickDisconnectionWin32(void); #endif // _glfw3_win32_joystick_h_ diff --git a/src/win32_monitor.c b/src/win32_monitor.c index 4cac01fff..a6eeeb1ed 100644 --- a/src/win32_monitor.c +++ b/src/win32_monitor.c @@ -32,13 +32,58 @@ #include #include -// These constants are missing on MinGW -#ifndef EDS_ROTATEDMODE - #define EDS_ROTATEDMODE 0x00000004 -#endif -#ifndef DISPLAY_DEVICE_ACTIVE - #define DISPLAY_DEVICE_ACTIVE 0x00000001 -#endif + +// Create monitor from an adapter and (optionally) a display +// +static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, + DISPLAY_DEVICEW* display) +{ + _GLFWmonitor* monitor; + char* name; + HDC dc; + + if (display) + name = _glfwCreateUTF8FromWideStringWin32(display->DeviceString); + else + name = _glfwCreateUTF8FromWideStringWin32(adapter->DeviceString); + if (!name) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert string to UTF-8"); + return NULL; + } + + dc = CreateDCW(L"DISPLAY", adapter->DeviceName, NULL, NULL); + + monitor = _glfwAllocMonitor(name, + GetDeviceCaps(dc, HORZSIZE), + GetDeviceCaps(dc, VERTSIZE)); + + DeleteDC(dc); + free(name); + + if (adapter->StateFlags & DISPLAY_DEVICE_MODESPRUNED) + monitor->win32.modesPruned = GLFW_TRUE; + + wcscpy(monitor->win32.adapterName, adapter->DeviceName); + WideCharToMultiByte(CP_UTF8, 0, + adapter->DeviceName, -1, + monitor->win32.publicAdapterName, + sizeof(monitor->win32.publicAdapterName), + NULL, NULL); + + if (display) + { + wcscpy(monitor->win32.displayName, display->DeviceName); + WideCharToMultiByte(CP_UTF8, 0, + display->DeviceName, -1, + monitor->win32.publicDisplayName, + sizeof(monitor->win32.publicDisplayName), + NULL, NULL); + } + + return monitor; +} ////////////////////////////////////////////////////////////////////////// @@ -104,17 +149,18 @@ void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor) _GLFWmonitor** _glfwPlatformGetMonitors(int* count) { int found = 0; + DWORD adapterIndex, displayIndex, primaryIndex = 0; + DISPLAY_DEVICEW adapter, display; + GLFWbool hasDisplays = GLFW_FALSE; _GLFWmonitor** monitors = NULL; - DWORD adapterIndex, displayIndex; *count = 0; + // HACK: Check if any active adapters have connected displays + // If not, this is a headless system or a VMware guest + for (adapterIndex = 0; ; adapterIndex++) { - DISPLAY_DEVICEW adapter; - int widthMM, heightMM; - HDC dc; - ZeroMemory(&adapter, sizeof(DISPLAY_DEVICEW)); adapter.cb = sizeof(DISPLAY_DEVICEW); @@ -124,71 +170,65 @@ _GLFWmonitor** _glfwPlatformGetMonitors(int* count) if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE)) continue; - dc = CreateDCW(L"DISPLAY", adapter.DeviceName, NULL, NULL); - widthMM = GetDeviceCaps(dc, HORZSIZE); - heightMM = GetDeviceCaps(dc, VERTSIZE); - DeleteDC(dc); + ZeroMemory(&display, sizeof(DISPLAY_DEVICEW)); + display.cb = sizeof(DISPLAY_DEVICEW); - for (displayIndex = 0; ; displayIndex++) + if (EnumDisplayDevicesW(adapter.DeviceName, 0, &display, 0)) { - DISPLAY_DEVICEW display; - _GLFWmonitor* monitor; - char* name; - - ZeroMemory(&display, sizeof(DISPLAY_DEVICEW)); - display.cb = sizeof(DISPLAY_DEVICEW); - - if (!EnumDisplayDevicesW(adapter.DeviceName, displayIndex, &display, 0)) - break; - - name = _glfwCreateUTF8FromWideStringWin32(display.DeviceString); - if (!name) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to convert string to UTF-8"); - continue; - } - - monitor = _glfwAllocMonitor(name, widthMM, heightMM); - free(name); - - if (adapter.StateFlags & DISPLAY_DEVICE_MODESPRUNED) - monitor->win32.modesPruned = GLFW_TRUE; - - wcscpy(monitor->win32.adapterName, adapter.DeviceName); - wcscpy(monitor->win32.displayName, display.DeviceName); - - WideCharToMultiByte(CP_UTF8, 0, - adapter.DeviceName, -1, - monitor->win32.publicAdapterName, - sizeof(monitor->win32.publicAdapterName), - NULL, NULL); - - WideCharToMultiByte(CP_UTF8, 0, - display.DeviceName, -1, - monitor->win32.publicDisplayName, - sizeof(monitor->win32.publicDisplayName), - NULL, NULL); - - found++; - monitors = realloc(monitors, sizeof(_GLFWmonitor*) * found); - monitors[found - 1] = monitor; - - if (adapter.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE && - displayIndex == 0) - { - _GLFW_SWAP_POINTERS(monitors[0], monitors[found - 1]); - } + hasDisplays = GLFW_TRUE; + break; } } + for (adapterIndex = 0; ; adapterIndex++) + { + ZeroMemory(&adapter, sizeof(DISPLAY_DEVICEW)); + adapter.cb = sizeof(DISPLAY_DEVICEW); + + if (!EnumDisplayDevicesW(NULL, adapterIndex, &adapter, 0)) + break; + + if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE)) + continue; + + if (adapter.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) + primaryIndex = found; + + if (hasDisplays) + { + for (displayIndex = 0; ; displayIndex++) + { + ZeroMemory(&display, sizeof(DISPLAY_DEVICEW)); + display.cb = sizeof(DISPLAY_DEVICEW); + + if (!EnumDisplayDevicesW(adapter.DeviceName, displayIndex, &display, 0)) + break; + + found++; + monitors = realloc(monitors, sizeof(_GLFWmonitor*) * found); + monitors[found - 1] = createMonitor(&adapter, &display); + } + } + else + { + found++; + monitors = realloc(monitors, sizeof(_GLFWmonitor*) * found); + monitors[found - 1] = createMonitor(&adapter, NULL); + } + } + + _GLFW_SWAP_POINTERS(monitors[0], monitors[primaryIndex]); + *count = found; return monitors; } GLFWbool _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second) { - return wcscmp(first->win32.displayName, second->win32.displayName) == 0; + if (wcslen(first->win32.displayName)) + return wcscmp(first->win32.displayName, second->win32.displayName) == 0; + else + return wcscmp(first->win32.adapterName, second->win32.adapterName) == 0; } void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) diff --git a/src/win32_platform.h b/src/win32_platform.h index 9c9983a84..f3570bcc2 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -61,8 +61,14 @@ #define _WIN32_WINNT 0x0501 #endif +// GLFW uses DirectInput8 interfaces +#define DIRECTINPUT_VERSION 0x0800 + +#include #include #include +#include +#include #include #if defined(_MSC_VER) @@ -70,7 +76,7 @@ #define strdup _strdup #endif -// HACK: Define macros that some older windows.h variants don't +// HACK: Define macros that some windows.h variants don't #ifndef WM_MOUSEHWHEEL #define WM_MOUSEHWHEEL 0x020E #endif @@ -92,6 +98,12 @@ #ifndef GET_XBUTTON_WPARAM #define GET_XBUTTON_WPARAM(w) (HIWORD(w)) #endif +#ifndef EDS_ROTATEDMODE + #define EDS_ROTATEDMODE 0x00000004 +#endif +#ifndef DISPLAY_DEVICE_ACTIVE + #define DISPLAY_DEVICE_ACTIVE 0x00000001 +#endif #if WINVER < 0x0601 typedef struct tagCHANGEFILTERSTRUCT @@ -114,16 +126,54 @@ typedef enum PROCESS_DPI_AWARENESS } PROCESS_DPI_AWARENESS; #endif /*DPI_ENUMS_DECLARED*/ +// HACK: Define macros that some xinput.h variants don't +#ifndef XINPUT_CAPS_WIRELESS + #define XINPUT_CAPS_WIRELESS 0x0002 +#endif +#ifndef XINPUT_DEVSUBTYPE_WHEEL + #define XINPUT_DEVSUBTYPE_WHEEL 0x02 +#endif +#ifndef XINPUT_DEVSUBTYPE_ARCADE_STICK + #define XINPUT_DEVSUBTYPE_ARCADE_STICK 0x03 +#endif +#ifndef XINPUT_DEVSUBTYPE_FLIGHT_STICK + #define XINPUT_DEVSUBTYPE_FLIGHT_STICK 0x04 +#endif +#ifndef XINPUT_DEVSUBTYPE_DANCE_PAD + #define XINPUT_DEVSUBTYPE_DANCE_PAD 0x05 +#endif +#ifndef XINPUT_DEVSUBTYPE_GUITAR + #define XINPUT_DEVSUBTYPE_GUITAR 0x06 +#endif +#ifndef XINPUT_DEVSUBTYPE_DRUM_KIT + #define XINPUT_DEVSUBTYPE_DRUM_KIT 0x08 +#endif +#ifndef XINPUT_DEVSUBTYPE_ARCADE_PAD + #define XINPUT_DEVSUBTYPE_ARCADE_PAD 0x13 +#endif +#ifndef XUSER_MAX_COUNT + #define XUSER_MAX_COUNT 4 +#endif + +// HACK: Define macros that some dinput.h variants don't +#ifndef DIDFT_OPTIONAL + #define DIDFT_OPTIONAL 0x80000000 +#endif + // winmm.dll function pointer typedefs -typedef MMRESULT (WINAPI * JOYGETDEVCAPS_T)(UINT,LPJOYCAPS,UINT); -typedef MMRESULT (WINAPI * JOYGETPOS_T)(UINT,LPJOYINFO); -typedef MMRESULT (WINAPI * JOYGETPOSEX_T)(UINT,LPJOYINFOEX); typedef DWORD (WINAPI * TIMEGETTIME_T)(void); -#define _glfw_joyGetDevCaps _glfw.win32.winmm.joyGetDevCaps -#define _glfw_joyGetPos _glfw.win32.winmm.joyGetPos -#define _glfw_joyGetPosEx _glfw.win32.winmm.joyGetPosEx #define _glfw_timeGetTime _glfw.win32.winmm.timeGetTime +// xinput.dll function pointer typedefs +typedef DWORD (WINAPI * XINPUTGETCAPABILITIES_T)(DWORD,DWORD,XINPUT_CAPABILITIES*); +typedef DWORD (WINAPI * XINPUTGETSTATE_T)(DWORD,XINPUT_STATE*); +#define _glfw_XInputGetCapabilities _glfw.win32.xinput.XInputGetCapabilities +#define _glfw_XInputGetState _glfw.win32.xinput.XInputGetState + +// dinput8.dll function pointer typedefs +typedef HRESULT (WINAPI * DIRECTINPUT8CREATE_T)(HINSTANCE,DWORD,REFIID,LPVOID*,LPUNKNOWN); +#define _glfw_DirectInput8Create _glfw.win32.dinput8.DirectInput8Create + // user32.dll function pointer typedefs typedef BOOL (WINAPI * SETPROCESSDPIAWARE_T)(void); typedef BOOL (WINAPI * CHANGEWINDOWMESSAGEFILTEREX_T)(HWND,UINT,DWORD,PCHANGEFILTERSTRUCT); @@ -155,16 +205,8 @@ typedef VkResult (APIENTRY *PFN_vkCreateWin32SurfaceKHR)(VkInstance,const VkWin3 typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)(VkPhysicalDevice,uint32_t); #include "win32_joystick.h" - -#if defined(_GLFW_WGL) - #include "wgl_context.h" -#elif defined(_GLFW_EGL) - #define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->win32.handle) - #define _GLFW_EGL_NATIVE_DISPLAY EGL_DEFAULT_DISPLAY - #include "egl_context.h" -#else - #error "No supported context creation API selected" -#endif +#include "wgl_context.h" +#include "egl_context.h" #define _GLFW_WNDCLASSNAME L"GLFW30" @@ -172,6 +214,9 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)( #define _glfw_dlclose(handle) FreeLibrary((HMODULE) handle) #define _glfw_dlsym(handle, name) GetProcAddress((HMODULE) handle, name) +#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->win32.handle) +#define _GLFW_EGL_NATIVE_DISPLAY EGL_DEFAULT_DISPLAY + #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowWin32 win32 #define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryWin32 win32 #define _GLFW_PLATFORM_LIBRARY_TIME_STATE _GLFWtimeWin32 win32_time @@ -185,14 +230,12 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)( typedef struct _GLFWwindowWin32 { HWND handle; + HICON bigIcon; + HICON smallIcon; GLFWbool cursorTracked; GLFWbool iconified; - int minwidth, minheight; - int maxwidth, maxheight; - int numer, denom; - // The last received cursor position, regardless of source int cursorPosX, cursorPosY; @@ -210,30 +253,35 @@ typedef struct _GLFWlibraryWin32 short int publicKeys[512]; short int nativeKeys[GLFW_KEY_LAST + 1]; - // winmm.dll struct { HINSTANCE instance; - JOYGETDEVCAPS_T joyGetDevCaps; - JOYGETPOS_T joyGetPos; - JOYGETPOSEX_T joyGetPosEx; TIMEGETTIME_T timeGetTime; } winmm; - // user32.dll struct { - HINSTANCE instance; - SETPROCESSDPIAWARE_T SetProcessDPIAware; + HINSTANCE instance; + DIRECTINPUT8CREATE_T DirectInput8Create; + IDirectInput8W* api; + } dinput8; + + struct { + HINSTANCE instance; + XINPUTGETCAPABILITIES_T XInputGetCapabilities; + XINPUTGETSTATE_T XInputGetState; + } xinput; + + struct { + HINSTANCE instance; + SETPROCESSDPIAWARE_T SetProcessDPIAware; CHANGEWINDOWMESSAGEFILTEREX_T ChangeWindowMessageFilterEx; } user32; - // dwmapi.dll struct { HINSTANCE instance; DWMISCOMPOSITIONENABLED_T DwmIsCompositionEnabled; DWMFLUSH_T DwmFlush; } dwmapi; - // shcore.dll struct { HINSTANCE instance; SETPROCESSDPIAWARENESS_T SetProcessDpiAwareness; @@ -271,8 +319,7 @@ typedef struct _GLFWcursorWin32 typedef struct _GLFWtimeWin32 { GLFWbool hasPC; - double resolution; - unsigned __int64 base; + uint64_t frequency; } _GLFWtimeWin32; diff --git a/src/win32_time.c b/src/win32_time.c index c8bb61f82..43e673512 100644 --- a/src/win32_time.c +++ b/src/win32_time.c @@ -28,21 +28,6 @@ #include "internal.h" -// Return raw time -// -static unsigned __int64 getRawTime(void) -{ - if (_glfw.win32_time.hasPC) - { - unsigned __int64 time; - QueryPerformanceCounter((LARGE_INTEGER*) &time); - return time; - } - else - return (unsigned __int64) _glfw_timeGetTime(); -} - - ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// @@ -51,20 +36,18 @@ static unsigned __int64 getRawTime(void) // void _glfwInitTimerWin32(void) { - unsigned __int64 frequency; + uint64_t frequency; if (QueryPerformanceFrequency((LARGE_INTEGER*) &frequency)) { _glfw.win32_time.hasPC = GLFW_TRUE; - _glfw.win32_time.resolution = 1.0 / (double) frequency; + _glfw.win32_time.frequency = frequency; } else { _glfw.win32_time.hasPC = GLFW_FALSE; - _glfw.win32_time.resolution = 0.001; // winmm resolution is 1 ms + _glfw.win32_time.frequency = 1000; } - - _glfw.win32_time.base = getRawTime(); } @@ -72,15 +55,20 @@ void _glfwInitTimerWin32(void) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -double _glfwPlatformGetTime(void) +uint64_t _glfwPlatformGetTimerValue(void) { - return (double) (getRawTime() - _glfw.win32_time.base) * - _glfw.win32_time.resolution; + if (_glfw.win32_time.hasPC) + { + uint64_t value; + QueryPerformanceCounter((LARGE_INTEGER*) &value); + return value; + } + else + return (uint64_t) _glfw_timeGetTime(); } -void _glfwPlatformSetTime(double time) +uint64_t _glfwPlatformGetTimerFrequency(void) { - _glfw.win32_time.base = getRawTime() - - (unsigned __int64) (time / _glfw.win32_time.resolution); + return _glfw.win32_time.frequency; } diff --git a/src/win32_window.c b/src/win32_window.c index 0de246db7..b5c03ea3f 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -27,6 +27,7 @@ #include "internal.h" +#include #include #include #include @@ -41,15 +42,20 @@ static DWORD getWindowStyle(const _GLFWwindow* window) { DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN; - if (window->decorated && !window->monitor) - { - style |= WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; - - if (window->resizable) - style |= WS_MAXIMIZEBOX | WS_SIZEBOX; - } - else + if (window->monitor) style |= WS_POPUP; + else + { + if (window->decorated) + { + style |= WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; + + if (window->resizable) + style |= WS_MAXIMIZEBOX | WS_THICKFRAME; + } + else + style |= WS_POPUP; + } return style; } @@ -60,12 +66,118 @@ static DWORD getWindowExStyle(const _GLFWwindow* window) { DWORD style = WS_EX_APPWINDOW; - if (window->decorated && !window->monitor) - style |= WS_EX_WINDOWEDGE; + if (window->monitor || window->floating) + style |= WS_EX_TOPMOST; return style; } +// Returns the image whose area most closely matches the desired one +// +static const GLFWimage* chooseImage(int count, const GLFWimage* images, + int width, int height) +{ + int i, leastDiff = INT_MAX; + const GLFWimage* closest = NULL; + + for (i = 0; i < count; i++) + { + const int currDiff = abs(images[i].width * images[i].height - + width * height); + if (currDiff < leastDiff) + { + closest = images + i; + leastDiff = currDiff; + } + } + + return closest; +} + +// Creates an RGBA icon or cursor +// +static HICON createIcon(const GLFWimage* image, + int xhot, int yhot, GLFWbool icon) +{ + int i; + HDC dc; + HICON handle; + HBITMAP color, mask; + BITMAPV5HEADER bi; + ICONINFO ii; + unsigned char* target = NULL; + unsigned char* source = image->pixels; + + ZeroMemory(&bi, sizeof(bi)); + bi.bV5Size = sizeof(BITMAPV5HEADER); + bi.bV5Width = image->width; + bi.bV5Height = -image->height; + bi.bV5Planes = 1; + bi.bV5BitCount = 32; + bi.bV5Compression = BI_BITFIELDS; + bi.bV5RedMask = 0x00ff0000; + bi.bV5GreenMask = 0x0000ff00; + bi.bV5BlueMask = 0x000000ff; + bi.bV5AlphaMask = 0xff000000; + + dc = GetDC(NULL); + color = CreateDIBSection(dc, + (BITMAPINFO*) &bi, + DIB_RGB_COLORS, + (void**) &target, + NULL, + (DWORD) 0); + ReleaseDC(NULL, dc); + + if (!color) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to create RGBA bitmap"); + return NULL; + } + + mask = CreateBitmap(image->width, image->height, 1, 1, NULL); + if (!mask) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to create mask bitmap"); + DeleteObject(color); + return NULL; + } + + for (i = 0; i < image->width * image->height; i++) + { + target[0] = source[2]; + target[1] = source[1]; + target[2] = source[0]; + target[3] = source[3]; + target += 4; + source += 4; + } + + ZeroMemory(&ii, sizeof(ii)); + ii.fIcon = icon; + ii.xHotspot = xhot; + ii.yHotspot = yhot; + ii.hbmMask = mask; + ii.hbmColor = color; + + handle = CreateIconIndirect(&ii); + + DeleteObject(color); + DeleteObject(mask); + + if (!handle) + { + if (icon) + _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create icon"); + else + _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create cursor"); + } + + return handle; +} + // Translate client window size to full window size according to styles // static void getFullWindowSize(DWORD style, DWORD exStyle, @@ -83,8 +195,7 @@ static void getFullWindowSize(DWORD style, DWORD exStyle, static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area) { int xoff, yoff; - const float ratio = (float) window->win32.numer / - (float) window->win32.denom; + const float ratio = (float) window->numer / (float) window->denom; getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), 0, 0, &xoff, &yoff); @@ -221,9 +332,9 @@ static int translateKey(WPARAM wParam, LPARAM lParam) return _glfw.win32.publicKeys[HIWORD(lParam) & 0x1FF]; } -// Enter full screen mode +// Make the specified window and its video mode active on its monitor // -static GLFWbool enterFullscreenMode(_GLFWwindow* window) +static GLFWbool acquireMonitor(_GLFWwindow* window) { GLFWvidmode mode; GLFWbool status; @@ -235,15 +346,21 @@ static GLFWbool enterFullscreenMode(_GLFWwindow* window) _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); SetWindowPos(window->win32.handle, HWND_TOPMOST, - xpos, ypos, mode.width, mode.height, SWP_NOCOPYBITS); + xpos, ypos, mode.width, mode.height, + SWP_NOACTIVATE | SWP_NOCOPYBITS); + _glfwInputMonitorWindowChange(window->monitor, window); return status; } -// Leave full screen mode +// Remove the window and restore the original video mode // -static void leaveFullscreenMode(_GLFWwindow* window) +static void releaseMonitor(_GLFWwindow* window) { + if (window->monitor->window != window) + return; + + _glfwInputMonitorWindowChange(window->monitor, NULL); _glfwRestoreVideoModeWin32(window->monitor); } @@ -252,18 +369,13 @@ static void leaveFullscreenMode(_GLFWwindow* window) static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - _GLFWwindow* window = (_GLFWwindow*) GetWindowLongPtrW(hWnd, 0); + _GLFWwindow* window = (_GLFWwindow*) GetPropW(hWnd, L"GLFW"); if (!window) { + // This is the message handling for the hidden helper window + switch (uMsg) { - case WM_NCCREATE: - { - CREATESTRUCTW* cs = (CREATESTRUCTW*) lParam; - SetWindowLongPtrW(hWnd, 0, (LONG_PTR) cs->lpCreateParams); - break; - } - case WM_DEVICECHANGE: { if (wParam == DBT_DEVNODES_CHANGED) @@ -271,6 +383,24 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, _glfwInputMonitorChange(); return TRUE; } + else if (wParam == DBT_DEVICEARRIVAL) + { + DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; + if (dbh) + { + if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) + _glfwDetectJoystickConnectionWin32(); + } + } + else if (wParam == DBT_DEVICEREMOVECOMPLETE) + { + DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; + if (dbh) + { + if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) + _glfwDetectJoystickDisconnectionWin32(); + } + } break; } @@ -483,32 +613,39 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_SIZE: { + const GLFWbool iconified = + !window->win32.iconified && wParam == SIZE_MINIMIZED; + const GLFWbool restored = + window->win32.iconified && + (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED); + if (_glfw.cursorWindow == window) { if (window->cursorMode == GLFW_CURSOR_DISABLED) updateClipRect(window); } - if (!window->win32.iconified && wParam == SIZE_MINIMIZED) - { - window->win32.iconified = GLFW_TRUE; - if (window->monitor) - leaveFullscreenMode(window); - + if (iconified) _glfwInputWindowIconify(window, GLFW_TRUE); - } - else if (window->win32.iconified && - (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)) - { - window->win32.iconified = GLFW_FALSE; - if (window->monitor) - enterFullscreenMode(window); - + else if (restored) _glfwInputWindowIconify(window, GLFW_FALSE); - } _glfwInputFramebufferSize(window, LOWORD(lParam), HIWORD(lParam)); _glfwInputWindowSize(window, LOWORD(lParam), HIWORD(lParam)); + + if (iconified) + { + window->win32.iconified = GLFW_TRUE; + if (window->monitor) + releaseMonitor(window); + } + else if (restored) + { + window->win32.iconified = GLFW_FALSE; + if (window->monitor) + acquireMonitor(window); + } + return 0; } @@ -530,8 +667,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_SIZING: { - if (window->win32.numer == GLFW_DONT_CARE || - window->win32.denom == GLFW_DONT_CARE) + if (window->numer == GLFW_DONT_CARE || + window->denom == GLFW_DONT_CARE) { break; } @@ -545,21 +682,24 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, int xoff, yoff; MINMAXINFO* mmi = (MINMAXINFO*) lParam; + if (window->monitor) + break; + getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), 0, 0, &xoff, &yoff); - if (window->win32.minwidth != GLFW_DONT_CARE && - window->win32.minheight != GLFW_DONT_CARE) + if (window->minwidth != GLFW_DONT_CARE && + window->minheight != GLFW_DONT_CARE) { - mmi->ptMinTrackSize.x = window->win32.minwidth + xoff; - mmi->ptMinTrackSize.y = window->win32.minheight + yoff; + mmi->ptMinTrackSize.x = window->minwidth + xoff; + mmi->ptMinTrackSize.y = window->minheight + yoff; } - if (window->win32.maxwidth != GLFW_DONT_CARE && - window->win32.maxheight != GLFW_DONT_CARE) + if (window->maxwidth != GLFW_DONT_CARE && + window->maxheight != GLFW_DONT_CARE) { - mmi->ptMaxTrackSize.x = window->win32.maxwidth + xoff; - mmi->ptMaxTrackSize.y = window->win32.maxheight + yoff; + mmi->ptMaxTrackSize.x = window->maxwidth + xoff; + mmi->ptMaxTrackSize.y = window->maxheight + yoff; } return 0; @@ -653,16 +793,18 @@ static int createWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig) { int xpos, ypos, fullWidth, fullHeight; WCHAR* wideTitle; + DWORD style = getWindowStyle(window); + DWORD exStyle = getWindowExStyle(window); - if (wndconfig->monitor) + if (window->monitor) { GLFWvidmode mode; // NOTE: This window placement is temporary and approximate, as the // correct position and size cannot be known until the monitor // video mode has been set - _glfwPlatformGetMonitorPos(wndconfig->monitor, &xpos, &ypos); - _glfwPlatformGetVideoMode(wndconfig->monitor, &mode); + _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); + _glfwPlatformGetVideoMode(window->monitor, &mode); fullWidth = mode.width; fullHeight = mode.height; } @@ -671,7 +813,10 @@ static int createWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig) xpos = CW_USEDEFAULT; ypos = CW_USEDEFAULT; - getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), + if (wndconfig->maximized) + style |= WS_MAXIMIZE; + + getFullWindowSize(style, exStyle, wndconfig->width, wndconfig->height, &fullWidth, &fullHeight); } @@ -684,16 +829,16 @@ static int createWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig) return GLFW_FALSE; } - window->win32.handle = CreateWindowExW(getWindowExStyle(window), + window->win32.handle = CreateWindowExW(exStyle, _GLFW_WNDCLASSNAME, wideTitle, - getWindowStyle(window), + style, xpos, ypos, fullWidth, fullHeight, NULL, // No parent window NULL, // No window menu GetModuleHandleW(NULL), - window); // Pass object to WM_CREATE + NULL); free(wideTitle); @@ -703,6 +848,8 @@ static int createWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig) return GLFW_FALSE; } + SetPropW(window->win32.handle, L"GLFW", window); + if (_glfw_ChangeWindowMessageFilterEx) { _glfw_ChangeWindowMessageFilterEx(window->win32.handle, @@ -713,23 +860,8 @@ static int createWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig) WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL); } - if (wndconfig->floating && !wndconfig->monitor) - { - SetWindowPos(window->win32.handle, - HWND_TOPMOST, - 0, 0, 0, 0, - SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); - } - DragAcceptFiles(window->win32.handle, TRUE); - window->win32.minwidth = GLFW_DONT_CARE; - window->win32.minheight = GLFW_DONT_CARE; - window->win32.maxwidth = GLFW_DONT_CARE; - window->win32.maxheight = GLFW_DONT_CARE; - window->win32.numer = GLFW_DONT_CARE; - window->win32.denom = GLFW_DONT_CARE; - return GLFW_TRUE; } @@ -739,6 +871,7 @@ static void destroyWindow(_GLFWwindow* window) { if (window->win32.handle) { + RemovePropW(window->win32.handle, L"GLFW"); DestroyWindow(window->win32.handle); window->win32.handle = NULL; } @@ -759,7 +892,6 @@ GLFWbool _glfwRegisterWindowClassWin32(void) wc.cbSize = sizeof(wc); wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wc.lpfnWndProc = (WNDPROC) windowProc; - wc.cbWndExtra = sizeof(void*) + sizeof(int); // Make room for one pointer wc.hInstance = GetModuleHandleW(NULL); wc.hCursor = LoadCursorW(NULL, IDC_ARROW); wc.lpszClassName = _GLFW_WNDCLASSNAME; @@ -808,61 +940,65 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!createWindow(window, wndconfig)) return GLFW_FALSE; - if (ctxconfig->api != GLFW_NO_API) + if (ctxconfig->client != GLFW_NO_API) { -#if defined(_GLFW_WGL) - if (!_glfwCreateContextWGL(window, ctxconfig, fbconfig)) - return GLFW_FALSE; - - status = _glfwAnalyzeContextWGL(window, ctxconfig, fbconfig); - - if (status == _GLFW_RECREATION_IMPOSSIBLE) - return GLFW_FALSE; - - if (status == _GLFW_RECREATION_REQUIRED) + if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) { - // Some window hints require us to re-create the context using WGL - // extensions retrieved through the current context, as we cannot - // check for WGL extensions or retrieve WGL entry points before we - // have a current context (actually until we have implicitly loaded - // the vendor ICD) - - // Yes, this is strange, and yes, this is the proper way on WGL - - // As Windows only allows you to set the pixel format once for - // a window, we need to destroy the current window and create a new - // one to be able to use the new pixel format - - // Technically, it may be possible to keep the old window around if - // we're just creating an OpenGL 3.0+ context with the same pixel - // format, but it's not worth the added code complexity - - // First we clear the current context (the one we just created) - // This is usually done by glfwDestroyWindow, but as we're not doing - // full GLFW window destruction, it's duplicated here - _glfwPlatformMakeContextCurrent(NULL); - - // Next destroy the Win32 window and WGL context (without resetting - // or destroying the GLFW window object) - _glfwDestroyContextWGL(window); - destroyWindow(window); - - // ...and then create them again, this time with better APIs - if (!createWindow(window, wndconfig)) - return GLFW_FALSE; if (!_glfwCreateContextWGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; + + status = _glfwAnalyzeContextWGL(window, ctxconfig, fbconfig); + + if (status == _GLFW_RECREATION_IMPOSSIBLE) + return GLFW_FALSE; + + if (status == _GLFW_RECREATION_REQUIRED) + { + // Some window hints require us to re-create the context using WGL + // extensions retrieved through the current context, as we cannot + // check for WGL extensions or retrieve WGL entry points before we + // have a current context (actually until we have implicitly loaded + // the vendor ICD) + + // Yes, this is strange, and yes, this is the proper way on WGL + + // As Windows only allows you to set the pixel format once for + // a window, we need to destroy the current window and create a new + // one to be able to use the new pixel format + + // Technically, it may be possible to keep the old window around if + // we're just creating an OpenGL 3.0+ context with the same pixel + // format, but it's not worth the added code complexity + + // First we clear the current context (the one we just created) + // This is usually done by glfwDestroyWindow, but as we're not doing + // full GLFW window destruction, it's duplicated here + window->context.makeContextCurrent(NULL); + + // Next destroy the Win32 window and WGL context (without resetting + // or destroying the GLFW window object) + window->context.destroyContext(window); + destroyWindow(window); + + // ...and then create them again, this time with better APIs + if (!createWindow(window, wndconfig)) + return GLFW_FALSE; + if (!_glfwCreateContextWGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + } + else + { + if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; } -#elif defined(_GLFW_EGL) - if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) - return GLFW_FALSE; -#endif } if (window->monitor) { _glfwPlatformShowWindow(window); - if (!enterFullscreenMode(window)) + _glfwPlatformFocusWindow(window); + if (!acquireMonitor(window)) return GLFW_FALSE; } @@ -872,18 +1008,18 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, void _glfwPlatformDestroyWindow(_GLFWwindow* window) { if (window->monitor) - leaveFullscreenMode(window); + releaseMonitor(window); - if (window->context.api != GLFW_NO_API) - { -#if defined(_GLFW_WGL) - _glfwDestroyContextWGL(window); -#elif defined(_GLFW_EGL) - _glfwDestroyContextEGL(window); -#endif - } + if (window->context.client != GLFW_NO_API) + window->context.destroyContext(window); destroyWindow(window); + + if (window->win32.bigIcon) + DestroyIcon(window->win32.bigIcon); + + if (window->win32.smallIcon) + DestroyIcon(window->win32.smallIcon); } void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) @@ -900,6 +1036,45 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) free(wideTitle); } +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, + int count, const GLFWimage* images) +{ + HICON bigIcon = NULL, smallIcon = NULL; + + if (count) + { + const GLFWimage* bigImage = chooseImage(count, images, + GetSystemMetrics(SM_CXICON), + GetSystemMetrics(SM_CYICON)); + const GLFWimage* smallImage = chooseImage(count, images, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON)); + + bigIcon = createIcon(bigImage, 0, 0, GLFW_TRUE); + smallIcon = createIcon(smallImage, 0, 0, GLFW_TRUE); + } + else + { + bigIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICON); + smallIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICONSM); + } + + SendMessage(window->win32.handle, WM_SETICON, ICON_BIG, (LPARAM) bigIcon); + SendMessage(window->win32.handle, WM_SETICON, ICON_SMALL, (LPARAM) smallIcon); + + if (window->win32.bigIcon) + DestroyIcon(window->win32.bigIcon); + + if (window->win32.smallIcon) + DestroyIcon(window->win32.smallIcon); + + if (count) + { + window->win32.bigIcon = bigIcon; + window->win32.smallIcon = smallIcon; + } +} + void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) { POINT pos = { 0, 0 }; @@ -934,15 +1109,17 @@ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) { if (window->monitor) - enterFullscreenMode(window); + { + if (window->monitor->window == window) + acquireMonitor(window); + } else { - int fullWidth, fullHeight; - getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), - width, height, &fullWidth, &fullHeight); - + RECT rect = { 0, 0, width, height }; + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); SetWindowPos(window->win32.handle, HWND_TOP, - 0, 0, fullWidth, fullHeight, + 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER); } } @@ -953,11 +1130,6 @@ void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, { RECT area; - window->win32.minwidth = minwidth; - window->win32.minheight = minheight; - window->win32.maxwidth = maxwidth; - window->win32.maxheight = maxheight; - if ((minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) && (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE)) { @@ -975,9 +1147,6 @@ void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom { RECT area; - window->win32.numer = numer; - window->win32.denom = denom; - if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE) return; @@ -1026,15 +1195,12 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window) ShowWindow(window->win32.handle, SW_RESTORE); } -void _glfwPlatformShowWindow(_GLFWwindow* window) +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) { - ShowWindow(window->win32.handle, SW_SHOW); - BringWindowToTop(window->win32.handle); - SetForegroundWindow(window->win32.handle); - SetFocus(window->win32.handle); + ShowWindow(window->win32.handle, SW_MAXIMIZE); } -void _glfwPlatformUnhideWindow(_GLFWwindow* window) +void _glfwPlatformShowWindow(_GLFWwindow* window) { ShowWindow(window->win32.handle, SW_SHOW); } @@ -1044,6 +1210,99 @@ void _glfwPlatformHideWindow(_GLFWwindow* window) ShowWindow(window->win32.handle, SW_HIDE); } +void _glfwPlatformFocusWindow(_GLFWwindow* window) +{ + BringWindowToTop(window->win32.handle); + SetForegroundWindow(window->win32.handle); + SetFocus(window->win32.handle); +} + +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) +{ + if (window->monitor == monitor) + { + if (monitor) + { + if (monitor->window == window) + acquireMonitor(window); + } + else + { + RECT rect = { xpos, ypos, xpos + width, ypos + height }; + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); + SetWindowPos(window->win32.handle, HWND_TOP, + rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, + SWP_NOCOPYBITS | SWP_NOACTIVATE | SWP_NOZORDER); + } + + return; + } + + if (window->monitor) + releaseMonitor(window); + + _glfwInputWindowMonitorChange(window, monitor); + + if (monitor) + { + GLFWvidmode mode; + DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); + UINT flags = SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOCOPYBITS; + + if (window->decorated) + { + style &= ~WS_OVERLAPPEDWINDOW; + style |= getWindowStyle(window); + SetWindowLongW(window->win32.handle, GWL_STYLE, style); + + flags |= SWP_FRAMECHANGED; + } + + _glfwPlatformGetVideoMode(monitor, &mode); + _glfwPlatformGetMonitorPos(monitor, &xpos, &ypos); + + SetWindowPos(window->win32.handle, HWND_TOPMOST, + xpos, ypos, mode.width, mode.height, + flags); + + acquireMonitor(window); + } + else + { + HWND after; + RECT rect = { xpos, ypos, xpos + width, ypos + height }; + DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); + UINT flags = SWP_NOACTIVATE | SWP_NOCOPYBITS; + + if (window->decorated) + { + style &= ~WS_POPUP; + style |= getWindowStyle(window); + SetWindowLongW(window->win32.handle, GWL_STYLE, style); + + flags |= SWP_FRAMECHANGED; + } + + if (window->floating) + after = HWND_TOPMOST; + else + after = HWND_NOTOPMOST; + + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); + SetWindowPos(window->win32.handle, after, + rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, + flags); + } +} + int _glfwPlatformWindowFocused(_GLFWwindow* window) { return window->win32.handle == GetActiveWindow(); @@ -1059,6 +1318,11 @@ int _glfwPlatformWindowVisible(_GLFWwindow* window) return IsWindowVisible(window->win32.handle); } +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + return IsZoomed(window->win32.handle); +} + void _glfwPlatformPollEvents(void) { MSG msg; @@ -1131,6 +1395,13 @@ void _glfwPlatformWaitEvents(void) _glfwPlatformPollEvents(); } +void _glfwPlatformWaitEventsTimeout(double timeout) +{ + MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD) (timeout * 1e3), QS_ALLEVENTS); + + _glfwPlatformPollEvents(); +} + void _glfwPlatformPostEmptyEvent(void) { _GLFWwindow* window = _glfw.windowListHead; @@ -1218,61 +1489,7 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot) { - HDC dc; - HBITMAP bitmap, mask; - BITMAPV5HEADER bi; - ICONINFO ii; - DWORD* target = 0; - BYTE* source = (BYTE*) image->pixels; - int i; - - ZeroMemory(&bi, sizeof(bi)); - bi.bV5Size = sizeof(BITMAPV5HEADER); - bi.bV5Width = image->width; - bi.bV5Height = -image->height; - bi.bV5Planes = 1; - bi.bV5BitCount = 32; - bi.bV5Compression = BI_BITFIELDS; - bi.bV5RedMask = 0x00ff0000; - bi.bV5GreenMask = 0x0000ff00; - bi.bV5BlueMask = 0x000000ff; - bi.bV5AlphaMask = 0xff000000; - - dc = GetDC(NULL); - bitmap = CreateDIBSection(dc, (BITMAPINFO*) &bi, DIB_RGB_COLORS, - (void**) &target, NULL, (DWORD) 0); - ReleaseDC(NULL, dc); - - if (!bitmap) - return GLFW_FALSE; - - mask = CreateBitmap(image->width, image->height, 1, 1, NULL); - if (!mask) - { - DeleteObject(bitmap); - return GLFW_FALSE; - } - - for (i = 0; i < image->width * image->height; i++, target++, source += 4) - { - *target = (source[3] << 24) | - (source[0] << 16) | - (source[1] << 8) | - source[2]; - } - - ZeroMemory(&ii, sizeof(ii)); - ii.fIcon = FALSE; - ii.xHotspot = xhot; - ii.yHotspot = yhot; - ii.hbmMask = mask; - ii.hbmColor = bitmap; - - cursor->win32.handle = (HCURSOR) CreateIconIndirect(&ii); - - DeleteObject(bitmap); - DeleteObject(mask); - + cursor->win32.handle = (HCURSOR) createIcon(image, xhot, yhot, GLFW_FALSE); if (!cursor->win32.handle) return GLFW_FALSE; @@ -1301,6 +1518,7 @@ void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) { + RECT area; POINT pos; if (_glfw.cursorWindow != window) @@ -1315,6 +1533,13 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) if (WindowFromPoint(pos) != window->win32.handle) return; + GetClientRect(window->win32.handle, &area); + ClientToScreen(window->win32.handle, (POINT*) &area.left); + ClientToScreen(window->win32.handle, (POINT*) &area.right); + + if (!PtInRect(&area, pos)) + return; + if (cursor) SetCursor(cursor->win32.handle); else @@ -1403,7 +1628,7 @@ const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) return _glfw.win32.clipboardString; } -char** _glfwPlatformGetRequiredInstanceExtensions(int* count) +char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count) { char** extensions; @@ -1422,7 +1647,7 @@ char** _glfwPlatformGetRequiredInstanceExtensions(int* count) int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, - unsigned int queuefamily) + uint32_t queuefamily) { PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR vkGetPhysicalDeviceWin32PresentationSupportKHR = (PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR) diff --git a/src/window.c b/src/window.c index 5b0706e6c..397cf5774 100644 --- a/src/window.c +++ b/src/window.c @@ -31,6 +31,7 @@ #include #include #include +#include ////////////////////////////////////////////////////////////////////////// @@ -109,6 +110,11 @@ void _glfwInputWindowCloseRequest(_GLFWwindow* window) window->callbacks.close((GLFWwindow*) window); } +void _glfwInputWindowMonitorChange(_GLFWwindow* window, _GLFWmonitor* monitor) +{ + window->monitor = monitor; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW public API ////// @@ -125,44 +131,37 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, _GLFWwindow* window; _GLFWwindow* previous; - assert(title); + assert(title != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); if (width <= 0 || height <= 0) { - _glfwInputError(GLFW_INVALID_VALUE, "Invalid window size"); + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid window size %ix%i", + width, height); + return NULL; } - fbconfig = _glfw.hints.framebuffer; - ctxconfig = _glfw.hints.context; - wndconfig = _glfw.hints.window; - fbconfig.transparent = _glfw.hints.framebuffer.transparent; + fbconfig = _glfw.hints.framebuffer; + ctxconfig = _glfw.hints.context; + wndconfig = _glfw.hints.window; - wndconfig.width = width; - wndconfig.height = height; - wndconfig.title = title; - wndconfig.monitor = (_GLFWmonitor*) monitor; - ctxconfig.share = (_GLFWwindow*) share; - wndconfig.transparent = _glfw.hints.framebuffer.transparent; + wndconfig.width = width; + wndconfig.height = height; + wndconfig.title = title; + ctxconfig.share = (_GLFWwindow*) share; if (ctxconfig.share) { - if (ctxconfig.share->context.api == GLFW_NO_API) + if (ctxconfig.share->context.client == GLFW_NO_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return NULL; } } - if (wndconfig.monitor) - { - wndconfig.resizable = GLFW_TRUE; - wndconfig.visible = GLFW_TRUE; - wndconfig.focused = GLFW_TRUE; - } - if (!_glfwIsValidContextConfig(&ctxconfig)) return NULL; @@ -177,7 +176,7 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, window->videoMode.blueBits = fbconfig.blueBits; window->videoMode.refreshRate = _glfw.hints.refreshRate; - window->monitor = wndconfig.monitor; + window->monitor = (_GLFWmonitor*) monitor; window->resizable = wndconfig.resizable; window->decorated = wndconfig.decorated; window->autoIconify = wndconfig.autoIconify; @@ -185,42 +184,43 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, window->transparent = wndconfig.transparent; window->cursorMode = GLFW_CURSOR_NORMAL; + window->minwidth = GLFW_DONT_CARE; + window->minheight = GLFW_DONT_CARE; + window->maxwidth = GLFW_DONT_CARE; + window->maxheight = GLFW_DONT_CARE; + window->numer = GLFW_DONT_CARE; + window->denom = GLFW_DONT_CARE; + // Save the currently current context so it can be restored later previous = _glfwPlatformGetCurrentContext(); + if (ctxconfig.client != GLFW_NO_API) + glfwMakeContextCurrent(NULL); // Open the actual window and create its context if (!_glfwPlatformCreateWindow(window, &wndconfig, &ctxconfig, &fbconfig)) { + glfwMakeContextCurrent((GLFWwindow*) previous); glfwDestroyWindow((GLFWwindow*) window); - _glfwPlatformMakeContextCurrent(previous); return NULL; } - if (ctxconfig.api != GLFW_NO_API) + if (ctxconfig.client != GLFW_NO_API) { - _glfwPlatformMakeContextCurrent(window); + window->context.makeContextCurrent(window); // Retrieve the actual (as opposed to requested) context attributes if (!_glfwRefreshContextAttribs(&ctxconfig)) { + glfwMakeContextCurrent((GLFWwindow*) previous); glfwDestroyWindow((GLFWwindow*) window); - _glfwPlatformMakeContextCurrent(previous); - return NULL; - } - - // Verify the context against the requested parameters - if (!_glfwIsValidContext(&ctxconfig)) - { - glfwDestroyWindow((GLFWwindow*) window); - _glfwPlatformMakeContextCurrent(previous); return NULL; } // Restore the previously current context (or NULL) - _glfwPlatformMakeContextCurrent(previous); + glfwMakeContextCurrent((GLFWwindow*) previous); } - if (wndconfig.monitor) + if (window->monitor) { int width, height; _glfwPlatformGetWindowSize(window, &width, &height); @@ -234,10 +234,9 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, { if (wndconfig.visible) { + _glfwPlatformShowWindow(window); if (wndconfig.focused) - _glfwPlatformShowWindow(window); - else - _glfwPlatformUnhideWindow(window); + _glfwPlatformFocusWindow(window); } } @@ -251,9 +250,10 @@ void glfwDefaultWindowHints(void) memset(&_glfw.hints, 0, sizeof(_glfw.hints)); // The default is OpenGL with minimum version 1.0 - _glfw.hints.context.api = GLFW_OPENGL_API; - _glfw.hints.context.major = 1; - _glfw.hints.context.minor = 0; + _glfw.hints.context.client = GLFW_OPENGL_API; + _glfw.hints.context.source = GLFW_NATIVE_CONTEXT_API; + _glfw.hints.context.major = 1; + _glfw.hints.context.minor = 0; // The default is a focused, visible, resizable window with decorations _glfw.hints.window.resizable = GLFW_TRUE; @@ -346,11 +346,17 @@ GLFWAPI void glfwWindowHint(int hint, int value) case GLFW_FLOATING: _glfw.hints.window.floating = value ? GLFW_TRUE : GLFW_FALSE; break; + case GLFW_MAXIMIZED: + _glfw.hints.window.maximized = value ? GLFW_TRUE : GLFW_FALSE; + break; case GLFW_VISIBLE: _glfw.hints.window.visible = value ? GLFW_TRUE : GLFW_FALSE; break; case GLFW_CLIENT_API: - _glfw.hints.context.api = value; + _glfw.hints.context.client = value; + break; + case GLFW_CONTEXT_CREATION_API: + _glfw.hints.context.source = value; break; case GLFW_CONTEXT_VERSION_MAJOR: _glfw.hints.context.major = value; @@ -380,7 +386,7 @@ GLFWAPI void glfwWindowHint(int hint, int value) _glfw.hints.refreshRate = value; break; default: - _glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint"); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint %i", hint); break; } } @@ -401,7 +407,7 @@ GLFWAPI void glfwDestroyWindow(GLFWwindow* handle) // The window's context must not be current on another thread when the // window is destroyed if (window == _glfwPlatformGetCurrentContext()) - _glfwPlatformMakeContextCurrent(NULL); + glfwMakeContextCurrent(NULL); // Clear the focused window pointer if this is the focused window if (_glfw.cursorWindow == window) @@ -425,7 +431,7 @@ GLFWAPI void glfwDestroyWindow(GLFWwindow* handle) GLFWAPI int glfwWindowShouldClose(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(0); return window->closed; @@ -434,7 +440,7 @@ GLFWAPI int glfwWindowShouldClose(GLFWwindow* handle) GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* handle, int value) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT(); window->closed = value; @@ -443,18 +449,30 @@ GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* handle, int value) GLFWAPI void glfwSetWindowTitle(GLFWwindow* handle, const char* title) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); - assert(title); + assert(title != NULL); _GLFW_REQUIRE_INIT(); _glfwPlatformSetWindowTitle(window, title); } +GLFWAPI void glfwSetWindowIcon(GLFWwindow* handle, + int count, const GLFWimage* images) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + assert(count >= 0); + assert(count == 0 || images != NULL); + + _GLFW_REQUIRE_INIT(); + _glfwPlatformSetWindowIcon(window, count, images); +} + GLFWAPI void glfwGetWindowPos(GLFWwindow* handle, int* xpos, int* ypos) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); if (xpos) *xpos = 0; @@ -468,16 +486,12 @@ GLFWAPI void glfwGetWindowPos(GLFWwindow* handle, int* xpos, int* ypos) GLFWAPI void glfwSetWindowPos(GLFWwindow* handle, int xpos, int ypos) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT(); if (window->monitor) - { - _glfwInputError(GLFW_INVALID_VALUE, - "Full screen windows cannot be moved"); return; - } _glfwPlatformSetWindowPos(window, xpos, ypos); } @@ -485,7 +499,7 @@ GLFWAPI void glfwSetWindowPos(GLFWwindow* handle, int xpos, int ypos) GLFWAPI void glfwGetWindowSize(GLFWwindow* handle, int* width, int* height) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); if (width) *width = 0; @@ -499,15 +513,12 @@ GLFWAPI void glfwGetWindowSize(GLFWwindow* handle, int* width, int* height) GLFWAPI void glfwSetWindowSize(GLFWwindow* handle, int width, int height) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT(); - if (window->monitor) - { - window->videoMode.width = width; - window->videoMode.height = height; - } + window->videoMode.width = width; + window->videoMode.height = height; _glfwPlatformSetWindowSize(window, width, height); } @@ -517,10 +528,22 @@ GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* handle, int maxwidth, int maxheight) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT(); + if (minwidth < 0 || minheight < 0 || + maxwidth < minwidth || maxheight < minheight) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid window size limits"); + return; + } + + window->minwidth = minwidth; + window->minheight = minheight; + window->maxwidth = maxwidth; + window->maxheight = maxheight; + if (window->monitor || !window->resizable) return; @@ -532,19 +555,22 @@ GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* handle, GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* handle, int numer, int denom) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT(); - if (window->monitor || !window->resizable) - return; - - if (!denom) + if (numer <= 0 || denom <= 0) { - _glfwInputError(GLFW_INVALID_VALUE, "Denominator cannot be zero"); + _glfwInputError(GLFW_INVALID_VALUE, "Invalid window aspect ratio"); return; } + window->numer = numer; + window->denom = denom; + + if (window->monitor || !window->resizable) + return; + _glfwPlatformSetWindowAspectRatio(window, numer, denom); } @@ -567,7 +593,7 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* handle, int* right, int* bottom) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); if (left) *left = 0; @@ -585,7 +611,7 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* handle, GLFWAPI void glfwIconifyWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT(); _glfwPlatformIconifyWindow(window); @@ -594,16 +620,23 @@ GLFWAPI void glfwIconifyWindow(GLFWwindow* handle) GLFWAPI void glfwRestoreWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT(); _glfwPlatformRestoreWindow(window); } +GLFWAPI void glfwMaximizeWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT(); + _glfwPlatformMaximizeWindow(window); +} + GLFWAPI void glfwShowWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT(); @@ -611,12 +644,13 @@ GLFWAPI void glfwShowWindow(GLFWwindow* handle) return; _glfwPlatformShowWindow(window); + _glfwPlatformFocusWindow(window); } GLFWAPI void glfwHideWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT(); @@ -626,10 +660,20 @@ GLFWAPI void glfwHideWindow(GLFWwindow* handle) _glfwPlatformHideWindow(window); } +GLFWAPI void glfwFocusWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + _glfwPlatformFocusWindow(window); +} + GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(0); @@ -641,6 +685,8 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) return _glfwPlatformWindowIconified(window); case GLFW_VISIBLE: return _glfwPlatformWindowVisible(window); + case GLFW_MAXIMIZED: + return _glfwPlatformWindowMaximized(window); case GLFW_RESIZABLE: return window->resizable; case GLFW_DECORATED: @@ -648,7 +694,9 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) case GLFW_FLOATING: return window->floating; case GLFW_CLIENT_API: - return window->context.api; + return window->context.client; + case GLFW_CONTEXT_CREATION_API: + return window->context.source; case GLFW_CONTEXT_VERSION_MAJOR: return window->context.major; case GLFW_CONTEXT_VERSION_MINOR: @@ -669,23 +717,44 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) return window->context.noerror; } - _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute"); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute %i", attrib); return 0; } GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); return (GLFWmonitor*) window->monitor; } +GLFWAPI void glfwSetWindowMonitor(GLFWwindow* wh, + GLFWmonitor* mh, + int xpos, int ypos, + int width, int height, + int refreshRate) +{ + _GLFWwindow* window = (_GLFWwindow*) wh; + _GLFWmonitor* monitor = (_GLFWmonitor*) mh; + assert(window); + + _GLFW_REQUIRE_INIT(); + + window->videoMode.width = width; + window->videoMode.height = height; + window->videoMode.refreshRate = refreshRate; + + _glfwPlatformSetWindowMonitor(window, monitor, + xpos, ypos, width, height, + refreshRate); +} + GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* handle, void* pointer) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT(); window->userPointer = pointer; @@ -694,7 +763,7 @@ GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* handle, void* pointer) GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); return window->userPointer; @@ -704,7 +773,7 @@ GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* handle, GLFWwindowposfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.pos, cbfun); @@ -715,7 +784,7 @@ GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* handle, GLFWwindowsizefun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.size, cbfun); @@ -726,7 +795,7 @@ GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* handle, GLFWwindowclosefun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.close, cbfun); @@ -737,7 +806,7 @@ GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* handle, GLFWwindowrefreshfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.refresh, cbfun); @@ -748,7 +817,7 @@ GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* handle, GLFWwindowfocusfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.focus, cbfun); @@ -759,7 +828,7 @@ GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* handle, GLFWwindowiconifyfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.iconify, cbfun); @@ -770,7 +839,7 @@ GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* handle GLFWframebuffersizefun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window); + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.fbsize, cbfun); @@ -793,6 +862,19 @@ GLFWAPI void glfwWaitEvents(void) _glfwPlatformWaitEvents(); } +GLFWAPI void glfwWaitEventsTimeout(double timeout) +{ + _GLFW_REQUIRE_INIT(); + + if (timeout != timeout || timeout < 0.0 || timeout > DBL_MAX) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid time %f", timeout); + return; + } + + _glfwPlatformWaitEventsTimeout(timeout); +} + GLFWAPI void glfwPostEmptyEvent(void) { _GLFW_REQUIRE_INIT(); diff --git a/src/wl_init.c b/src/wl_init.c index 90dc507be..29509ae91 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -84,12 +84,7 @@ static void pointerHandleMotion(void* data, return; if (window->cursorMode == GLFW_CURSOR_DISABLED) - { - /* TODO */ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: GLFW_CURSOR_DISABLED not supported"); return; - } else { window->wl.cursorPosX = wl_fixed_to_double(sx); @@ -413,6 +408,20 @@ static void registryHandleGlobal(void* data, wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL); } } + else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) + { + _glfw.wl.relativePointerManager = + wl_registry_bind(registry, name, + &zwp_relative_pointer_manager_v1_interface, + 1); + } + else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) + { + _glfw.wl.pointerConstraints = + wl_registry_bind(registry, name, + &zwp_pointer_constraints_v1_interface, + 1); + } } static void registryHandleGlobalRemove(void *data, diff --git a/src/wl_monitor.c b/src/wl_monitor.c index 7c58c8f84..3b2b95fed 100644 --- a/src/wl_monitor.c +++ b/src/wl_monitor.c @@ -244,13 +244,15 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) { // TODO - fprintf(stderr, "_glfwPlatformGetGammaRamp not implemented yet\n"); + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Gamma ramp getting not supported yet"); } void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) { // TODO - fprintf(stderr, "_glfwPlatformSetGammaRamp not implemented yet\n"); + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Gamma ramp setting not supported yet"); } diff --git a/src/wl_platform.h b/src/wl_platform.h index 56ce16311..221108ed0 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -49,12 +49,10 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR #include "posix_time.h" #include "linux_joystick.h" #include "xkb_unicode.h" +#include "egl_context.h" -#if defined(_GLFW_EGL) - #include "egl_context.h" -#else - #error "The Wayland backend depends on EGL platform support" -#endif +#include "wayland-relative-pointer-unstable-v1-client-protocol.h" +#include "wayland-pointer-constraints-unstable-v1-client-protocol.h" #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) #define _glfw_dlclose(handle) dlclose(handle) @@ -68,6 +66,9 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR #define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorWayland wl #define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorWayland wl +#define _GLFW_PLATFORM_CONTEXT_STATE +#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE + // Wayland-specific video mode data // @@ -80,6 +81,7 @@ typedef struct _GLFWwindowWayland { int width, height; GLFWbool visible; + GLFWbool maximized; struct wl_surface* surface; struct wl_egl_window* native; struct wl_shell_surface* shell_surface; @@ -88,6 +90,8 @@ typedef struct _GLFWwindowWayland _GLFWcursor* currentCursor; double cursorPosX, cursorPosY; + char* title; + // We need to track the monitors the window spans on to calculate the // optimal scaling factor. int scale; @@ -95,6 +99,10 @@ typedef struct _GLFWwindowWayland int monitorsCount; int monitorsSize; + struct { + struct zwp_relative_pointer_v1* relativePointer; + struct zwp_locked_pointer_v1* lockedPointer; + } pointerLock; } _GLFWwindowWayland; @@ -110,6 +118,8 @@ typedef struct _GLFWlibraryWayland struct wl_seat* seat; struct wl_pointer* pointer; struct wl_keyboard* keyboard; + struct zwp_relative_pointer_manager_v1* relativePointerManager; + struct zwp_pointer_constraints_v1* pointerConstraints; int wl_compositor_version; diff --git a/src/wl_window.c b/src/wl_window.c index 75aa912e4..58a721fd9 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -55,7 +55,32 @@ static void handleConfigure(void* data, int32_t height) { _GLFWwindow* window = data; - _glfwInputFramebufferSize(window, width, height); + float aspectRatio; + float targetRatio; + + if (!window->monitor) + { + if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE) + { + aspectRatio = (float)width / (float)height; + targetRatio = (float)window->numer / (float)window->denom; + if (aspectRatio < targetRatio) + height = width / targetRatio; + else if (aspectRatio > targetRatio) + width = height * targetRatio; + } + + if (window->minwidth != GLFW_DONT_CARE && width < window->minwidth) + width = window->minwidth; + else if (window->maxwidth != GLFW_DONT_CARE && width > window->maxwidth) + width = window->maxwidth; + + if (window->minheight != GLFW_DONT_CARE && height < window->minheight) + height = window->minheight; + else if (window->maxheight != GLFW_DONT_CARE && height > window->maxheight) + height = window->maxheight; + } + _glfwInputWindowSize(window, width, height); _glfwPlatformSetWindowSize(window, width, height); _glfwInputWindowDamage(window); @@ -80,8 +105,7 @@ static void checkScaleChange(_GLFWwindow* window) int monitorScale; // Check if we will be able to set the buffer scale or not. - if (_glfw.wl.wl_compositor_version < - WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION) + if (_glfw.wl.wl_compositor_version < 3) return; // Get the scale factor from the highest scale monitor. @@ -150,6 +174,21 @@ static const struct wl_surface_listener surfaceListener = { handleLeave }; +// Makes the surface considered as XRGB instead of ARGB. +static void setOpaqueRegion(_GLFWwindow* window) +{ + struct wl_region* region; + + region = wl_compositor_create_region(_glfw.wl.compositor); + if (!region) + return; + + wl_region_add(region, 0, 0, window->wl.width, window->wl.height); + wl_surface_set_opaque_region(window->wl.surface, region); + wl_surface_commit(window->wl.surface); + wl_region_destroy(region); +} + static GLFWbool createSurface(_GLFWwindow* window, const _GLFWwndconfig* wndconfig) { @@ -169,6 +208,18 @@ static GLFWbool createSurface(_GLFWwindow* window, if (!window->wl.native) return GLFW_FALSE; + window->wl.width = wndconfig->width; + window->wl.height = wndconfig->height; + window->wl.scale = 1; + + // TODO: make this optional once issue #197 is fixed. + setOpaqueRegion(window); + + return GLFW_TRUE; +} + +static GLFWbool createShellSurface(_GLFWwindow* window) +{ window->wl.shell_surface = wl_shell_get_shell_surface(_glfw.wl.shell, window->wl.surface); if (!window->wl.shell_surface) @@ -178,9 +229,25 @@ static GLFWbool createSurface(_GLFWwindow* window, &shellSurfaceListener, window); - window->wl.width = wndconfig->width; - window->wl.height = wndconfig->height; - window->wl.scale = 1; + if (window->wl.title) + wl_shell_surface_set_title(window->wl.shell_surface, window->wl.title); + + if (window->monitor) + { + wl_shell_surface_set_fullscreen( + window->wl.shell_surface, + WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, + 0, + window->monitor->wl.output); + } + else if (window->wl.maximized) + { + wl_shell_surface_set_maximized(window->wl.shell_surface, NULL); + } + else + { + wl_shell_surface_set_toplevel(window->wl.shell_surface); + } return GLFW_TRUE; } @@ -324,23 +391,26 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!createSurface(window, wndconfig)) return GLFW_FALSE; - if (ctxconfig->api != GLFW_NO_API) + if (ctxconfig->client != GLFW_NO_API) { if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; } - if (wndconfig->monitor) + if (wndconfig->title) + window->wl.title = strdup(wndconfig->title); + + if (wndconfig->visible) { - wl_shell_surface_set_fullscreen( - window->wl.shell_surface, - WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, - 0, - wndconfig->monitor->wl.output); + if (!createShellSurface(window)) + return GLFW_FALSE; + + window->wl.visible = GLFW_TRUE; } else { - wl_shell_surface_set_toplevel(window->wl.shell_surface); + window->wl.shell_surface = NULL; + window->wl.visible = GLFW_FALSE; } window->wl.currentCursor = NULL; @@ -365,7 +435,8 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) _glfwInputWindowFocus(window, GLFW_FALSE); } - _glfwDestroyContextEGL(window); + if (window->context.client != GLFW_NO_API) + window->context.destroyContext(window); if (window->wl.native) wl_egl_window_destroy(window->wl.native); @@ -376,12 +447,24 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) if (window->wl.surface) wl_surface_destroy(window->wl.surface); + free(window->wl.title); free(window->wl.monitors); } void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) { - wl_shell_surface_set_title(window->wl.shell_surface, title); + if (window->wl.title) + free(window->wl.title); + window->wl.title = strdup(title); + if (window->wl.shell_surface) + wl_shell_surface_set_title(window->wl.shell_surface, title); +} + +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, + int count, const GLFWimage* images) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Setting window icon not supported"); } void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) @@ -416,6 +499,7 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) window->wl.width = width; window->wl.height = height; wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0); + setOpaqueRegion(window); _glfwInputFramebufferSize(window, scaledWidth, scaledHeight); } @@ -423,14 +507,14 @@ void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight) { - // TODO - fprintf(stderr, "_glfwPlatformSetWindowSizeLimits not implemented yet\n"); + // TODO: find out how to trigger a resize. + // The actual limits are checked in the wl_shell_surface::configure handler. } void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) { - // TODO - fprintf(stderr, "_glfwPlatformSetWindowAspectRatio not implemented yet\n"); + // TODO: find out how to trigger a resize. + // The actual limits are checked in the wl_shell_surface::configure handler. } void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) @@ -444,55 +528,108 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, int* left, int* top, int* right, int* bottom) { - // TODO - fprintf(stderr, "_glfwPlatformGetWindowFrameSize not implemented yet\n"); + // TODO: will need a proper implementation once decorations are + // implemented, but for now just leave everything as 0. } void _glfwPlatformIconifyWindow(_GLFWwindow* window) { - // TODO - fprintf(stderr, "_glfwPlatformIconifyWindow not implemented yet\n"); + // TODO: move to xdg_shell instead of wl_shell. + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Iconify window not supported"); } void _glfwPlatformRestoreWindow(_GLFWwindow* window) { - // TODO - fprintf(stderr, "_glfwPlatformRestoreWindow not implemented yet\n"); + // TODO: also do the same for iconified. + if (window->monitor || window->wl.maximized) + { + if (window->wl.shell_surface) + wl_shell_surface_set_toplevel(window->wl.shell_surface); + + window->wl.maximized = GLFW_FALSE; + } +} + +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ + if (!window->monitor && !window->wl.maximized) + { + if (window->wl.shell_surface) + { + // Let the compositor select the best output. + wl_shell_surface_set_maximized(window->wl.shell_surface, NULL); + } + window->wl.maximized = GLFW_TRUE; + } } void _glfwPlatformShowWindow(_GLFWwindow* window) { - wl_shell_surface_set_toplevel(window->wl.shell_surface); -} - -void _glfwPlatformUnhideWindow(_GLFWwindow* window) -{ - // TODO - fprintf(stderr, "_glfwPlatformUnhideWindow not implemented yet\n"); + if (!window->monitor) + { + if (!window->wl.shell_surface) + createShellSurface(window); + window->wl.visible = GLFW_TRUE; + } } void _glfwPlatformHideWindow(_GLFWwindow* window) { - wl_surface_attach(window->wl.surface, NULL, 0, 0); - wl_surface_commit(window->wl.surface); + if (!window->monitor) + { + if (window->wl.shell_surface) + wl_shell_surface_destroy(window->wl.shell_surface); + window->wl.visible = GLFW_FALSE; + } +} + +void _glfwPlatformFocusWindow(_GLFWwindow* window) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Focusing a window requires user interaction"); +} + +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) +{ + if (monitor) + { + wl_shell_surface_set_fullscreen( + window->wl.shell_surface, + WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, + refreshRate * 1000, // Convert Hz to mHz. + monitor->wl.output); + } + else + { + wl_shell_surface_set_toplevel(window->wl.shell_surface); + } + _glfwInputWindowMonitorChange(window, monitor); } int _glfwPlatformWindowFocused(_GLFWwindow* window) { - // TODO - return GLFW_FALSE; + return _glfw.wl.keyboardFocus == window; } int _glfwPlatformWindowIconified(_GLFWwindow* window) { - // TODO + // TODO: move to xdg_shell, wl_shell doesn't have any iconified concept. return GLFW_FALSE; } int _glfwPlatformWindowVisible(_GLFWwindow* window) { - // TODO - return GLFW_FALSE; + return window->wl.visible; +} + +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + return window->wl.maximized; } void _glfwPlatformPollEvents(void) @@ -505,6 +642,11 @@ void _glfwPlatformWaitEvents(void) handleEvents(-1); } +void _glfwPlatformWaitEventsTimeout(double timeout) +{ + handleEvents((int) (timeout * 1e3)); +} + void _glfwPlatformPostEmptyEvent(void) { wl_display_sync(_glfw.wl.display); @@ -518,11 +660,17 @@ void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) *ypos = window->wl.cursorPosY; } +static GLFWbool isPointerLocked(_GLFWwindow* window); + void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) { - // A Wayland client can not set the cursor position - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Cursor position setting not supported"); + if (isPointerLocked(window)) + { + zwp_locked_pointer_v1_set_cursor_position_hint( + window->wl.pointerLock.lockedPointer, + wl_fixed_from_double(x), wl_fixed_from_double(y)); + wl_surface_commit(window->wl.surface); + } } void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) @@ -550,7 +698,7 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, if (fd < 0) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Creating a buffer file for %d B failed: %m\n", + "Wayland: Creating a buffer file for %d B failed: %m", length); return GLFW_FALSE; } @@ -559,7 +707,7 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, if (data == MAP_FAILED) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Cursor mmap failed: %m\n"); + "Wayland: Cursor mmap failed: %m"); close(fd); return GLFW_FALSE; } @@ -571,10 +719,12 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, unsigned char* target = data; for (i = 0; i < image->width * image->height; i++, source += 4) { - *target++ = source[2]; - *target++ = source[1]; - *target++ = source[0]; - *target++ = source[3]; + unsigned int alpha = source[3]; + + *target++ = (unsigned char) ((source[2] * alpha) / 255); + *target++ = (unsigned char) ((source[1] * alpha) / 255); + *target++ = (unsigned char) ((source[0] * alpha) / 255); + *target++ = (unsigned char) alpha; } cursor->wl.buffer = @@ -620,6 +770,103 @@ void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) wl_buffer_destroy(cursor->wl.buffer); } +static void handleRelativeMotion(void* data, + struct zwp_relative_pointer_v1* pointer, + uint32_t timeHi, + uint32_t timeLo, + wl_fixed_t dx, + wl_fixed_t dy, + wl_fixed_t dxUnaccel, + wl_fixed_t dyUnaccel) +{ + _GLFWwindow* window = data; + + if (window->cursorMode != GLFW_CURSOR_DISABLED) + return; + + _glfwInputCursorMotion(window, + wl_fixed_to_double(dxUnaccel), + wl_fixed_to_double(dyUnaccel)); +} + +static const struct zwp_relative_pointer_v1_listener relativePointerListener = { + handleRelativeMotion +}; + +static void handleLocked(void* data, + struct zwp_locked_pointer_v1* lockedPointer) +{ +} + +static void unlockPointer(_GLFWwindow* window) +{ + struct zwp_relative_pointer_v1* relativePointer = + window->wl.pointerLock.relativePointer; + struct zwp_locked_pointer_v1* lockedPointer = + window->wl.pointerLock.lockedPointer; + + zwp_relative_pointer_v1_destroy(relativePointer); + zwp_locked_pointer_v1_destroy(lockedPointer); + + window->wl.pointerLock.relativePointer = NULL; + window->wl.pointerLock.lockedPointer = NULL; +} + +static void lockPointer(_GLFWwindow* window); + +static void handleUnlocked(void* data, + struct zwp_locked_pointer_v1* lockedPointer) +{ +} + +static const struct zwp_locked_pointer_v1_listener lockedPointerListener = { + handleLocked, + handleUnlocked +}; + +static void lockPointer(_GLFWwindow* window) +{ + struct zwp_relative_pointer_v1* relativePointer; + struct zwp_locked_pointer_v1* lockedPointer; + + if (!_glfw.wl.relativePointerManager) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: no relative pointer manager"); + return; + } + + relativePointer = + zwp_relative_pointer_manager_v1_get_relative_pointer( + _glfw.wl.relativePointerManager, + _glfw.wl.pointer); + zwp_relative_pointer_v1_add_listener(relativePointer, + &relativePointerListener, + window); + + lockedPointer = + zwp_pointer_constraints_v1_lock_pointer( + _glfw.wl.pointerConstraints, + window->wl.surface, + _glfw.wl.pointer, + NULL, + ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); + zwp_locked_pointer_v1_add_listener(lockedPointer, + &lockedPointerListener, + window); + + window->wl.pointerLock.relativePointer = relativePointer; + window->wl.pointerLock.lockedPointer = lockedPointer; + + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, + NULL, 0, 0); +} + +static GLFWbool isPointerLocked(_GLFWwindow* window) +{ + return window->wl.pointerLock.lockedPointer != NULL; +} + void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) { struct wl_buffer* buffer; @@ -637,6 +884,10 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) if (window != _glfw.wl.pointerFocus) return; + // Unlock possible pointer lock if no longer disabled. + if (window->cursorMode != GLFW_CURSOR_DISABLED && isPointerLocked(window)) + unlockPointer(window); + if (window->cursorMode == GLFW_CURSOR_NORMAL) { if (cursor) @@ -680,26 +931,34 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) wl_surface_commit(surface); } } - else /* Cursor is hidden set cursor surface to NULL */ + else if (window->cursorMode == GLFW_CURSOR_DISABLED) { - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, NULL, 0, 0); + if (!isPointerLocked(window)) + lockPointer(window); + } + else if (window->cursorMode == GLFW_CURSOR_HIDDEN) + { + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, + NULL, 0, 0); } } void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) { // TODO - fprintf(stderr, "_glfwPlatformSetClipboardString not implemented yet\n"); + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Clipboard setting not implemented yet"); } const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) { // TODO - fprintf(stderr, "_glfwPlatformGetClipboardString not implemented yet\n"); + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Clipboard getting not implemented yet"); return NULL; } -char** _glfwPlatformGetRequiredInstanceExtensions(int* count) +char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count) { char** extensions; @@ -718,7 +977,7 @@ char** _glfwPlatformGetRequiredInstanceExtensions(int* count) int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, - unsigned int queuefamily) + uint32_t queuefamily) { PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR = (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR) diff --git a/src/x11_init.c b/src/x11_init.c index e17a7170c..112e4547d 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -356,16 +356,13 @@ static Atom getSupportedAtom(Atom* supportedAtoms, unsigned long atomCount, const char* atomName) { - Atom atom = XInternAtom(_glfw.x11.display, atomName, True); - if (atom != None) - { - unsigned long i; + unsigned long i; + const Atom atom = XInternAtom(_glfw.x11.display, atomName, False); - for (i = 0; i < atomCount; i++) - { - if (supportedAtoms[i] == atom) - return atom; - } + for (i = 0; i < atomCount; i++) + { + if (supportedAtoms[i] == atom) + return atom; } return None; @@ -378,13 +375,11 @@ static void detectEWMH(void) Window* windowFromRoot = NULL; Window* windowFromChild = NULL; - // First we need a couple of atoms, which should already be there - Atom supportingWmCheck = - XInternAtom(_glfw.x11.display, "_NET_SUPPORTING_WM_CHECK", True); - Atom wmSupported = - XInternAtom(_glfw.x11.display, "_NET_SUPPORTED", True); - if (supportingWmCheck == None || wmSupported == None) - return; + // First we need a couple of atoms + const Atom supportingWmCheck = + XInternAtom(_glfw.x11.display, "_NET_SUPPORTING_WM_CHECK", False); + const Atom wmSupported = + XInternAtom(_glfw.x11.display, "_NET_SUPPORTED", False); // Then we look for the _NET_SUPPORTING_WM_CHECK property of the root window if (_glfwGetWindowPropertyX11(_glfw.x11.root, @@ -444,24 +439,22 @@ static void detectEWMH(void) getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_ABOVE"); _glfw.x11.NET_WM_STATE_FULLSCREEN = getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_FULLSCREEN"); + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_VERT"); + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_HORZ"); _glfw.x11.NET_WM_FULLSCREEN_MONITORS = getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_FULLSCREEN_MONITORS"); - _glfw.x11.NET_WM_NAME = - getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_NAME"); - _glfw.x11.NET_WM_ICON_NAME = - getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_ICON_NAME"); - _glfw.x11.NET_WM_PID = - getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_PID"); - _glfw.x11.NET_WM_PING = - getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_PING"); + _glfw.x11.NET_WM_WINDOW_TYPE = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE"); + _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE_NORMAL"); _glfw.x11.NET_ACTIVE_WINDOW = getSupportedAtom(supportedAtoms, atomCount, "_NET_ACTIVE_WINDOW"); _glfw.x11.NET_FRAME_EXTENTS = getSupportedAtom(supportedAtoms, atomCount, "_NET_FRAME_EXTENTS"); _glfw.x11.NET_REQUEST_FRAME_EXTENTS = getSupportedAtom(supportedAtoms, atomCount, "_NET_REQUEST_FRAME_EXTENTS"); - _glfw.x11.NET_WM_BYPASS_COMPOSITOR = - getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_BYPASS_COMPOSITOR"); XFree(supportedAtoms); } @@ -470,18 +463,6 @@ static void detectEWMH(void) // static GLFWbool initExtensions(void) { - // Find or create window manager atoms - _glfw.x11.WM_PROTOCOLS = XInternAtom(_glfw.x11.display, - "WM_PROTOCOLS", - False); - _glfw.x11.WM_STATE = XInternAtom(_glfw.x11.display, "WM_STATE", False); - _glfw.x11.WM_DELETE_WINDOW = XInternAtom(_glfw.x11.display, - "WM_DELETE_WINDOW", - False); - _glfw.x11.MOTIF_WM_HINTS = XInternAtom(_glfw.x11.display, - "_MOTIF_WM_HINTS", - False); - #if defined(_GLFW_HAS_XF86VM) // Check for XF86VidMode extension _glfw.x11.vidmode.available = @@ -540,25 +521,6 @@ static GLFWbool initExtensions(void) _glfw.x11.xinerama.available = GLFW_TRUE; } -#if defined(_GLFW_HAS_XINPUT) - if (XQueryExtension(_glfw.x11.display, - "XInputExtension", - &_glfw.x11.xi.majorOpcode, - &_glfw.x11.xi.eventBase, - &_glfw.x11.xi.errorBase)) - { - _glfw.x11.xi.major = 2; - _glfw.x11.xi.minor = 0; - - if (XIQueryVersion(_glfw.x11.display, - &_glfw.x11.xi.major, - &_glfw.x11.xi.minor) != BadRequest) - { - _glfw.x11.xi.available = GLFW_TRUE; - } - } -#endif /*_GLFW_HAS_XINPUT*/ - // Check if Xkb is supported on this display _glfw.x11.xkb.major = 1; _glfw.x11.xkb.minor = 0; @@ -596,7 +558,7 @@ static GLFWbool initExtensions(void) // Detect whether an EWMH-conformant window manager is running detectEWMH(); - // Find or create string format atoms + // String format atoms _glfw.x11.NULL_ = XInternAtom(_glfw.x11.display, "NULL", False); _glfw.x11.UTF8_STRING = XInternAtom(_glfw.x11.display, "UTF8_STRING", False); @@ -604,38 +566,62 @@ static GLFWbool initExtensions(void) XInternAtom(_glfw.x11.display, "COMPOUND_STRING", False); _glfw.x11.ATOM_PAIR = XInternAtom(_glfw.x11.display, "ATOM_PAIR", False); - // Find or create selection property atom + // Custom selection property atom _glfw.x11.GLFW_SELECTION = XInternAtom(_glfw.x11.display, "GLFW_SELECTION", False); - // Find or create standard clipboard atoms + // ICCCM standard clipboard atoms _glfw.x11.TARGETS = XInternAtom(_glfw.x11.display, "TARGETS", False); _glfw.x11.MULTIPLE = XInternAtom(_glfw.x11.display, "MULTIPLE", False); _glfw.x11.CLIPBOARD = XInternAtom(_glfw.x11.display, "CLIPBOARD", False); - // Find or create clipboard manager atoms + // Clipboard manager atoms _glfw.x11.CLIPBOARD_MANAGER = XInternAtom(_glfw.x11.display, "CLIPBOARD_MANAGER", False); _glfw.x11.SAVE_TARGETS = XInternAtom(_glfw.x11.display, "SAVE_TARGETS", False); - // Find Xdnd (drag and drop) atoms, if available - _glfw.x11.XdndAware = XInternAtom(_glfw.x11.display, "XdndAware", True); - _glfw.x11.XdndEnter = XInternAtom(_glfw.x11.display, "XdndEnter", True); - _glfw.x11.XdndPosition = XInternAtom(_glfw.x11.display, "XdndPosition", True); - _glfw.x11.XdndStatus = XInternAtom(_glfw.x11.display, "XdndStatus", True); - _glfw.x11.XdndActionCopy = XInternAtom(_glfw.x11.display, "XdndActionCopy", True); - _glfw.x11.XdndDrop = XInternAtom(_glfw.x11.display, "XdndDrop", True); - _glfw.x11.XdndLeave = XInternAtom(_glfw.x11.display, "XdndLeave", True); - _glfw.x11.XdndFinished = XInternAtom(_glfw.x11.display, "XdndFinished", True); - _glfw.x11.XdndSelection = XInternAtom(_glfw.x11.display, "XdndSelection", True); + // Xdnd (drag and drop) atoms + _glfw.x11.XdndAware = XInternAtom(_glfw.x11.display, "XdndAware", False); + _glfw.x11.XdndEnter = XInternAtom(_glfw.x11.display, "XdndEnter", False); + _glfw.x11.XdndPosition = XInternAtom(_glfw.x11.display, "XdndPosition", False); + _glfw.x11.XdndStatus = XInternAtom(_glfw.x11.display, "XdndStatus", False); + _glfw.x11.XdndActionCopy = XInternAtom(_glfw.x11.display, "XdndActionCopy", False); + _glfw.x11.XdndDrop = XInternAtom(_glfw.x11.display, "XdndDrop", False); + _glfw.x11.XdndLeave = XInternAtom(_glfw.x11.display, "XdndLeave", False); + _glfw.x11.XdndFinished = XInternAtom(_glfw.x11.display, "XdndFinished", False); + _glfw.x11.XdndSelection = XInternAtom(_glfw.x11.display, "XdndSelection", False); + + // ICCCM, EWMH and Motif window property atoms + // These can be set safely even without WM support + // The EWMH atoms that require WM support are handled in detectEWMH + _glfw.x11.WM_PROTOCOLS = + XInternAtom(_glfw.x11.display, "WM_PROTOCOLS", False); + _glfw.x11.WM_STATE = + XInternAtom(_glfw.x11.display, "WM_STATE", False); + _glfw.x11.WM_DELETE_WINDOW = + XInternAtom(_glfw.x11.display, "WM_DELETE_WINDOW", False); + _glfw.x11.NET_WM_ICON = + XInternAtom(_glfw.x11.display, "_NET_WM_ICON", False); + _glfw.x11.NET_WM_PING = + XInternAtom(_glfw.x11.display, "_NET_WM_PING", False); + _glfw.x11.NET_WM_PID = + XInternAtom(_glfw.x11.display, "_NET_WM_PID", False); + _glfw.x11.NET_WM_NAME = + XInternAtom(_glfw.x11.display, "_NET_WM_NAME", False); + _glfw.x11.NET_WM_ICON_NAME = + XInternAtom(_glfw.x11.display, "_NET_WM_ICON_NAME", False); + _glfw.x11.NET_WM_BYPASS_COMPOSITOR = + XInternAtom(_glfw.x11.display, "_NET_WM_BYPASS_COMPOSITOR", False); + _glfw.x11.MOTIF_WM_HINTS = + XInternAtom(_glfw.x11.display, "_MOTIF_WM_HINTS", False); return GLFW_TRUE; } // Create a blank cursor for hidden and disabled cursor modes // -static Cursor createNULLCursor(void) +static Cursor createHiddenCursor(void) { unsigned char pixels[16 * 16 * 4]; GLFWimage image = { 16, 16, pixels }; @@ -705,10 +691,12 @@ Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot) for (i = 0; i < image->width * image->height; i++, target++, source += 4) { - *target = (source[3] << 24) | - (source[0] << 16) | - (source[1] << 8) | - source[2]; + unsigned int alpha = source[3]; + + *target = (alpha << 24) | + ((unsigned char) ((source[0] * alpha) / 255) << 16) | + ((unsigned char) ((source[1] * alpha) / 255) << 8) | + ((unsigned char) ((source[2] * alpha) / 255) << 0); } cursor = XcursorImageLoadCursor(_glfw.x11.display, native); @@ -758,7 +746,7 @@ int _glfwPlatformInit(void) if (!initExtensions()) return GLFW_FALSE; - _glfw.x11.cursor = createNULLCursor(); + _glfw.x11.cursor = createHiddenCursor(); if (XSupportsLocale()) { @@ -778,17 +766,13 @@ int _glfwPlatformInit(void) if (!_glfwInitThreadLocalStoragePOSIX()) return GLFW_FALSE; -#if defined(_GLFW_GLX) if (!_glfwInitGLX()) return GLFW_FALSE; -#elif defined(_GLFW_EGL) - if (!_glfwInitEGL()) - return GLFW_FALSE; -#endif if (!_glfwInitJoysticksLinux()) return GLFW_FALSE; + _glfwInitEGL(); _glfwInitTimerPOSIX(); return GLFW_TRUE; @@ -816,9 +800,7 @@ void _glfwPlatformTerminate(void) _glfw.x11.im = NULL; } -#if defined(_GLFW_EGL) _glfwTerminateEGL(); -#endif if (_glfw.x11.display) { @@ -828,9 +810,7 @@ void _glfwPlatformTerminate(void) // NOTE: This needs to be done after XCloseDisplay, as libGL registers // cleanup callbacks that get called by it -#if defined(_GLFW_GLX) _glfwTerminateGLX(); -#endif _glfwTerminateJoysticksLinux(); _glfwTerminateThreadLocalStoragePOSIX(); @@ -838,12 +818,7 @@ void _glfwPlatformTerminate(void) const char* _glfwPlatformGetVersionString(void) { - return _GLFW_VERSION_NUMBER " X11" -#if defined(_GLFW_GLX) - " GLX" -#elif defined(_GLFW_EGL) - " EGL" -#endif + return _GLFW_VERSION_NUMBER " X11 GLX EGL" #if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) " clock_gettime" #else @@ -852,9 +827,6 @@ const char* _glfwPlatformGetVersionString(void) #if defined(__linux__) " /dev/js" #endif -#if defined(_GLFW_HAS_XINPUT) - " XI" -#endif #if defined(_GLFW_HAS_XF86VM) " Xf86vm" #endif diff --git a/src/x11_platform.h b/src/x11_platform.h index 65aba0ed8..9c76d1898 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -47,11 +47,6 @@ // The Xinerama extension provides legacy monitor indices #include -#if defined(_GLFW_HAS_XINPUT) - // The XInput2 extension provides improved input events - #include -#endif - #if defined(_GLFW_HAS_XF86VM) // The Xf86VidMode extension provides fallback gamma control #include @@ -92,21 +87,16 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(Vk #include "posix_time.h" #include "linux_joystick.h" #include "xkb_unicode.h" - -#if defined(_GLFW_GLX) - #include "glx_context.h" -#elif defined(_GLFW_EGL) - #define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->x11.handle) - #define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.x11.display) - #include "egl_context.h" -#else - #error "No supported context creation API selected" -#endif +#include "glx_context.h" +#include "egl_context.h" #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) #define _glfw_dlclose(handle) dlclose(handle) #define _glfw_dlsym(handle, name) dlsym(handle, name) +#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->x11.handle) +#define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.x11.display) + #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowX11 x11 #define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11 #define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorX11 x11 @@ -121,6 +111,8 @@ typedef struct _GLFWwindowX11 Window handle; XIC ic; + GLFWbool overrideRedirect; + // Cached position and size used to filter out duplicate events int width, height; int xpos, ypos; @@ -170,11 +162,16 @@ typedef struct _GLFWlibraryX11 Atom WM_DELETE_WINDOW; Atom NET_WM_NAME; Atom NET_WM_ICON_NAME; + Atom NET_WM_ICON; Atom NET_WM_PID; Atom NET_WM_PING; + Atom NET_WM_WINDOW_TYPE; + Atom NET_WM_WINDOW_TYPE_NORMAL; Atom NET_WM_STATE; Atom NET_WM_STATE_ABOVE; Atom NET_WM_STATE_FULLSCREEN; + Atom NET_WM_STATE_MAXIMIZED_VERT; + Atom NET_WM_STATE_MAXIMIZED_HORZ; Atom NET_WM_BYPASS_COMPOSITOR; Atom NET_WM_FULLSCREEN_MONITORS; Atom NET_ACTIVE_WINDOW; @@ -248,17 +245,6 @@ typedef struct _GLFWlibraryX11 XGETXCBCONNECTION_T XGetXCBConnection; } x11xcb; -#if defined(_GLFW_HAS_XINPUT) - struct { - GLFWbool available; - int majorOpcode; - int eventBase; - int errorBase; - int major; - int minor; - } xi; -#endif /*_GLFW_HAS_XINPUT*/ - #if defined(_GLFW_HAS_XF86VM) struct { GLFWbool available; diff --git a/src/x11_window.c b/src/x11_window.c index 8ffd81c57..19b9d2469 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -54,11 +54,19 @@ void selectDisplayConnection(struct timeval* timeout) { fd_set fds; - int result; + int result, count; const int fd = ConnectionNumber(_glfw.x11.display); + count = fd + 1; + FD_ZERO(&fds); FD_SET(fd, &fds); +#if defined(__linux__) + FD_SET(_glfw.linux_js.inotify, &fds); + + if (fd < _glfw.linux_js.inotify) + count = _glfw.linux_js.inotify + 1; +#endif // NOTE: We use select instead of an X function like XNextEvent, as the // wait inside those are guarded by the mutex protecting the display @@ -68,7 +76,7 @@ void selectDisplayConnection(struct timeval* timeout) // TODO: Update timeout value manually do { - result = select(fd + 1, &fds, NULL, NULL, timeout); + result = select(count, &fds, NULL, NULL, timeout); } while (result == -1 && errno == EINTR && timeout == NULL); } @@ -199,6 +207,140 @@ static void sendEventToWM(_GLFWwindow* window, Atom type, &event); } +// Updates the normal hints according to the window settings +// +static void updateNormalHints(_GLFWwindow* window, int width, int height) +{ + XSizeHints* hints = XAllocSizeHints(); + + if (!window->monitor) + { + if (window->resizable) + { + if (window->minwidth != GLFW_DONT_CARE && + window->minheight != GLFW_DONT_CARE && + window->maxwidth != GLFW_DONT_CARE && + window->maxheight != GLFW_DONT_CARE) + { + hints->flags |= (PMinSize | PMaxSize); + hints->min_width = window->minwidth; + hints->min_height = window->minheight; + hints->max_width = window->maxwidth; + hints->max_height = window->maxheight; + } + + if (window->numer != GLFW_DONT_CARE && + window->denom != GLFW_DONT_CARE) + { + hints->flags |= PAspect; + hints->min_aspect.x = hints->max_aspect.x = window->numer; + hints->min_aspect.y = hints->max_aspect.y = window->denom; + } + } + else + { + hints->flags |= (PMinSize | PMaxSize); + hints->min_width = hints->max_width = width; + hints->min_height = hints->max_height = height; + } + } + + XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints); + XFree(hints); +} + +// Updates the full screen status of the window +// +static void updateWindowMode(_GLFWwindow* window) +{ + if (window->monitor) + { + if (_glfw.x11.xinerama.available && + _glfw.x11.NET_WM_FULLSCREEN_MONITORS) + { + sendEventToWM(window, + _glfw.x11.NET_WM_FULLSCREEN_MONITORS, + window->monitor->x11.index, + window->monitor->x11.index, + window->monitor->x11.index, + window->monitor->x11.index, + 0); + } + + if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN) + { + sendEventToWM(window, + _glfw.x11.NET_WM_STATE, + _NET_WM_STATE_ADD, + _glfw.x11.NET_WM_STATE_FULLSCREEN, + 0, 1, 0); + } + else + { + // This is the butcher's way of removing window decorations + // Setting the override-redirect attribute on a window makes the + // window manager ignore the window completely (ICCCM, section 4) + // The good thing is that this makes undecorated full screen windows + // easy to do; the bad thing is that we have to do everything + // manually and some things (like iconify/restore) won't work at + // all, as those are tasks usually performed by the window manager + + XSetWindowAttributes attributes; + attributes.override_redirect = True; + XChangeWindowAttributes(_glfw.x11.display, + window->x11.handle, + CWOverrideRedirect, + &attributes); + + window->x11.overrideRedirect = GLFW_TRUE; + } + + // Enable compositor bypass + { + const unsigned long value = 1; + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32, + PropModeReplace, (unsigned char*) &value, 1); + } + } + else + { + if (_glfw.x11.xinerama.available && + _glfw.x11.NET_WM_FULLSCREEN_MONITORS) + { + XDeleteProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_FULLSCREEN_MONITORS); + } + + if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN) + { + sendEventToWM(window, + _glfw.x11.NET_WM_STATE, + _NET_WM_STATE_REMOVE, + _glfw.x11.NET_WM_STATE_FULLSCREEN, + 0, 1, 0); + } + else + { + XSetWindowAttributes attributes; + attributes.override_redirect = False; + XChangeWindowAttributes(_glfw.x11.display, + window->x11.handle, + CWOverrideRedirect, + &attributes); + + window->x11.overrideRedirect = GLFW_FALSE; + } + + // Disable compositor bypass + { + XDeleteProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_BYPASS_COMPOSITOR); + } + } +} + // Splits and translates a text/uri-list into separate file paths // NOTE: This function destroys the provided string // @@ -300,88 +442,70 @@ static GLFWbool createWindow(_GLFWwindow* window, (XPointer) window); } - if (wndconfig->monitor) + if (!wndconfig->decorated) { - if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_FULLSCREEN) + struct { - // This is the butcher's way of removing window decorations - // Setting the override-redirect attribute on a window makes the - // window manager ignore the window completely (ICCCM, section 4) - // The good thing is that this makes undecorated full screen windows - // easy to do; the bad thing is that we have to do everything - // manually and some things (like iconify/restore) won't work at - // all, as those are tasks usually performed by the window manager + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long input_mode; + unsigned long status; + } hints; - XSetWindowAttributes attributes; - attributes.override_redirect = True; - XChangeWindowAttributes(_glfw.x11.display, - window->x11.handle, - CWOverrideRedirect, - &attributes); - } + hints.flags = 2; // Set decorations + hints.decorations = 0; // No decorations + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.MOTIF_WM_HINTS, + _glfw.x11.MOTIF_WM_HINTS, 32, + PropModeReplace, + (unsigned char*) &hints, + sizeof(hints) / sizeof(long)); } - else + + if (_glfw.x11.NET_WM_STATE && !window->monitor) { - if (!wndconfig->decorated) - { - struct - { - unsigned long flags; - unsigned long functions; - unsigned long decorations; - long input_mode; - unsigned long status; - } hints; - - hints.flags = 2; // Set decorations - hints.decorations = 0; // No decorations - - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.MOTIF_WM_HINTS, - _glfw.x11.MOTIF_WM_HINTS, 32, - PropModeReplace, - (unsigned char*) &hints, - sizeof(hints) / sizeof(long)); - } + Atom states[3]; + int count = 0; if (wndconfig->floating) { - if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_ABOVE) + if (_glfw.x11.NET_WM_STATE_ABOVE) + states[count++] = _glfw.x11.NET_WM_STATE_ABOVE; + } + + if (wndconfig->maximized) + { + if (_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) { - sendEventToWM(window, - _glfw.x11.NET_WM_STATE, - _NET_WM_STATE_ADD, - _glfw.x11.NET_WM_STATE_ABOVE, - 0, 1, 0); + states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT; + states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ; } } - } + if (count) + { + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_STATE, XA_ATOM, 32, + PropModeReplace, (unsigned char*) &states, count); + } + } // Declare the WM protocols supported by GLFW { - int count = 0; - Atom protocols[2]; - - // The WM_DELETE_WINDOW ICCCM protocol - // Basic window close notification protocol - if (_glfw.x11.WM_DELETE_WINDOW) - protocols[count++] = _glfw.x11.WM_DELETE_WINDOW; - - // The _NET_WM_PING EWMH protocol - // Tells the WM to ping the GLFW window and flag the application as - // unresponsive if the WM doesn't get a reply within a few seconds - if (_glfw.x11.NET_WM_PING) - protocols[count++] = _glfw.x11.NET_WM_PING; - - if (count > 0) + Atom protocols[] = { - XSetWMProtocols(_glfw.x11.display, window->x11.handle, - protocols, count); - } + _glfw.x11.WM_DELETE_WINDOW, + _glfw.x11.NET_WM_PING + }; + + XSetWMProtocols(_glfw.x11.display, window->x11.handle, + protocols, sizeof(protocols) / sizeof(Atom)); } - if (_glfw.x11.NET_WM_PID) + // Declare our PID { const pid_t pid = getpid(); @@ -391,6 +515,14 @@ static GLFWbool createWindow(_GLFWwindow* window, (unsigned char*) &pid, 1); } + if (_glfw.x11.NET_WM_WINDOW_TYPE && _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL) + { + Atom type = _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL; + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32, + PropModeReplace, (unsigned char*) &type, 1); + } + // Set ICCCM WM_HINTS property { XWMHints* hints = XAllocWMHints(); @@ -408,35 +540,7 @@ static GLFWbool createWindow(_GLFWwindow* window, XFree(hints); } - // Set ICCCM WM_NORMAL_HINTS property (even if no parts are set) - { - XSizeHints* hints = XAllocSizeHints(); - hints->flags = 0; - - if (wndconfig->monitor) - { - hints->flags |= PPosition; - _glfwPlatformGetMonitorPos(wndconfig->monitor, &hints->x, &hints->y); - } - else - { - // HACK: Explicitly setting PPosition to any value causes some WMs, - // notably Compiz and Metacity, to honor the position of - // unmapped windows set by XMoveWindow - hints->flags |= PPosition; - hints->x = hints->y = 0; - } - - if (!wndconfig->resizable) - { - hints->flags |= (PMinSize | PMaxSize); - hints->min_width = hints->max_width = wndconfig->width; - hints->min_height = hints->max_height = wndconfig->height; - } - - XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints); - XFree(hints); - } + updateNormalHints(window, wndconfig->width, wndconfig->height); // Set ICCCM WM_CLASS property // HACK: Until a mechanism for specifying the application name is added, the @@ -451,23 +555,6 @@ static GLFWbool createWindow(_GLFWwindow* window, XFree(hint); } -#if defined(_GLFW_HAS_XINPUT) - if (_glfw.x11.xi.available) - { - // Select for XInput2 events - - XIEventMask eventmask; - unsigned char mask[] = { 0 }; - - eventmask.deviceid = 2; - eventmask.mask_len = sizeof(mask); - eventmask.mask = mask; - XISetMask(mask, XI_Motion); - - XISelectEvents(_glfw.x11.display, window->x11.handle, &eventmask, 1); - } -#endif /*_GLFW_HAS_XINPUT*/ - if (_glfw.x11.XdndAware) { // Announce support for Xdnd (drag and drop) @@ -708,10 +795,12 @@ static void pushSelectionToManager(_GLFWwindow* window) } } -// Enter full screen mode +// Make the specified window and its video mode active on its monitor // -static void enterFullscreenMode(_GLFWwindow* window) +static GLFWbool acquireMonitor(_GLFWwindow* window) { + GLFWbool status; + if (_glfw.x11.saver.count == 0) { // Remember old screen saver settings @@ -726,24 +815,17 @@ static void enterFullscreenMode(_GLFWwindow* window) DefaultExposures); } - _glfw.x11.saver.count++; + if (!window->monitor->window) + _glfw.x11.saver.count++; - _glfwSetVideoModeX11(window->monitor, &window->videoMode); + status = _glfwSetVideoModeX11(window->monitor, &window->videoMode); - if (_glfw.x11.NET_WM_BYPASS_COMPOSITOR) - { - const unsigned long value = 1; - - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32, - PropModeReplace, (unsigned char*) &value, 1); - } - - // Position the window over its monitor + if (window->x11.overrideRedirect) { int xpos, ypos; GLFWvidmode mode; + // Manually position the window over its monitor _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); _glfwPlatformGetVideoMode(window->monitor, &mode); @@ -751,48 +833,18 @@ static void enterFullscreenMode(_GLFWwindow* window) xpos, ypos, mode.width, mode.height); } - if (_glfw.x11.xinerama.available && _glfw.x11.NET_WM_FULLSCREEN_MONITORS) - { - sendEventToWM(window, - _glfw.x11.NET_WM_FULLSCREEN_MONITORS, - window->monitor->x11.index, - window->monitor->x11.index, - window->monitor->x11.index, - window->monitor->x11.index, - 0); - } - - if (_glfw.x11.NET_ACTIVE_WINDOW) - { - // Ask the window manager to raise and focus the GLFW window - // Only focused windows with the _NET_WM_STATE_FULLSCREEN state end up - // on top of all other windows ("Stacking order" in EWMH spec) - sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0); - } - else - { - XRaiseWindow(_glfw.x11.display, window->x11.handle); - XSetInputFocus(_glfw.x11.display, window->x11.handle, - RevertToParent, CurrentTime); - } - - if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN) - { - // Ask the window manager to make the GLFW window a full screen window - // Full screen windows are undecorated and, when focused, are kept - // on top of all other windows - sendEventToWM(window, - _glfw.x11.NET_WM_STATE, - _NET_WM_STATE_ADD, - _glfw.x11.NET_WM_STATE_FULLSCREEN, - 0, 1, 0); - } + _glfwInputMonitorWindowChange(window->monitor, window); + return status; } -// Leave full screen mode +// Remove the window and restore the original video mode // -static void leaveFullscreenMode(_GLFWwindow* window) +static void releaseMonitor(_GLFWwindow* window) { + if (window->monitor->window != window) + return; + + _glfwInputMonitorWindowChange(window->monitor, NULL); _glfwRestoreVideoModeX11(window->monitor); _glfw.x11.saver.count--; @@ -920,7 +972,7 @@ static void processEvent(XEvent *event) while (c - chars < count) _glfwInputChar(window, decodeUTF8(&c), mods, plain); } -#else +#else /*X_HAVE_UTF8_STRING*/ wchar_t buffer[16]; wchar_t* chars = buffer; @@ -944,7 +996,7 @@ static void processEvent(XEvent *event) for (i = 0; i < count; i++) _glfwInputChar(window, chars[i], mods, plain); } -#endif +#endif /*X_HAVE_UTF8_STRING*/ if (chars != buffer) free(chars); @@ -985,12 +1037,13 @@ static void processEvent(XEvent *event) next.xkey.window == event->xkey.window && next.xkey.keycode == keycode) { - // HACK: Repeat events sometimes leak through due to - // some sort of time drift, so add an epsilon - // Toshiyuki Takahashi can press a button 16 times - // per second so it's fairly safe to assume that - // no human is pressing the key 50 times per - // second (value is ms) + // HACK: The time of repeat events sometimes doesn't + // match that of the press event, so add an + // epsilon + // Toshiyuki Takahashi can press a button + // 16 times per second so it's fairly safe to + // assume that no human is pressing the key 50 + // times per second (value is ms) if ((next.xkey.time - event->xkey.time) < 20) { // This is very likely a server-generated key repeat @@ -1141,12 +1194,15 @@ static void processEvent(XEvent *event) if (event->xconfigure.x != window->x11.xpos || event->xconfigure.y != window->x11.ypos) { - _glfwInputWindowPos(window, - event->xconfigure.x, - event->xconfigure.y); + if (window->x11.overrideRedirect || event->xany.send_event) + { + _glfwInputWindowPos(window, + event->xconfigure.x, + event->xconfigure.y); - window->x11.xpos = event->xconfigure.x; - window->x11.ypos = event->xconfigure.y; + window->x11.xpos = event->xconfigure.x; + window->x11.ypos = event->xconfigure.y; + } } return; @@ -1340,14 +1396,14 @@ static void processEvent(XEvent *event) if (state == IconicState) { if (window->monitor) - leaveFullscreenMode(window); + releaseMonitor(window); _glfwInputWindowIconify(window, GLFW_TRUE); } else if (state == NormalState) { if (window->monitor) - enterFullscreenMode(window); + acquireMonitor(window); _glfwInputWindowIconify(window, GLFW_FALSE); } @@ -1370,54 +1426,6 @@ static void processEvent(XEvent *event) case DestroyNotify: return; - -#if defined(_GLFW_HAS_XINPUT) - case GenericEvent: - { - if (event->xcookie.extension == _glfw.x11.xi.majorOpcode && - XGetEventData(_glfw.x11.display, &event->xcookie)) - { - if (event->xcookie.evtype == XI_Motion) - { - XIDeviceEvent* data = (XIDeviceEvent*) event->xcookie.data; - - window = findWindowByHandle(data->event); - if (window) - { - if (data->event_x != window->x11.warpPosX || - data->event_y != window->x11.warpPosY) - { - // The cursor was moved by something other than GLFW - - double x, y; - - if (window->cursorMode == GLFW_CURSOR_DISABLED) - { - if (_glfw.cursorWindow != window) - return; - - x = data->event_x - window->x11.cursorPosX; - y = data->event_y - window->x11.cursorPosY; - } - else - { - x = data->event_x; - y = data->event_y; - } - - _glfwInputCursorMotion(window, x, y); - } - - window->x11.cursorPosX = data->event_x; - window->x11.cursorPosY = data->event_y; - } - } - } - - XFreeEventData(_glfw.x11.display, &event->xcookie); - return; - } -#endif /*_GLFW_HAS_XINPUT*/ } } @@ -1470,40 +1478,48 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, Visual* visual; int depth; - if (ctxconfig->api == GLFW_NO_API) + if (ctxconfig->client == GLFW_NO_API) { visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen); depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen); } else { -#if defined(_GLFW_GLX) - if (!_glfwChooseVisualGLX(ctxconfig, fbconfig, &visual, &depth)) - return GLFW_FALSE; -#elif defined(_GLFW_EGL) - if (!_glfwChooseVisualEGL(ctxconfig, fbconfig, &visual, &depth)) - return GLFW_FALSE; -#endif + if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) + { + if (!_glfwChooseVisualGLX(ctxconfig, fbconfig, &visual, &depth)) + return GLFW_FALSE; + } + else + { + if (!_glfwChooseVisualEGL(ctxconfig, fbconfig, &visual, &depth)) + return GLFW_FALSE; + } } if (!createWindow(window, wndconfig, visual, depth)) return GLFW_FALSE; - if (ctxconfig->api != GLFW_NO_API) + if (ctxconfig->client != GLFW_NO_API) { -#if defined(_GLFW_GLX) - if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig)) - return GLFW_FALSE; -#elif defined(_GLFW_EGL) - if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) - return GLFW_FALSE; -#endif + if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) + { + if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else + { + if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } } - if (wndconfig->monitor) + if (window->monitor) { _glfwPlatformShowWindow(window); - enterFullscreenMode(window); + updateWindowMode(window); + if (!acquireMonitor(window)) + return GLFW_FALSE; } return GLFW_TRUE; @@ -1512,7 +1528,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, void _glfwPlatformDestroyWindow(_GLFWwindow* window) { if (window->monitor) - leaveFullscreenMode(window); + releaseMonitor(window); if (window->x11.ic) { @@ -1520,14 +1536,8 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) window->x11.ic = NULL; } - if (window->context.api != GLFW_NO_API) - { -#if defined(_GLFW_GLX) - _glfwDestroyContextGLX(window); -#elif defined(_GLFW_EGL) - _glfwDestroyContextEGL(window); -#endif - } + if (window->context.client != GLFW_NO_API) + window->context.destroyContext(window); if (window->x11.handle) { @@ -1570,20 +1580,59 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) NULL, NULL, NULL); #endif - if (_glfw.x11.NET_WM_NAME) - { - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_NAME, _glfw.x11.UTF8_STRING, 8, - PropModeReplace, - (unsigned char*) title, strlen(title)); - } + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_NAME, _glfw.x11.UTF8_STRING, 8, + PropModeReplace, + (unsigned char*) title, strlen(title)); - if (_glfw.x11.NET_WM_ICON_NAME) + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_ICON_NAME, _glfw.x11.UTF8_STRING, 8, + PropModeReplace, + (unsigned char*) title, strlen(title)); + + XFlush(_glfw.x11.display); +} + +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, + int count, const GLFWimage* images) +{ + if (count) { - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_ICON_NAME, _glfw.x11.UTF8_STRING, 8, + int i, j, longCount = 0; + + for (i = 0; i < count; i++) + longCount += 2 + images[i].width * images[i].height; + + long* icon = calloc(longCount, sizeof(long)); + long* target = icon; + + for (i = 0; i < count; i++) + { + *target++ = images[i].width; + *target++ = images[i].height; + + for (j = 0; j < images[i].width * images[i].height; j++) + { + *target++ = (images[i].pixels[j * 4 + 0] << 16) | + (images[i].pixels[j * 4 + 1] << 8) | + (images[i].pixels[j * 4 + 2] << 0) | + (images[i].pixels[j * 4 + 3] << 24); + } + } + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_ICON, + XA_CARDINAL, 32, PropModeReplace, - (unsigned char*) title, strlen(title)); + (unsigned char*) icon, + longCount); + + free(icon); + } + else + { + XDeleteProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_ICON); } XFlush(_glfw.x11.display); @@ -1615,6 +1664,24 @@ void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) { + // HACK: Explicitly setting PPosition to any value causes some WMs, notably + // Compiz and Metacity, to honor the position of unmapped windows + if (!_glfwPlatformWindowVisible(window)) + { + long supplied; + XSizeHints* hints = XAllocSizeHints(); + + if (XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied)) + { + hints->flags |= PPosition; + hints->x = hints->y = 0; + + XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints); + } + + XFree(hints); + } + XMoveWindow(_glfw.x11.display, window->x11.handle, xpos, ypos); XFlush(_glfw.x11.display); } @@ -1634,31 +1701,13 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) { if (window->monitor) { - _glfwSetVideoModeX11(window->monitor, &window->videoMode); - - if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_FULLSCREEN) - { - GLFWvidmode mode; - _glfwPlatformGetVideoMode(window->monitor, &mode); - XResizeWindow(_glfw.x11.display, window->x11.handle, - mode.width, mode.height); - } + if (window->monitor->window == window) + acquireMonitor(window); } else { if (!window->resizable) - { - // Update window size restrictions to match new window size - - XSizeHints* hints = XAllocSizeHints(); - - hints->flags |= (PMinSize | PMaxSize); - hints->min_width = hints->max_width = width; - hints->min_height = hints->max_height = height; - - XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints); - XFree(hints); - } + updateNormalHints(window, width, height); XResizeWindow(_glfw.x11.display, window->x11.handle, width, height); } @@ -1670,55 +1719,18 @@ void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight) { - long supplied; - XSizeHints* hints = XAllocSizeHints(); - - if (XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied)) - { - if (minwidth == GLFW_DONT_CARE || minwidth == GLFW_DONT_CARE) - hints->flags &= ~PMinSize; - else - { - hints->flags |= PMinSize; - hints->min_width = minwidth; - hints->min_height = minheight; - } - - if (maxwidth == GLFW_DONT_CARE || maxwidth == GLFW_DONT_CARE) - hints->flags &= ~PMaxSize; - else - { - hints->flags |= PMaxSize; - hints->max_width = maxwidth; - hints->max_height = maxheight; - } - - XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints); - } - - XFree(hints); + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + updateNormalHints(window, width, height); + XFlush(_glfw.x11.display); } void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) { - long supplied; - XSizeHints* hints = XAllocSizeHints(); - - if (XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied)) - { - if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE) - hints->flags &= ~PAspect; - else - { - hints->flags |= PAspect; - hints->min_aspect.x = hints->max_aspect.x = numer; - hints->min_aspect.y = hints->max_aspect.y = denom; - } - - XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints); - } - - XFree(hints); + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + updateNormalHints(window, width, height); + XFlush(_glfw.x11.display); } void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) @@ -1732,13 +1744,16 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, { long* extents = NULL; + if (window->monitor || !window->decorated) + return; + if (_glfw.x11.NET_FRAME_EXTENTS == None) return; if (!_glfwPlatformWindowVisible(window) && _glfw.x11.NET_REQUEST_FRAME_EXTENTS) { - double base; + uint64_t base; XEvent event; // Ensure _NET_FRAME_EXTENTS is set, allowing glfwGetWindowFrameSize to @@ -1746,13 +1761,14 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, sendEventToWM(window, _glfw.x11.NET_REQUEST_FRAME_EXTENTS, 0, 0, 0, 0, 0); + base = _glfwPlatformGetTimerValue(); + // HACK: Poll with timeout for the required reply instead of blocking // This is done because some window managers (at least Unity, // Fluxbox and Xfwm) failed to send the required reply // They have been fixed but broken versions are still in the wild // If you are affected by this and your window manager is NOT // listed above, PLEASE report it to their and our issue trackers - base = _glfwPlatformGetTime(); while (!XCheckIfEvent(_glfw.x11.display, &event, isFrameExtentsEvent, @@ -1761,7 +1777,8 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, double remaining; struct timeval timeout; - remaining = 0.5 + base - _glfwPlatformGetTime(); + remaining = 0.5 - (_glfwPlatformGetTimerValue() - base) / + (double) _glfwPlatformGetTimerFrequency(); if (remaining <= 0.0) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -1796,7 +1813,7 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, void _glfwPlatformIconifyWindow(_GLFWwindow* window) { - if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_FULLSCREEN) + if (window->x11.overrideRedirect) { // Override-redirect windows cannot be iconified or restored, as those // tasks are performed by the window manager @@ -1811,7 +1828,7 @@ void _glfwPlatformIconifyWindow(_GLFWwindow* window) void _glfwPlatformRestoreWindow(_GLFWwindow* window) { - if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_FULLSCREEN) + if (window->x11.overrideRedirect) { // Override-redirect windows cannot be iconified or restored, as those // tasks are performed by the window manager @@ -1820,17 +1837,43 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window) return; } - XMapWindow(_glfw.x11.display, window->x11.handle); + if (_glfwPlatformWindowIconified(window)) + XMapWindow(_glfw.x11.display, window->x11.handle); + else + { + if (_glfw.x11.NET_WM_STATE && + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) + { + sendEventToWM(window, + _glfw.x11.NET_WM_STATE, + _NET_WM_STATE_REMOVE, + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ, + 1, 0); + } + } + XFlush(_glfw.x11.display); } +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ + if (_glfw.x11.NET_WM_STATE && + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) + { + 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); + } +} + void _glfwPlatformShowWindow(_GLFWwindow* window) -{ - XMapRaised(_glfw.x11.display, window->x11.handle); - XFlush(_glfw.x11.display); -} - -void _glfwPlatformUnhideWindow(_GLFWwindow* window) { XMapWindow(_glfw.x11.display, window->x11.handle); XFlush(_glfw.x11.display); @@ -1842,6 +1885,63 @@ void _glfwPlatformHideWindow(_GLFWwindow* window) XFlush(_glfw.x11.display); } +void _glfwPlatformFocusWindow(_GLFWwindow* window) +{ + if (_glfw.x11.NET_ACTIVE_WINDOW) + sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0); + else + { + XRaiseWindow(_glfw.x11.display, window->x11.handle); + XSetInputFocus(_glfw.x11.display, window->x11.handle, + RevertToParent, CurrentTime); + } + + XFlush(_glfw.x11.display); +} + +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) +{ + if (window->monitor == monitor) + { + if (monitor) + { + if (monitor->window == window) + acquireMonitor(window); + } + else + { + XMoveResizeWindow(_glfw.x11.display, window->x11.handle, + xpos, ypos, width, height); + } + + return; + } + + if (window->monitor) + releaseMonitor(window); + + _glfwInputWindowMonitorChange(window, monitor); + updateNormalHints(window, width, height); + updateWindowMode(window); + + if (window->monitor) + { + XMapRaised(_glfw.x11.display, window->x11.handle); + acquireMonitor(window); + } + else + { + XMoveResizeWindow(_glfw.x11.display, window->x11.handle, + xpos, ypos, width, height); + } + + XFlush(_glfw.x11.display); +} + int _glfwPlatformWindowFocused(_GLFWwindow* window) { Window focused; @@ -1863,8 +1963,35 @@ int _glfwPlatformWindowVisible(_GLFWwindow* window) return wa.map_state == IsViewable; } +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + Atom* states; + unsigned long i; + GLFWbool maximized = GLFW_FALSE; + const unsigned long count = + _glfwGetWindowPropertyX11(window->x11.handle, + _glfw.x11.NET_WM_STATE, + XA_ATOM, + (unsigned char**) &states); + + for (i = 0; i < count; i++) + { + if (states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || + states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) + { + maximized = GLFW_TRUE; + break; + } + } + + XFree(states); + return maximized; +} + void _glfwPlatformPollEvents(void) { + _glfwPollJoystickEvents(); + int count = XPending(_glfw.x11.display); while (count--) { @@ -1890,6 +2017,28 @@ void _glfwPlatformWaitEvents(void) _glfwPlatformPollEvents(); } +void _glfwPlatformWaitEventsTimeout(double timeout) +{ + const double deadline = timeout + _glfwPlatformGetTimerValue() / + (double) _glfwPlatformGetTimerFrequency(); + + while (!XPending(_glfw.x11.display)) + { + const double remaining = deadline - _glfwPlatformGetTimerValue() / + (double) _glfwPlatformGetTimerFrequency(); + if (remaining <= 0.0) + return; + + const long seconds = (long) remaining; + const long microseconds = (long) ((remaining - seconds) * 1e6); + struct timeval tv = { seconds, microseconds }; + + selectDisplayConnection(&tv); + } + + _glfwPlatformPollEvents(); +} + void _glfwPlatformPostEmptyEvent(void) { XEvent event; @@ -2117,25 +2266,25 @@ const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) return _glfw.x11.clipboardString; } -char** _glfwPlatformGetRequiredInstanceExtensions(int* count) +char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count) { char** extensions; *count = 0; - if (!_glfw.vk.KHR_xlib_surface) + if (!_glfw.vk.KHR_xcb_surface || !_glfw.x11.x11xcb.handle) { - if (!_glfw.vk.KHR_xcb_surface || !_glfw.x11.x11xcb.handle) + if (!_glfw.vk.KHR_xlib_surface) return NULL; } extensions = calloc(2, sizeof(char*)); extensions[0] = strdup("VK_KHR_surface"); - if (_glfw.vk.KHR_xlib_surface) - extensions[1] = strdup("VK_KHR_xlib_surface"); - else + if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) extensions[1] = strdup("VK_KHR_xcb_surface"); + else + extensions[1] = strdup("VK_KHR_xlib_surface"); *count = 2; return extensions; @@ -2143,29 +2292,12 @@ char** _glfwPlatformGetRequiredInstanceExtensions(int* count) int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, - unsigned int queuefamily) + uint32_t queuefamily) { VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display, _glfw.x11.screen)); - if (_glfw.vk.KHR_xlib_surface) - { - PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR = - (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR) - vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR"); - if (!vkGetPhysicalDeviceXlibPresentationSupportKHR) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "X11: Vulkan instance missing VK_KHR_xlib_surface extension"); - return GLFW_FALSE; - } - - return vkGetPhysicalDeviceXlibPresentationSupportKHR(device, - queuefamily, - _glfw.x11.display, - visualID); - } - else + if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) { PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR vkGetPhysicalDeviceXcbPresentationSupportKHR = (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR) @@ -2191,6 +2323,23 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, connection, visualID); } + else + { + PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR = + (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR) + vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR"); + if (!vkGetPhysicalDeviceXlibPresentationSupportKHR) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "X11: Vulkan instance missing VK_KHR_xlib_surface extension"); + return GLFW_FALSE; + } + + return vkGetPhysicalDeviceXlibPresentationSupportKHR(device, + queuefamily, + _glfw.x11.display, + visualID); + } } VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, @@ -2198,37 +2347,7 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface) { - if (_glfw.vk.KHR_xlib_surface) - { - VkResult err; - VkXlibSurfaceCreateInfoKHR sci; - PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR; - - vkCreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR) - vkGetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR"); - if (!vkCreateXlibSurfaceKHR) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "X11: Vulkan instance missing VK_KHR_xlib_surface extension"); - return VK_ERROR_EXTENSION_NOT_PRESENT; - } - - memset(&sci, 0, sizeof(sci)); - sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; - sci.dpy = _glfw.x11.display; - sci.window = window->x11.handle; - - err = vkCreateXlibSurfaceKHR(instance, &sci, allocator, surface); - if (err) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Failed to create Vulkan X11 surface: %s", - _glfwGetVulkanResultString(err)); - } - - return err; - } - else + if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) { VkResult err; VkXcbSurfaceCreateInfoKHR sci; @@ -2265,6 +2384,36 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, _glfwGetVulkanResultString(err)); } + return err; + } + else + { + VkResult err; + VkXlibSurfaceCreateInfoKHR sci; + PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR; + + vkCreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR) + vkGetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR"); + if (!vkCreateXlibSurfaceKHR) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "X11: Vulkan instance missing VK_KHR_xlib_surface extension"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + memset(&sci, 0, sizeof(sci)); + sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + sci.dpy = _glfw.x11.display; + sci.window = window->x11.handle; + + err = vkCreateXlibSurfaceKHR(instance, &sci, allocator, surface); + if (err) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to create Vulkan X11 surface: %s", + _glfwGetVulkanResultString(err)); + } + return err; } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 88468a9ef..9339ee594 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -30,17 +30,18 @@ add_executable(reopen reopen.c ${GLAD}) add_executable(cursor cursor.c ${GLAD}) add_executable(empty WIN32 MACOSX_BUNDLE empty.c ${TINYCTHREAD} ${GLAD}) +add_executable(icon WIN32 MACOSX_BUNDLE icon.c ${GLAD}) add_executable(sharing WIN32 MACOSX_BUNDLE sharing.c ${GLAD}) add_executable(tearing WIN32 MACOSX_BUNDLE tearing.c ${GETOPT} ${GLAD}) add_executable(threads WIN32 MACOSX_BUNDLE threads.c ${TINYCTHREAD} ${GLAD}) -add_executable(transparent_threads WIN32 MACOSX_BUNDLE transparent_threads.c ${TINYCTHREAD} ${GLAD}) +add_executable(timeout WIN32 MACOSX_BUNDLE timeout.c ${GLAD}) add_executable(title WIN32 MACOSX_BUNDLE title.c ${GLAD}) -add_executable(windows WIN32 MACOSX_BUNDLE windows.c ${GLAD}) +add_executable(windows WIN32 MACOSX_BUNDLE windows.c ${GETOPT} ${GLAD}) target_link_libraries(empty "${CMAKE_THREAD_LIBS_INIT}" "${RT_LIBRARY}") target_link_libraries(threads "${CMAKE_THREAD_LIBS_INIT}" "${RT_LIBRARY}") -set(WINDOWS_BINARIES empty sharing tearing threads transparent_threads title windows) +set(WINDOWS_BINARIES empty icon sharing tearing threads timeout title windows) set(CONSOLE_BINARIES clipboard events msaa gamma glfwinfo iconify joysticks monitors reopen cursor) @@ -65,7 +66,7 @@ if (APPLE) set_target_properties(sharing PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Sharing") set_target_properties(tearing PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Tearing") set_target_properties(threads PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Threads") - set_target_properties(transparent_threads PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Transparent threads") + set_target_properties(timeout PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Timeout") set_target_properties(title PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Title") set_target_properties(windows PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Windows") diff --git a/tests/cursor.c b/tests/cursor.c index cbd5f0ee8..f1bcc42f5 100644 --- a/tests/cursor.c +++ b/tests/cursor.c @@ -195,6 +195,7 @@ int main(void) int i; GLFWwindow* window; GLFWcursor* star_cursors[CURSOR_FRAME_COUNT]; + GLFWcursor* current_frame = NULL; glfwSetErrorCallback(error_callback); @@ -279,11 +280,22 @@ int main(void) if (animate_cursor) { const int i = (int) (glfwGetTime() * 30.0) % CURSOR_FRAME_COUNT; - glfwSetCursor(window, star_cursors[i]); + if (current_frame != star_cursors[i]) + { + glfwSetCursor(window, star_cursors[i]); + current_frame = star_cursors[i]; + } } + else + current_frame = NULL; if (wait_events) - glfwWaitEvents(); + { + if (animate_cursor) + glfwWaitEventsTimeout(1.0 / 30.0); + else + glfwWaitEvents(); + } else glfwPollEvents(); diff --git a/tests/events.c b/tests/events.c index 3d4a81065..5e697f973 100644 --- a/tests/events.c +++ b/tests/events.c @@ -451,6 +451,29 @@ static void monitor_callback(GLFWmonitor* monitor, int event) } } +static void joystick_callback(int joy, int event) +{ + if (event == GLFW_CONNECTED) + { + int axisCount, buttonCount; + + glfwGetJoystickAxes(joy, &axisCount); + glfwGetJoystickButtons(joy, &buttonCount); + + printf("%08x at %0.3f: Joystick %i (%s) was connected with %i axes and %i buttons\n", + counter++, glfwGetTime(), + joy, + glfwGetJoystickName(joy), + axisCount, + buttonCount); + } + else + { + printf("%08x at %0.3f: Joystick %i was disconnected\n", + counter++, glfwGetTime(), joy); + } +} + int main(int argc, char** argv) { Slot* slots; @@ -467,6 +490,7 @@ int main(int argc, char** argv) printf("Library initialized\n"); glfwSetMonitorCallback(monitor_callback); + glfwSetJoystickCallback(joystick_callback); while ((ch = getopt(argc, argv, "hfn:")) != -1) { diff --git a/tests/glfwinfo.c b/tests/glfwinfo.c index fce3885f2..e85b38585 100644 --- a/tests/glfwinfo.c +++ b/tests/glfwinfo.c @@ -23,8 +23,9 @@ // //======================================================================== -#include +#define VK_NO_PROTOTYPES #include +#include #include #include @@ -40,6 +41,9 @@ #define API_NAME_OPENGL "gl" #define API_NAME_OPENGL_ES "es" +#define API_NAME_NATIVE "native" +#define API_NAME_EGL "egl" + #define PROFILE_NAME_CORE "core" #define PROFILE_NAME_COMPAT "compat" @@ -59,6 +63,9 @@ static void usage(void) printf(" -b, --behavior=BEHAVIOR the release behavior to use (" BEHAVIOR_NAME_NONE " or " BEHAVIOR_NAME_FLUSH ")\n"); + printf(" -c, --context-api=API the context creation API to use (" + API_NAME_NATIVE " or " + API_NAME_EGL ")\n"); printf(" -d, --debug request a debug context\n"); printf(" -f, --forward require a forward-compatible context\n"); printf(" -h, --help show this help\n"); @@ -164,15 +171,15 @@ static const char* get_strategy_name_glfw(int strategy) return "unknown"; } -static void list_context_extensions(int api, int major, int minor) +static void list_context_extensions(int client, int major, int minor) { int i; GLint count; const GLubyte* extensions; - printf("%s context extensions:\n", get_api_name(api)); + printf("%s context extensions:\n", get_api_name(client)); - if (api == GLFW_OPENGL_API && major > 2) + if (client == GLFW_OPENGL_API && major > 2) { glGetIntegerv(GL_NUM_EXTENSIONS, &count); @@ -350,12 +357,13 @@ static void print_version(void) int main(int argc, char** argv) { - int ch, api, major, minor, revision, profile; + int ch, client, context, major, minor, revision, profile; GLint redbits, greenbits, bluebits, alphabits, depthbits, stencilbits; int list_extensions = GLFW_FALSE, list_layers = GLFW_FALSE; + GLenum error; GLFWwindow* window; - enum { API, BEHAVIOR, DEBUG, FORWARD, HELP, EXTENSIONS, LAYERS, + enum { CLIENT, CONTEXT, BEHAVIOR, DEBUG, FORWARD, HELP, EXTENSIONS, LAYERS, MAJOR, MINOR, PROFILE, ROBUSTNESS, VERSION, REDBITS, GREENBITS, BLUEBITS, ALPHABITS, DEPTHBITS, STENCILBITS, ACCUMREDBITS, ACCUMGREENBITS, ACCUMBLUEBITS, ACCUMALPHABITS, @@ -363,7 +371,8 @@ int main(int argc, char** argv) const struct option options[] = { { "behavior", 1, NULL, BEHAVIOR }, - { "client-api", 1, NULL, API }, + { "client-api", 1, NULL, CLIENT }, + { "context-api", 1, NULL, CONTEXT }, { "debug", 0, NULL, DEBUG }, { "forward", 0, NULL, FORWARD }, { "help", 0, NULL, HELP }, @@ -408,7 +417,7 @@ int main(int argc, char** argv) switch (ch) { case 'a': - case API: + case CLIENT: if (strcasecmp(optarg, API_NAME_OPENGL) == 0) glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); else if (strcasecmp(optarg, API_NAME_OPENGL_ES) == 0) @@ -437,6 +446,18 @@ int main(int argc, char** argv) exit(EXIT_FAILURE); } break; + case 'c': + case CONTEXT: + if (strcasecmp(optarg, API_NAME_NATIVE) == 0) + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); + else if (strcasecmp(optarg, API_NAME_EGL) == 0) + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); + else + { + usage(); + exit(EXIT_FAILURE); + } + break; case 'd': case DEBUG: glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); @@ -608,32 +629,37 @@ int main(int argc, char** argv) glfwMakeContextCurrent(window); gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + error = glGetError(); + if (error != GL_NO_ERROR) + printf("*** OpenGL error after make current: 0x%08x ***\n", error); + // Report client API version - api = glfwGetWindowAttrib(window, GLFW_CLIENT_API); + client = glfwGetWindowAttrib(window, GLFW_CLIENT_API); + context = glfwGetWindowAttrib(window, GLFW_CONTEXT_CREATION_API); major = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MAJOR); minor = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MINOR); revision = glfwGetWindowAttrib(window, GLFW_CONTEXT_REVISION); profile = glfwGetWindowAttrib(window, GLFW_OPENGL_PROFILE); printf("%s context version string: \"%s\"\n", - get_api_name(api), + get_api_name(client), glGetString(GL_VERSION)); printf("%s context version parsed by GLFW: %u.%u.%u\n", - get_api_name(api), + get_api_name(client), major, minor, revision); // Report client API context properties - if (api == GLFW_OPENGL_API) + if (client == GLFW_OPENGL_API) { if (major >= 3) { GLint flags; glGetIntegerv(GL_CONTEXT_FLAGS, &flags); - printf("%s context flags (0x%08x):", get_api_name(api), flags); + printf("%s context flags (0x%08x):", get_api_name(client), flags); if (flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) printf(" forward-compatible"); @@ -645,7 +671,7 @@ int main(int argc, char** argv) printf(" no-error"); putchar('\n'); - printf("%s context flags parsed by GLFW:", get_api_name(api)); + printf("%s context flags parsed by GLFW:", get_api_name(client)); if (glfwGetWindowAttrib(window, GLFW_OPENGL_FORWARD_COMPAT)) printf(" forward-compatible"); @@ -664,12 +690,12 @@ int main(int argc, char** argv) glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask); printf("%s profile mask (0x%08x): %s\n", - get_api_name(api), + get_api_name(client), mask, get_profile_name_gl(mask)); printf("%s profile mask parsed by GLFW: %s\n", - get_api_name(api), + get_api_name(client), get_profile_name_glfw(profile)); } @@ -680,33 +706,33 @@ int main(int argc, char** argv) glGetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, &strategy); printf("%s robustness strategy (0x%08x): %s\n", - get_api_name(api), + get_api_name(client), strategy, get_strategy_name_gl(strategy)); printf("%s robustness strategy parsed by GLFW: %s\n", - get_api_name(api), + get_api_name(client), get_strategy_name_glfw(robustness)); } } printf("%s context renderer string: \"%s\"\n", - get_api_name(api), + get_api_name(client), glGetString(GL_RENDERER)); printf("%s context vendor string: \"%s\"\n", - get_api_name(api), + get_api_name(client), glGetString(GL_VENDOR)); if (major >= 2) { printf("%s context shading language version: \"%s\"\n", - get_api_name(api), + get_api_name(client), glGetString(GL_SHADING_LANGUAGE_VERSION)); } - printf("%s framebuffer:\n", get_api_name(api)); + printf("%s framebuffer:\n", get_api_name(client)); - if (api == GLFW_OPENGL_API && profile == GLFW_OPENGL_CORE_PROFILE) + if (client == GLFW_OPENGL_API && profile == GLFW_OPENGL_CORE_PROFILE) { glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_BACK_LEFT, @@ -746,7 +772,7 @@ int main(int argc, char** argv) printf(" red: %u green: %u blue: %u alpha: %u depth: %u stencil: %u\n", redbits, greenbits, bluebits, alphabits, depthbits, stencilbits); - if (api == GLFW_OPENGL_ES_API || + if (client == GLFW_OPENGL_ES_API || glfwExtensionSupported("GL_ARB_multisample") || major > 1 || minor >= 3) { @@ -757,7 +783,7 @@ int main(int argc, char** argv) printf(" samples: %u sample buffers: %u\n", samples, samplebuffers); } - if (api == GLFW_OPENGL_API && profile != GLFW_OPENGL_CORE_PROFILE) + if (client == GLFW_OPENGL_API && profile != GLFW_OPENGL_CORE_PROFILE) { GLint accumredbits, accumgreenbits, accumbluebits, accumalphabits; GLint auxbuffers; @@ -773,7 +799,7 @@ int main(int argc, char** argv) } if (list_extensions) - list_context_extensions(api, major, minor); + list_context_extensions(client, major, minor); printf("Vulkan loader: %s\n", glfwVulkanSupported() ? "available" : "missing"); @@ -792,7 +818,7 @@ int main(int argc, char** argv) PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices; PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties; - re = glfwGetRequiredInstanceExtensions((int*) &re_count); + re = glfwGetRequiredInstanceExtensions(&re_count); printf("Vulkan required instance extensions:"); for (i = 0; i < re_count; i++) @@ -800,9 +826,7 @@ int main(int argc, char** argv) putchar('\n'); if (list_extensions) - { list_vulkan_instance_extensions(); - } if (list_layers) list_vulkan_instance_layers(); @@ -812,7 +836,7 @@ int main(int argc, char** argv) ai.applicationVersion = GLFW_VERSION_MAJOR; ai.pEngineName = "GLFW"; ai.engineVersion = GLFW_VERSION_MAJOR; - ai.apiVersion = VK_API_VERSION; + ai.apiVersion = VK_API_VERSION_1_0; ici.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; ici.pApplicationInfo = &ai; diff --git a/tests/icon.c b/tests/icon.c new file mode 100644 index 000000000..781b2cf65 --- /dev/null +++ b/tests/icon.c @@ -0,0 +1,148 @@ +//======================================================================== +// Window icon test program +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// +// This program is used to test the icon feature. +// +//======================================================================== + +#include +#include + +#include +#include +#include + +// a simple glfw logo +const char* const logo[] = +{ + "................", + "................", + "...0000..0......", + "...0.....0......", + "...0.00..0......", + "...0..0..0......", + "...0000..0000...", + "................", + "................", + "...000..0...0...", + "...0....0...0...", + "...000..0.0.0...", + "...0....0.0.0...", + "...0....00000...", + "................", + "................" +}; + +const unsigned char icon_colors[5][4] = +{ + { 0, 0, 0, 255 }, // black + { 255, 0, 0, 255 }, // red + { 0, 255, 0, 255 }, // green + { 0, 0, 255, 255 }, // blue + { 255, 255, 255, 255 } // white +}; + +static int cur_icon_color = 0; + +static void set_icon(GLFWwindow* window, int icon_color) +{ + int x, y; + unsigned char pixels[16 * 16 * 4]; + unsigned char* target = pixels; + GLFWimage img = { 16, 16, pixels }; + + for (y = 0; y < img.width; y++) + { + for (x = 0; x < img.height; x++) + { + if (logo[y][x] == '0') + memcpy(target, icon_colors[icon_color], 4); + else + memset(target, 0, 4); + + target += 4; + } + } + + glfwSetWindowIcon(window, 1, &img); +} + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (action != GLFW_PRESS) + return; + + switch (key) + { + case GLFW_KEY_ESCAPE: + glfwSetWindowShouldClose(window, GLFW_TRUE); + break; + case GLFW_KEY_SPACE: + cur_icon_color = (cur_icon_color + 1) % 5; + set_icon(window, cur_icon_color); + break; + case GLFW_KEY_X: + glfwSetWindowIcon(window, 0, NULL); + break; + } +} + +int main(int argc, char** argv) +{ + GLFWwindow* window; + + if (!glfwInit()) + { + fprintf(stderr, "Failed to initialize GLFW\n"); + exit(EXIT_FAILURE); + } + + window = glfwCreateWindow(200, 200, "Window Icon", NULL, NULL); + if (!window) + { + glfwTerminate(); + + fprintf(stderr, "Failed to open GLFW window\n"); + exit(EXIT_FAILURE); + } + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + + glfwSetKeyCallback(window, key_callback); + set_icon(window, cur_icon_color); + + while (!glfwWindowShouldClose(window)) + { + glClear(GL_COLOR_BUFFER_BIT); + glfwSwapBuffers(window); + glfwWaitEvents(); + } + + glfwDestroyWindow(window); + glfwTerminate(); + exit(EXIT_SUCCESS); +} + diff --git a/tests/iconify.c b/tests/iconify.c index a5675574e..870345987 100644 --- a/tests/iconify.c +++ b/tests/iconify.c @@ -36,6 +36,8 @@ #include "getopt.h" +static int windowed_xpos, windowed_ypos, windowed_width, windowed_height; + static void usage(void) { printf("Usage: iconify [-h] [-f [-a] [-n]]\n"); @@ -62,12 +64,47 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action, switch (key) { - case GLFW_KEY_SPACE: + case GLFW_KEY_I: glfwIconifyWindow(window); break; + case GLFW_KEY_M: + glfwMaximizeWindow(window); + break; + case GLFW_KEY_R: + glfwRestoreWindow(window); + break; case GLFW_KEY_ESCAPE: glfwSetWindowShouldClose(window, GLFW_TRUE); break; + case GLFW_KEY_F11: + case GLFW_KEY_ENTER: + { + if (mods != GLFW_MOD_ALT) + return; + + if (glfwGetWindowMonitor(window)) + { + glfwSetWindowMonitor(window, NULL, + windowed_xpos, windowed_ypos, + windowed_width, windowed_height, + 0); + } + else + { + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + if (monitor) + { + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + glfwGetWindowPos(window, &windowed_xpos, &windowed_ypos); + glfwGetWindowSize(window, &windowed_width, &windowed_height); + glfwSetWindowMonitor(window, monitor, + 0, 0, mode->width, mode->height, + mode->refreshRate); + } + } + + break; + } } } @@ -240,7 +277,7 @@ int main(int argc, char** argv) for (;;) { - glfwPollEvents(); + glfwWaitEvents(); for (i = 0; i < window_count; i++) { diff --git a/tests/joysticks.c b/tests/joysticks.c index 7eaf52383..04b578e90 100644 --- a/tests/joysticks.c +++ b/tests/joysticks.c @@ -39,17 +39,7 @@ #define strdup(x) _strdup(x) #endif -typedef struct Joystick -{ - int present; - char* name; - float* axes; - unsigned char* buttons; - int axis_count; - int button_count; -} Joystick; - -static Joystick joysticks[GLFW_JOYSTICK_LAST - GLFW_JOYSTICK_1 + 1]; +static int joysticks[GLFW_JOYSTICK_LAST + 1]; static int joystick_count = 0; static void error_callback(int error, const char* description) @@ -62,19 +52,23 @@ static void framebuffer_size_callback(GLFWwindow* window, int width, int height) glViewport(0, 0, width, height); } -static void draw_joystick(Joystick* j, int x, int y, int width, int height) +static void draw_joystick(int index, int x, int y, int width, int height) { int i; + int axis_count, button_count; + const float* axes; + const unsigned char* buttons; const int axis_height = 3 * height / 4; const int button_height = height / 4; - if (j->axis_count) + axes = glfwGetJoystickAxes(joysticks[index], &axis_count); + if (axis_count) { - const int axis_width = width / j->axis_count; + const int axis_width = width / axis_count; - for (i = 0; i < j->axis_count; i++) + for (i = 0; i < axis_count; i++) { - float value = j->axes[i] / 2.f + 0.5f; + float value = axes[i] / 2.f + 0.5f; glColor3f(0.3f, 0.3f, 0.3f); glRecti(x + i * axis_width, @@ -90,13 +84,14 @@ static void draw_joystick(Joystick* j, int x, int y, int width, int height) } } - if (j->button_count) + buttons = glfwGetJoystickButtons(joysticks[index], &button_count); + if (button_count) { - const int button_width = width / j->button_count; + const int button_width = width / button_count; - for (i = 0; i < j->button_count; i++) + for (i = 0; i < button_count; i++) { - if (j->buttons[i]) + if (buttons[i]) glColor3f(1.f, 1.f, 1.f); else glColor3f(0.3f, 0.3f, 0.3f); @@ -120,79 +115,58 @@ static void draw_joysticks(GLFWwindow* window) glOrtho(0.f, width, height, 0.f, 1.f, -1.f); glMatrixMode(GL_MODELVIEW); - for (i = 0; i < sizeof(joysticks) / sizeof(Joystick); i++) + for (i = 0; i < joystick_count; i++) { - Joystick* j = joysticks + i; - - if (j->present) - { - draw_joystick(j, - 0, offset * height / joystick_count, - width, height / joystick_count); - offset++; - } + draw_joystick(i, + 0, offset * height / joystick_count, + width, height / joystick_count); + offset++; } } -static void refresh_joysticks(void) +static void joystick_callback(int joy, int event) { - int i; - - for (i = 0; i < sizeof(joysticks) / sizeof(Joystick); i++) + if (event == GLFW_CONNECTED) { - Joystick* j = joysticks + i; + int axis_count, button_count; - if (glfwJoystickPresent(GLFW_JOYSTICK_1 + i)) + glfwGetJoystickAxes(joy, &axis_count); + glfwGetJoystickButtons(joy, &button_count); + + printf("Found joystick %i named \'%s\' with %i axes, %i buttons\n", + joy + 1, + glfwGetJoystickName(joy), + axis_count, + button_count); + + joysticks[joystick_count++] = joy; + } + else if (event == GLFW_DISCONNECTED) + { + int i; + + for (i = 0; i < joystick_count; i++) { - const float* axes; - const unsigned char* buttons; - int axis_count, button_count; - - free(j->name); - j->name = strdup(glfwGetJoystickName(GLFW_JOYSTICK_1 + i)); - - axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1 + i, &axis_count); - if (axis_count != j->axis_count) - { - j->axis_count = axis_count; - j->axes = realloc(j->axes, j->axis_count * sizeof(float)); - } - - memcpy(j->axes, axes, axis_count * sizeof(float)); - - buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1 + i, &button_count); - if (button_count != j->button_count) - { - j->button_count = button_count; - j->buttons = realloc(j->buttons, j->button_count); - } - - memcpy(j->buttons, buttons, button_count * sizeof(unsigned char)); - - if (!j->present) - { - printf("Found joystick %i named \'%s\' with %i axes, %i buttons\n", - i + 1, j->name, j->axis_count, j->button_count); - - joystick_count++; - } - - j->present = GLFW_TRUE; + if (joysticks[i] == joy) + break; } - else - { - if (j->present) - { - printf("Lost joystick %i named \'%s\'\n", i + 1, j->name); - free(j->name); - free(j->axes); - free(j->buttons); - memset(j, 0, sizeof(Joystick)); + for (i = i + 1; i < joystick_count; i++) + joysticks[i - 1] = joysticks[i]; - joystick_count--; - } - } + printf("Lost joystick %i\n", joy + 1); + joystick_count--; + } +} + +static void find_joysticks(void) +{ + int joy; + + for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + { + if (glfwJoystickPresent(joy)) + joystick_callback(joy, GLFW_CONNECTED); } } @@ -207,6 +181,9 @@ int main(void) if (!glfwInit()) exit(EXIT_FAILURE); + find_joysticks(); + glfwSetJoystickCallback(joystick_callback); + window = glfwCreateWindow(640, 480, "Joystick Test", NULL, NULL); if (!window) { @@ -224,11 +201,13 @@ int main(void) { glClear(GL_COLOR_BUFFER_BIT); - refresh_joysticks(); draw_joysticks(window); glfwSwapBuffers(window); glfwPollEvents(); + + // Workaround for an issue with msvcrt and mintty + fflush(stdout); } glfwTerminate(); diff --git a/tests/timeout.c b/tests/timeout.c new file mode 100644 index 000000000..83f18998e --- /dev/null +++ b/tests/timeout.c @@ -0,0 +1,97 @@ +//======================================================================== +// Event wait timeout test +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// +// This test is intended to verify that waiting for events with timeout works +// +//======================================================================== + +#include +#include + +#include +#include +#include +#include + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + glfwSetWindowShouldClose(window, GLFW_TRUE); +} + +static float nrand(void) +{ + return (float) rand() / (float) RAND_MAX; +} + +int main(void) +{ + GLFWwindow* window; + + srand((unsigned int) time(NULL)); + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + window = glfwCreateWindow(640, 480, "Event Wait Timeout Test", NULL, NULL); + if (!window) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + glfwSetKeyCallback(window, key_callback); + + while (!glfwWindowShouldClose(window)) + { + int width, height; + float r = nrand(), g = nrand(), b = nrand(); + float l = (float) sqrt(r * r + g * g + b * b); + + glfwGetFramebufferSize(window, &width, &height); + + glViewport(0, 0, width, height); + glClearColor(r / l, g / l, b / l, 1.f); + glClear(GL_COLOR_BUFFER_BIT); + glfwSwapBuffers(window); + + glfwWaitEventsTimeout(1.0); + } + + glfwDestroyWindow(window); + + glfwTerminate(); + exit(EXIT_SUCCESS); +} + diff --git a/tests/vulkan.c b/tests/vulkan.c index 21dd0137a..1b62d6814 100644 --- a/tests/vulkan.c +++ b/tests/vulkan.c @@ -1732,7 +1732,7 @@ static void demo_init_vk(struct demo *demo) { } /* Look for instance extensions */ - required_extensions = glfwGetRequiredInstanceExtensions((int*) &required_extension_count); + required_extensions = glfwGetRequiredInstanceExtensions(&required_extension_count); if (!required_extensions) { ERR_EXIT("glfwGetRequiredInstanceExtensions failed to find the " "platform surface extensions.\n\nDo you have a compatible " @@ -1778,7 +1778,7 @@ static void demo_init_vk(struct demo *demo) { .applicationVersion = 0, .pEngineName = APP_SHORT_NAME, .engineVersion = 0, - .apiVersion = VK_API_VERSION, + .apiVersion = VK_API_VERSION_1_0, }; VkInstanceCreateInfo inst_info = { .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, @@ -2106,8 +2106,7 @@ static void demo_init_connection(struct demo *demo) { } if (!glfwVulkanSupported()) { - printf("Cannot find a compatible Vulkan installable client driver " - "(ICD).\nExiting ...\n"); + printf("GLFW failed to find the Vulkan loader.\nExiting ...\n"); fflush(stdout); exit(1); } diff --git a/tests/windows.c b/tests/windows.c index b0d15e7fe..21db08d5c 100644 --- a/tests/windows.c +++ b/tests/windows.c @@ -33,6 +33,8 @@ #include #include +#include "getopt.h" + static const char* titles[] = { "Red", @@ -52,6 +54,14 @@ static const struct { 0.98f, 0.74f, 0.04f } }; +static void usage(void) +{ + printf("Usage: windows [-h] [-b]\n"); + printf("Options:\n"); + printf(" -b create decorated windows\n"); + printf(" -h show this help\n"); +} + static void error_callback(int error, const char* description) { fprintf(stderr, "Error: %s\n", description); @@ -80,16 +90,33 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action, int main(int argc, char** argv) { - int i; + int i, ch; + int decorated = GLFW_FALSE; int running = GLFW_TRUE; GLFWwindow* windows[4]; + while ((ch = getopt(argc, argv, "bh")) != -1) + { + switch (ch) + { + case 'b': + decorated = GLFW_TRUE; + break; + case 'h': + usage(); + exit(EXIT_SUCCESS); + default: + usage(); + exit(EXIT_FAILURE); + } + } + glfwSetErrorCallback(error_callback); if (!glfwInit()) exit(EXIT_FAILURE); - glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); + glfwWindowHint(GLFW_DECORATED, decorated); glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); for (i = 0; i < 4; i++) @@ -130,7 +157,7 @@ int main(int argc, char** argv) running = GLFW_FALSE; } - glfwPollEvents(); + glfwWaitEvents(); } glfwTerminate();