Merge pull request #3 from glfw/master

merge code
This commit is contained in:
playbar 2017-12-13 14:13:36 +08:00 committed by GitHub
commit fc2213e2e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
110 changed files with 9044 additions and 3049 deletions

View File

@ -4,6 +4,7 @@ branches:
- master - master
skip_tags: true skip_tags: true
environment: environment:
CFLAGS: /WX
matrix: matrix:
- BUILD_SHARED_LIBS: ON - BUILD_SHARED_LIBS: ON
- BUILD_SHARED_LIBS: OFF - BUILD_SHARED_LIBS: OFF
@ -12,11 +13,11 @@ matrix:
build_script: build_script:
- mkdir build - mkdir build
- cd build - cd build
- cmake -DBUILD_SHARED_LIBS=%BUILD_SHARED_LIBS% .. - cmake -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_SHARED_LIBS=%BUILD_SHARED_LIBS% ..
- cmake --build . - cmake --build .
notifications: notifications:
- provider: Email - provider: Email
to: to:
- ci@glfw.org - ci@glfw.org
- on_build_failure: true on_build_failure: true
- on_build_success: false on_build_success: false

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
*.m linguist-language=Objective-C

2
.gitignore vendored
View File

@ -56,6 +56,7 @@ examples/heightmap
examples/offscreen examples/offscreen
examples/particles examples/particles
examples/splitview examples/splitview
examples/sharing
examples/simple examples/simple
examples/wave examples/wave
tests/*.app tests/*.app
@ -72,7 +73,6 @@ tests/joysticks
tests/monitors tests/monitors
tests/msaa tests/msaa
tests/reopen tests/reopen
tests/sharing
tests/tearing tests/tearing
tests/threads tests/threads
tests/timeout tests/timeout

View File

@ -4,23 +4,59 @@ branches:
only: only:
- ci - ci
- master - master
os:
- linux
- osx
sudo: false sudo: false
dist: trusty
addons: addons:
apt: apt:
sources:
- kubuntu-backports
packages: packages:
- cmake - cmake
- libxrandr-dev
- libxinerama-dev
- libxcursor-dev
- libxi-dev
env: env:
global:
- CFLAGS=-Werror
matrix:
include:
- os: linux
env: BUILD_SHARED_LIBS=ON
- os: linux
env: BUILD_SHARED_LIBS=OFF
- os: linux
sudo: required
addons:
apt:
packages:
- libwayland-dev
- libxkbcommon-dev
- libegl1-mesa-dev
env:
- USE_WAYLAND=ON
- BUILD_SHARED_LIBS=ON - BUILD_SHARED_LIBS=ON
- os: linux
sudo: required
addons:
apt:
packages:
- libwayland-dev
- libxkbcommon-dev
- libegl1-mesa-dev
env:
- USE_WAYLAND=ON
- BUILD_SHARED_LIBS=OFF - BUILD_SHARED_LIBS=OFF
- os: osx
env: BUILD_SHARED_LIBS=ON
- os: osx
env: BUILD_SHARED_LIBS=OFF
script: script:
- mkdir build - mkdir build
- cd build - cd build
- cmake -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS} .. - if test -n "${USE_WAYLAND}"; then wget https://mirrors.kernel.org/ubuntu/pool/universe/e/extra-cmake-modules/extra-cmake-modules_5.38.0a-0ubuntu1_amd64.deb; fi
- if test -n "${USE_WAYLAND}"; then sudo dpkg -i extra-cmake-modules_5.38.0a-0ubuntu1_amd64.deb; fi
- if test -n "${USE_WAYLAND}"; then git clone git://anongit.freedesktop.org/wayland/wayland-protocols; fi
- if test -n "${USE_WAYLAND}"; then pushd wayland-protocols; ./autogen.sh --prefix=/usr; make -j; sudo make install; popd; fi
- cmake -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS} -DGLFW_USE_WAYLAND=${USE_WAYLAND} ..
- cmake --build . - cmake --build .
notifications: notifications:
email: email:

View File

@ -0,0 +1,33 @@
# Usage:
# cmake -P GenerateMappings.cmake <path/to/mappings.h.in> <path/to/mappings.h>
set(source_url "https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb_204.txt")
set(source_path "${CMAKE_CURRENT_BINARY_DIR}/gamecontrollerdb.txt")
set(template_path "${CMAKE_ARGV3}")
set(target_path "${CMAKE_ARGV4}")
if (NOT EXISTS "${template_path}")
message(FATAL_ERROR "Failed to find template file ${template_path}")
endif()
file(DOWNLOAD "${source_url}" "${source_path}"
STATUS download_status
TLS_VERIFY on)
list(GET download_status 0 status_code)
list(GET download_status 1 status_message)
if (status_code)
message(FATAL_ERROR "Failed to download ${source_url}: ${status_message}")
endif()
file(STRINGS "${source_path}" lines)
foreach(line ${lines})
if ("${line}" MATCHES "^[0-9a-fA-F].*$")
set(GLFW_GAMEPAD_MAPPINGS "${GLFW_GAMEPAD_MAPPINGS}\"${line}\",\n")
endif()
endforeach()
configure_file("${template_path}" "${target_path}" @ONLY NEWLINE_STYLE UNIX)
file(REMOVE "${source_path}")

View File

@ -1,37 +1,34 @@
# Try to find Mir on a Unix system # FindMir
# -------
# Finds the Mir library
# #
# This will define: # This will will define the following variables::
# #
# MIR_FOUND - System has Mir # MIR_FOUND - the system has Mir
# MIR_LIBRARIES - Link these to use Mir # MIR_INCLUDE_DIRS - the Mir include directory
# MIR_INCLUDE_DIR - Include directory for Mir # MIR_LIBRARIES - the Mir libraries
# MIR_DEFINITIONS - Compiler switches required for using Mir # MIR_DEFINITIONS - the Mir definitions
if (NOT WIN32)
find_package (PkgConfig) find_package (PkgConfig)
pkg_check_modules (PKG_MIR QUIET mirclient) if(PKG_CONFIG_FOUND)
set(MIR_DEFINITIONS ${PKG_MIR_CFLAGS_OTHER}) pkg_check_modules (PC_MIR mirclient>=0.26.2 QUIET)
find_path(MIR_INCLUDE_DIR find_path(MIR_INCLUDE_DIR NAMES mir_toolkit/mir_client_library.h
NAMES xkbcommon/xkbcommon.h PATHS ${PC_MIR_INCLUDE_DIRS})
HINTS ${PC_XKBCOMMON_INCLUDE_DIR} ${PC_XKBCOMMON_INCLUDE_DIRS}
)
find_library(MIR_LIBRARY find_library(MIR_LIBRARY NAMES mirclient
NAMES mirclient PATHS ${PC_MIR_LIBRARIES} ${PC_MIR_LIBRARY_DIRS})
HINTS ${PKG_MIR_LIBRARIES} ${MIR_LIBRARY_DIRS}
)
set (MIR_INCLUDE_DIR ${PKG_MIR_INCLUDE_DIRS})
set (MIR_LIBRARIES ${MIR_LIBRARY})
include (FindPackageHandleStandardArgs) include (FindPackageHandleStandardArgs)
find_package_handle_standard_args (MIR DEFAULT_MSG find_package_handle_standard_args (MIR
MIR_LIBRARIES REQUIRED_VARS MIR_LIBRARY MIR_INCLUDE_DIR)
MIR_INCLUDE_DIR
)
mark_as_advanced (MIR_LIBRARIES MIR_INCLUDE_DIR) if (MIR_FOUND)
set(MIR_LIBRARIES ${MIR_LIBRARY})
set(MIR_INCLUDE_DIRS ${PC_MIR_INCLUDE_DIRS})
set(MIR_DEFINITIONS -DHAVE_MIR=1)
endif()
endif () mark_as_advanced (MIR_LIBRARY MIR_INCLUDE_DIR)
endif()

View File

@ -28,12 +28,12 @@ if (WIN32)
"$ENV{VK_SDK_PATH}/Bin32") "$ENV{VK_SDK_PATH}/Bin32")
endif() endif()
elseif (APPLE) elseif (APPLE)
set(CMAKE_FIND_FRAMEWORK NEVER)
find_library(VULKAN_LIBRARY MoltenVK) find_library(VULKAN_LIBRARY MoltenVK)
if (VULKAN_LIBRARY) set(CMAKE_FIND_FRAMEWORK ONLY)
set(VULKAN_STATIC_LIBRARY ${VULKAN_LIBRARY}) find_library(VULKAN_STATIC_LIBRARY MoltenVK)
find_path(VULKAN_INCLUDE_DIR NAMES vulkan/vulkan.h HINTS find_path(VULKAN_INCLUDE_DIR NAMES vulkan/vulkan.h HINTS
"${VULKAN_LIBRARY}/Headers") "${VULKAN_LIBRARY}/Headers")
endif()
else() else()
find_path(VULKAN_INCLUDE_DIR NAMES vulkan/vulkan.h HINTS find_path(VULKAN_INCLUDE_DIR NAMES vulkan/vulkan.h HINTS
"$ENV{VULKAN_SDK}/include") "$ENV{VULKAN_SDK}/include")

View File

@ -1,8 +1,8 @@
set(CMAKE_LEGACY_CYGWIN_WIN32 OFF) cmake_minimum_required(VERSION 2.8.12)
project(GLFW C) project(GLFW C)
cmake_minimum_required(VERSION 2.8.12) set(CMAKE_LEGACY_CYGWIN_WIN32 OFF)
if (NOT CMAKE_VERSION VERSION_LESS "3.0") if (NOT CMAKE_VERSION VERSION_LESS "3.0")
# Until all major package systems have moved to CMake 3, # Until all major package systems have moved to CMake 3,
@ -181,7 +181,7 @@ endif()
#-------------------------------------------------------------------- #--------------------------------------------------------------------
if (GLFW_VULKAN_STATIC) if (GLFW_VULKAN_STATIC)
if (VULKAN_FOUND AND VULKAN_STATIC_LIBRARY) if (VULKAN_FOUND AND VULKAN_STATIC_LIBRARY)
list(APPEND glfw_LIBRARIES ${VULKAN_STATIC_LIBRARY} ${GLFW_VULKAN_DEPS}) list(APPEND glfw_LIBRARIES "${VULKAN_STATIC_LIBRARY}" ${GLFW_VULKAN_DEPS})
if (BUILD_SHARED_LIBS) if (BUILD_SHARED_LIBS)
message(WARNING "Linking Vulkan loader static library into GLFW") message(WARNING "Linking Vulkan loader static library into GLFW")
endif() endif()
@ -245,38 +245,28 @@ if (_GLFW_X11)
# Check for XRandR (modern resolution switching and gamma control) # Check for XRandR (modern resolution switching and gamma control)
if (NOT X11_Xrandr_FOUND) if (NOT X11_Xrandr_FOUND)
message(FATAL_ERROR "The RandR library and headers were not found") message(FATAL_ERROR "The RandR headers were not found")
endif() endif()
list(APPEND glfw_INCLUDE_DIRS "${X11_Xrandr_INCLUDE_PATH}")
list(APPEND glfw_LIBRARIES "${X11_Xrandr_LIB}")
list(APPEND glfw_PKG_DEPS "xrandr")
# Check for Xinerama (legacy multi-monitor support) # Check for Xinerama (legacy multi-monitor support)
if (NOT X11_Xinerama_FOUND) if (NOT X11_Xinerama_FOUND)
message(FATAL_ERROR "The Xinerama library and headers were not found") message(FATAL_ERROR "The Xinerama headers were not found")
endif() endif()
list(APPEND glfw_INCLUDE_DIRS "${X11_Xinerama_INCLUDE_PATH}")
list(APPEND glfw_LIBRARIES "${X11_Xinerama_LIB}")
list(APPEND glfw_PKG_DEPS "xinerama")
# Check for Xkb (X keyboard extension) # Check for Xkb (X keyboard extension)
if (NOT X11_Xkb_FOUND) if (NOT X11_Xkb_FOUND)
message(FATAL_ERROR "The X keyboard extension headers were not found") message(FATAL_ERROR "The X keyboard extension headers were not found")
endif() endif()
list(APPEND glfw_INCLUDE_DIR "${X11_Xkb_INCLUDE_PATH}") # Check for Xcursor (cursor creation from RGBA images)
# Check for Xcursor
if (NOT X11_Xcursor_FOUND) if (NOT X11_Xcursor_FOUND)
message(FATAL_ERROR "The Xcursor libraries and headers were not found") message(FATAL_ERROR "The Xcursor headers were not found")
endif() endif()
list(APPEND glfw_INCLUDE_DIR "${X11_Xcursor_INCLUDE_PATH}") list(APPEND glfw_INCLUDE_DIRS "${X11_Xrandr_INCLUDE_PATH}"
list(APPEND glfw_LIBRARIES "${X11_Xcursor_LIB}") "${X11_Xinerama_INCLUDE_PATH}"
list(APPEND glfw_PKG_DEPS "xcursor") "${X11_Xkb_INCLUDE_PATH}"
"${X11_Xcursor_INCLUDE_PATH}")
endif() endif()
#-------------------------------------------------------------------- #--------------------------------------------------------------------
@ -284,21 +274,22 @@ endif()
#-------------------------------------------------------------------- #--------------------------------------------------------------------
if (_GLFW_WAYLAND) if (_GLFW_WAYLAND)
find_package(ECM REQUIRED NO_MODULE) find_package(ECM REQUIRED NO_MODULE)
list(APPEND CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) list(APPEND CMAKE_MODULE_PATH "${ECM_MODULE_PATH}")
find_package(Wayland REQUIRED) find_package(Wayland REQUIRED Client Cursor Egl)
find_package(WaylandScanner REQUIRED) find_package(WaylandScanner REQUIRED)
find_package(WaylandProtocols 1.1 REQUIRED) find_package(WaylandProtocols 1.1 REQUIRED)
list(APPEND glfw_PKG_DEPS "wayland-egl") list(APPEND glfw_PKG_DEPS "wayland-egl")
list(APPEND glfw_INCLUDE_DIRS "${Wayland_INCLUDE_DIR}") list(APPEND glfw_INCLUDE_DIRS "${Wayland_INCLUDE_DIRS}")
list(APPEND glfw_LIBRARIES "${Wayland_LIBRARIES}" "${CMAKE_THREAD_LIBS_INIT}") list(APPEND glfw_LIBRARIES "${Wayland_LIBRARIES}" "${CMAKE_THREAD_LIBS_INIT}")
find_package(XKBCommon REQUIRED) find_package(XKBCommon REQUIRED)
list(APPEND glfw_PKG_DEPS "xkbcommon")
list(APPEND glfw_INCLUDE_DIRS "${XKBCOMMON_INCLUDE_DIRS}") list(APPEND glfw_INCLUDE_DIRS "${XKBCOMMON_INCLUDE_DIRS}")
list(APPEND glfw_LIBRARIES "${XKBCOMMON_LIBRARY}")
include(CheckIncludeFiles)
check_include_files(xkbcommon/xkbcommon-compose.h HAVE_XKBCOMMON_COMPOSE_H)
endif() endif()
#-------------------------------------------------------------------- #--------------------------------------------------------------------
@ -308,7 +299,7 @@ if (_GLFW_MIR)
find_package(Mir REQUIRED) find_package(Mir REQUIRED)
list(APPEND glfw_PKG_DEPS "mirclient") list(APPEND glfw_PKG_DEPS "mirclient")
list(APPEND glfw_INCLUDE_DIRS "${MIR_INCLUDE_DIR}") list(APPEND glfw_INCLUDE_DIRS "${MIR_INCLUDE_DIRS}")
list(APPEND glfw_LIBRARIES "${MIR_LIBRARIES}" "${CMAKE_THREAD_LIBS_INIT}") list(APPEND glfw_LIBRARIES "${MIR_LIBRARIES}" "${CMAKE_THREAD_LIBS_INIT}")
find_package(XKBCommon REQUIRED) find_package(XKBCommon REQUIRED)
@ -413,6 +404,7 @@ if (GLFW_INSTALL)
add_custom_target(uninstall add_custom_target(uninstall
"${CMAKE_COMMAND}" -P "${CMAKE_COMMAND}" -P
"${GLFW_BINARY_DIR}/cmake_uninstall.cmake") "${GLFW_BINARY_DIR}/cmake_uninstall.cmake")
set_target_properties(uninstall PROPERTIES FOLDER "GLFW3")
endif() endif()
endif() endif()

152
README.md
View File

@ -36,29 +36,28 @@ fixes live in [other branches](https://github.com/glfw/glfw/branches/all) until
they are stable enough to merge. they are stable enough to merge.
If you are new to GLFW, you may find the If you are new to GLFW, you may find the
[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
3 useful. If you have used GLFW 2 in the past, there is a you have used GLFW 2 in the past, there is a [transition
[transition guide](http://www.glfw.org/docs/latest/moving.html) for moving to guide](http://www.glfw.org/docs/latest/moving.html) for moving to the GLFW
the GLFW 3 API. 3 API.
## Compiling GLFW ## Compiling GLFW
GLFW itself requires only the headers and libraries for your window system. It GLFW itself requires only the headers and libraries for your window system. It
does not need the headers for any context creation API (WGL, GLX, EGL, NSGL) or does not need the headers for any context creation API (WGL, GLX, EGL, NSGL,
rendering API (OpenGL, OpenGL ES, Vulkan) to enable support for them. OSMesa) or rendering API (OpenGL, OpenGL ES, Vulkan) to enable support for them.
GLFW supports compilation on Windows with Visual C++ 2010 and later, MinGW and GLFW supports compilation on Windows with Visual C++ 2010 and later, MinGW and
MinGW-w64, on macOS with Clang and on Linux and other Unix-like systems with GCC MinGW-w64, on macOS with Clang and on Linux and other Unix-like systems with GCC
and Clang. It will likely compile in other environments as well, but this is and Clang. It will likely compile in other environments as well, but this is
not regularly tested. not regularly tested.
There are also [pre-compiled Windows There are [pre-compiled Windows binaries](http://www.glfw.org/download.html)
binaries](http://www.glfw.org/download.html) available for all compilers available for all supported compilers.
supported on that platform.
See the [compilation guide](http://www.glfw.org/docs/latest/compile.html) for See the [compilation guide](http://www.glfw.org/docs/latest/compile.html) for
more information about how to compile GLFW. more information about how to compile GLFW yourself.
## Using GLFW ## Using GLFW
@ -70,7 +69,7 @@ and the API reference.
## Contributing to GLFW ## Contributing to GLFW
See the [contribution See the [contribution
guide](https://github.com/glfw/glfw/blob/master/.github/CONTRIBUTING.md) for guide](https://github.com/glfw/glfw/blob/master/docs/CONTRIBUTING.md) for
more information. more information.
@ -89,6 +88,9 @@ in the documentation for more information.
GLFW itself depends only on the headers and libraries for your window system. GLFW itself depends only on the headers and libraries for your window system.
The (experimental) Wayland backend also depends on the `extra-cmake-modules`
package, which is used to generated Wayland protocol headers.
The examples and test programs depend on a number of tiny libraries. These are The examples and test programs depend on a number of tiny libraries. These are
located in the `deps/` directory. located in the `deps/` directory.
@ -105,23 +107,39 @@ located in the `deps/` directory.
- [Vulkan headers](https://www.khronos.org/registry/vulkan/) for Vulkan tests - [Vulkan headers](https://www.khronos.org/registry/vulkan/) for Vulkan tests
The Vulkan example additionally requires the Vulkan SDK to be installed, or it The Vulkan example additionally requires the Vulkan SDK to be installed, or it
will not be included in the build. On macOS you need to set the path to the will not be included in the build. On macOS you need to provide the path to the
MoltenVK SDK manually as it has no standard location. MoltenVK SDK manually as it has no standard installation location.
The documentation is generated with [Doxygen](http://doxygen.org/). If CMake The documentation is generated with [Doxygen](http://doxygen.org/) if CMake can
does not find Doxygen, the documentation will not be generated when you build. find that tool.
## Reporting bugs ## Reporting bugs
Bugs are reported to our [issue tracker](https://github.com/glfw/glfw/issues). Bugs are reported to our [issue tracker](https://github.com/glfw/glfw/issues).
Please check the [contribution Please check the [contribution
guide](https://github.com/glfw/glfw/blob/master/.github/CONTRIBUTING.md) for guide](https://github.com/glfw/glfw/blob/master/docs/CONTRIBUTING.md) for
information on what to include when reporting a bug. information on what to include when reporting a bug.
## Changelog ## Changelog
- Added `glfwGetError` function for querying the last error code and its
description (#970)
- Added `glfwUpdateGamepadMappings` function for importing gamepad mappings in
SDL\_GameControllerDB format (#900)
- Added `glfwJoystickIsGamepad` function for querying whether a joystick has
a gamepad mapping (#900)
- Added `glfwGetJoystickGUID` function for querying the SDL compatible GUID of
a joystick (#900)
- Added `glfwGetGamepadName` function for querying the name provided by the
gamepad mapping (#900)
- Added `glfwGetGamepadState` function, `GLFW_GAMEPAD_*` and `GLFWgamepadstate`
for retrieving gamepad input state (#900)
- Added `glfwGetWindowContentScale` and `glfwGetMonitorContentScale` for
DPI-aware rendering (#235,#439,#677,#845,#898)
- Added `glfwRequestWindowAttention` function for requesting attention from the
user (#732,#988)
- Added `glfwGetKeyScancode` function that allows retrieving platform dependent - Added `glfwGetKeyScancode` function that allows retrieving platform dependent
scancodes for keys (#830) scancodes for keys (#830)
- Added `glfwSetWindowMaximizeCallback` and `GLFWwindowmaximizefun` for - Added `glfwSetWindowMaximizeCallback` and `GLFWwindowmaximizefun` for
@ -129,20 +147,32 @@ information on what to include when reporting a bug.
- Added `glfwSetWindowAttrib` function for changing window attributes (#537) - Added `glfwSetWindowAttrib` function for changing window attributes (#537)
- Added `glfwGetJoystickHats` function for querying joystick hats - Added `glfwGetJoystickHats` function for querying joystick hats
(#889,#906,#934) (#889,#906,#934)
- Added `glfwInitHint` function for setting library initialization hints - Added `glfwInitHint` and `glfwInitHintString` for setting initialization hints
- Added `glfwGetWindowOpacity` and `glfwSetWindowOpacity` for controlling whole
window transparency (#1089)
- Added `glfwGetX11SelectionString` and `glfwSetX11SelectionString`
functions for accessing X11 primary selection (#894,#1056)
- Added headless [OSMesa](http://mesa3d.org/osmesa.html) backend (#850) - Added headless [OSMesa](http://mesa3d.org/osmesa.html) backend (#850)
- Added definition of `GLAPIENTRY` to public header - Added definition of `GLAPIENTRY` to public header
- Added `GLFW_TRANSPARENT_FRAMEBUFFER` window hint and attribute for controlling
per-pixel framebuffer transparency (#197,#663,#715,#723,#1078)
- Added `GLFW_CENTER_CURSOR` window hint for controlling cursor centering - Added `GLFW_CENTER_CURSOR` window hint for controlling cursor centering
(#749,#842) (#749,#842)
- Added `GLFW_JOYSTICK_HAT_BUTTONS` init hint (#889) - Added `GLFW_JOYSTICK_HAT_BUTTONS` init hint (#889)
- Added `GLFW_LOCK_KEY_MODS` input mode and `GLFW_MOD_*_LOCK` mod bits (#946)
- Added macOS specific `GLFW_COCOA_RETINA_FRAMEBUFFER` window hint - Added macOS specific `GLFW_COCOA_RETINA_FRAMEBUFFER` window hint
- Added macOS specific `GLFW_COCOA_FRAME_AUTOSAVE` window hint (#195) - Added macOS specific `GLFW_COCOA_FRAME_AUTOSAVE` window hint (#195)
- Added macOS specific `GLFW_COCOA_GRAPHICS_SWITCHING` window hint (#377,#935) - Added macOS specific `GLFW_COCOA_GRAPHICS_SWITCHING` window hint (#377,#935)
- Added macOS specific `GLFW_COCOA_CHDIR_RESOURCES` init hint - Added macOS specific `GLFW_COCOA_CHDIR_RESOURCES` init hint
- Added macOS specific `GLFW_COCOA_MENUBAR` init hint - Added macOS specific `GLFW_COCOA_MENUBAR` init hint
- Added X11 specific `GLFW_X11_WM_CLASS_NAME` and `GLFW_X11_WM_CLASS_CLASS` init
hints (#893)
- Added `GLFW_INCLUDE_ES32` for including the OpenGL ES 3.2 header - Added `GLFW_INCLUDE_ES32` for including the OpenGL ES 3.2 header
- Added `GLFW_OSMESA_CONTEXT_API` for creating OpenGL contexts with - Added `GLFW_OSMESA_CONTEXT_API` for creating OpenGL contexts with
[OSMesa](https://www.mesa3d.org/osmesa.html) (#281) [OSMesa](https://www.mesa3d.org/osmesa.html) (#281)
- Added `GenerateMappings.cmake` script for updating gamepad mappings
- Deprecated window parameter of clipboard string functions
- Deprecated charmods callback
- Removed `GLFW_USE_RETINA` compile-time option - Removed `GLFW_USE_RETINA` compile-time option
- Removed `GLFW_USE_CHDIR` compile-time option - Removed `GLFW_USE_CHDIR` compile-time option
- Removed `GLFW_USE_MENUBAR` compile-time option - Removed `GLFW_USE_MENUBAR` compile-time option
@ -153,8 +183,10 @@ information on what to include when reporting a bug.
`vkGetInstanceProcAddr` when `_GLFW_VULKAN_STATIC` was enabled `vkGetInstanceProcAddr` when `_GLFW_VULKAN_STATIC` was enabled
- Bugfix: Invalid library paths were used in test and example CMake files (#930) - Bugfix: Invalid library paths were used in test and example CMake files (#930)
- Bugfix: The scancode for synthetic key release events was always zero - Bugfix: The scancode for synthetic key release events was always zero
- Bugfix: The generated Doxyfile did not handle paths with spaces (#1081)
- [Win32] Added system error strings to relevant GLFW error descriptions (#733) - [Win32] Added system error strings to relevant GLFW error descriptions (#733)
- [Win32] Moved to `WM_INPUT` for disabled cursor mode motion input (#125) - [Win32] Moved to `WM_INPUT` for disabled cursor mode motion input (#125)
- [Win32] Removed XInput circular deadzone from joystick axis data (#1045)
- [Win32] Bugfix: Undecorated windows could not be iconified by the user (#861) - [Win32] Bugfix: Undecorated windows could not be iconified by the user (#861)
- [Win32] Bugfix: Deadzone logic could underflow with some controllers (#910) - [Win32] Bugfix: Deadzone logic could underflow with some controllers (#910)
- [Win32] Bugfix: Bitness test in `FindVulkan.cmake` was VS specific (#928) - [Win32] Bugfix: Bitness test in `FindVulkan.cmake` was VS specific (#928)
@ -167,11 +199,33 @@ information on what to include when reporting a bug.
- [Win32] Bugfix: Vulkan libraries have a new path as of SDK 1.0.42.0 (#956) - [Win32] Bugfix: Vulkan libraries have a new path as of SDK 1.0.42.0 (#956)
- [Win32] Bugfix: Monitors with no display devices were not enumerated (#960) - [Win32] Bugfix: Monitors with no display devices were not enumerated (#960)
- [Win32] Bugfix: Monitor events were not emitted (#784) - [Win32] Bugfix: Monitor events were not emitted (#784)
- [Win32] Bugfix: The Cygwin DLL was installed to the wrong directory (#1035)
- [Win32] Bugfix: Normalization of axis data via XInput was incorrect (#1045)
- [Win32] Bugfix: `glfw3native.h` would undefine a foreign `APIENTRY` (#1062)
- [Win32] Bugfix: Disabled cursor mode prevented use of caption buttons
(#650,#1071)
- [Win32] Bugfix: Returned key names did not match other platforms (#943)
- [Win32] Bugfix: Undecorated windows did not maximize to workarea (#899)
- [Win32] Bugfix: Window was resized twice when entering full screen (#1085)
- [X11] Moved to XI2 `XI_RawMotion` for disable cursor mode motion input (#125)
- [X11] Replaced `_GLFW_HAS_XF86VM` compile-time option with dynamic loading - [X11] Replaced `_GLFW_HAS_XF86VM` compile-time option with dynamic loading
- [X11] Bugfix: `glfwGetVideoMode` would segfault on Cygwin/X - [X11] Bugfix: `glfwGetVideoMode` would segfault on Cygwin/X
- [X11] Bugfix: Dynamic X11 library loading did not use full sonames (#941) - [X11] Bugfix: Dynamic X11 library loading did not use full sonames (#941)
- [X11] Bugfix: Window creation on 64-bit would read past top of stack (#951) - [X11] Bugfix: Window creation on 64-bit would read past top of stack (#951)
- [X11] Bugfix: XDND support had multiple non-conformance issues (#968)
- [X11] Bugfix: The RandR monitor path was disabled despite working RandR (#972)
- [X11] Bugfix: IM-duplicated key events would leak at low polling rates (#747)
- [X11] Bugfix: Gamma ramp setting via RandR did not validate ramp size
- [X11] Bugfix: Key name string encoding depended on current locale (#981,#983)
- [X11] Bugfix: Incremental reading of selections was not supported (#275)
- [X11] Bugfix: Selection I/O reported but did not support `COMPOUND_TEXT`
- [X11] Bugfix: Latin-1 text read from selections was not converted to UTF-8
- [X11] Bugfix: NVidia EGL would segfault if unloaded before closing the display
- [Linux] Moved to evdev for joystick input (#906,#1005)
- [Linux] Bugfix: Event processing did not detect joystick disconnection (#932) - [Linux] Bugfix: Event processing did not detect joystick disconnection (#932)
- [Linux] Bugfix: The joystick device path could be truncated (#1025)
- [Linux] Bugfix: `glfwInit` would fail if inotify creation failed (#833)
- [Linux] Bugfix: `strdup` was used without any required feature macro (#1055)
- [Cocoa] Added support for Vulkan window surface creation via - [Cocoa] Added support for Vulkan window surface creation via
[MoltenVK](https://moltengl.com/moltenvk/) (#870) [MoltenVK](https://moltengl.com/moltenvk/) (#870)
- [Cocoa] Added support for loading a `MainMenu.nib` when available - [Cocoa] Added support for loading a `MainMenu.nib` when available
@ -186,8 +240,23 @@ information on what to include when reporting a bug.
- [Cocoa] Bugfix: Leaving video mode with `glfwSetWindowMonitor` would set - [Cocoa] Bugfix: Leaving video mode with `glfwSetWindowMonitor` would set
incorrect position and size (#748) incorrect position and size (#748)
- [Cocoa] Bugfix: Iconified full screen windows could not be restored (#848) - [Cocoa] Bugfix: Iconified full screen windows could not be restored (#848)
- [X11] Moved to XI2 `XI_RawMotion` for disable cursor mode motion input (#125) - [Cocoa] Bugfix: Value range was ignored for joystick hats and buttons (#888)
- [Cocoa] Bugfix: Full screen framebuffer was incorrectly sized for some video
modes (#682)
- [Cocoa] Bugfix: A string object for IME was updated non-idiomatically (#1050)
- [Cocoa] Bugfix: A hidden or disabled cursor would become visible when a user
notification was shown (#971,#1028)
- [Cocoa] Bugfix: Some characters did not repeat due to Press and Hold (#1010)
- [Cocoa] Bugfix: Window title was lost when full screen or undecorated (#1082)
- [Cocoa] Bugfix: Window was resized twice when entering full screen (#1085)
- [Cocoa] Bugfix: Duplicate size events were not filtered (#1085)
- [WGL] Added support for `WGL_EXT_colorspace` for OpenGL ES contexts
- [WGL] Added support for `WGL_ARB_create_context_no_error`
- [GLX] Added support for `GLX_ARB_create_context_no_error`
- [GLX] Bugfix: Context creation could segfault if no GLXFBConfigs were
available (#1040)
- [EGL] Added support for `EGL_KHR_get_all_proc_addresses` (#871) - [EGL] Added support for `EGL_KHR_get_all_proc_addresses` (#871)
- [EGL] Added support for `EGL_KHR_context_flush_control`
- [EGL] Bugfix: The test for `EGL_RGB_BUFFER` was invalid - [EGL] Bugfix: The test for `EGL_RGB_BUFFER` was invalid
@ -214,20 +283,28 @@ GLFW exists because people around the world donated their time and lent their
skills. skills.
- Bobyshev Alexander - Bobyshev Alexander
- artblanc
- arturo
- Matt Arsenault - Matt Arsenault
- David Avedissian
- Keith Bauer - Keith Bauer
- John Bartholomew - John Bartholomew
- Niklas Behrens - Niklas Behrens
- Niklas Bergström - Niklas Bergström
- Denis Bernard
- Doug Binks - Doug Binks
- blanco - blanco
- Kyle Brenneman - Kyle Brenneman
- Rok Breulj
- Martin Capitanio - Martin Capitanio
- David Carlier
- Arturo Castro
- Chi-kwan Chan - Chi-kwan Chan
- Ian Clarkson
- Michał Cichoń
- Lambert Clara - Lambert Clara
- Yaron Cohen-Tal
- Omar Cornut
- Andrew Corrigan - Andrew Corrigan
- Bailey Cosier
- Noel Cower - Noel Cower
- Jason Daly - Jason Daly
- Jarrod Davis - Jarrod Davis
@ -236,9 +313,13 @@ skills.
- Michael Dickens - Michael Dickens
- Роман Донченко - Роман Донченко
- Mario Dorn - Mario Dorn
- Wolfgang Draxinger
- Jonathan Dummer - Jonathan Dummer
- Ralph Eastwood - Ralph Eastwood
- Fredrik Ehnbom
- Robin Eklind
- Siavash Eliasi - Siavash Eliasi
- Felipe Ferreira
- Michael Fogleman - Michael Fogleman
- Gerald Franz - Gerald Franz
- Mário Freitas - Mário Freitas
@ -246,6 +327,7 @@ skills.
- Marcus Geelnard - Marcus Geelnard
- Eloi Marín Gratacós - Eloi Marín Gratacós
- Stefan Gustavson - Stefan Gustavson
- Jonathan Hale
- Sylvain Hellegouarch - Sylvain Hellegouarch
- Matthew Henry - Matthew Henry
- heromyth - heromyth
@ -257,21 +339,24 @@ skills.
- Erik S. V. Jansson - Erik S. V. Jansson
- Toni Jovanoski - Toni Jovanoski
- Arseny Kapoulkine - Arseny Kapoulkine
- Cem Karan
- Osman Keskin - Osman Keskin
- Josh Kilmer
- Cameron King - Cameron King
- Peter Knut - Peter Knut
- Christoph Kubisch - Christoph Kubisch
- Yuri Kunde Schlesner
- Konstantin Käfer - Konstantin Käfer
- Eric Larson - Eric Larson
- Robin Leffmann - Robin Leffmann
- Glenn Lewis - Glenn Lewis
- Shane Liesegang - Shane Liesegang
- Eyal Lotem - Eyal Lotem
- Дмитри Малышев
- Martins Mozeiko
- Tristam MacDonald - Tristam MacDonald
- Hans Mackowiak - Hans Mackowiak
- Дмитри Малышев
- Zbigniew Mandziejewicz - Zbigniew Mandziejewicz
- Célestin Marot
- Kyle McDonald - Kyle McDonald
- David Medlock - David Medlock
- Bryce Mehring - Bryce Mehring
@ -283,40 +368,53 @@ skills.
- Bruce Mitchener - Bruce Mitchener
- Jack Moffitt - Jack Moffitt
- Jeff Molofee - Jeff Molofee
- Pierre Morel
- Jon Morton - Jon Morton
- Pierre Moulon - Pierre Moulon
- Martins Mozeiko
- Julian Møller - Julian Møller
- ndogxj
- Kristian Nielsen
- Kamil Nowakowski - Kamil Nowakowski
- Denis Ovod
- Ozzy - Ozzy
- Andri Pálsson - Andri Pálsson
- Peoro - Peoro
- Braden Pellett - Braden Pellett
- Christopher Pelloux
- Arturo J. Pérez - Arturo J. Pérez
- Anthony Pesch
- Orson Peters - Orson Peters
- Emmanuel Gil Peyrot - Emmanuel Gil Peyrot
- Cyril Pichard - Cyril Pichard
- Pieroman - Keith Pitt
- Stanislav Podgorskiy
- Alexandre Pretyman
- Philip Rideout - Philip Rideout
- Eddie Ringle
- Jorge Rodriguez - Jorge Rodriguez
- Ed Ropple - Ed Ropple
- Aleksey Rybalkin - Aleksey Rybalkin
- Riku Salminen - Riku Salminen
- Brandon Schaefer - Brandon Schaefer
- Sebastian Schuberth - Sebastian Schuberth
- Christian Sdunek
- Matt Sealey - Matt Sealey
- SephiRok
- Steve Sexton - Steve Sexton
- Systemcluster - Arkady Shapkin
- Yoshiki Shibukawa - Yoshiki Shibukawa
- Dmitri Shuralyov - Dmitri Shuralyov
- Daniel Skorupski - Daniel Skorupski
- Bradley Smith - Bradley Smith
- Patrick Snape - Patrick Snape
- Erlend Sogge Heggen
- Julian Squires - Julian Squires
- Johannes Stein - Johannes Stein
- Pontus Stenetorp
- Michael Stocker - Michael Stocker
- Justin Stoecker - Justin Stoecker
- Elviss Strazdins - Elviss Strazdins
- Paul Sultana
- Nathan Sweet - Nathan Sweet
- TTK-Bandit - TTK-Bandit
- Sergey Tikhomirov - Sergey Tikhomirov
@ -326,6 +424,7 @@ skills.
- Matthew Turner - Matthew Turner
- urraka - urraka
- Elias Vanderstuyft - Elias Vanderstuyft
- Stef Velzel
- Jari Vetoniemi - Jari Vetoniemi
- Ricardo Vieira - Ricardo Vieira
- Nicholas Vitovitch - Nicholas Vitovitch
@ -335,7 +434,8 @@ skills.
- Xo Wang - Xo Wang
- Jay Weisskopf - Jay Weisskopf
- Frank Wille - Frank Wille
- yuriks - Ryogo Yoshimura
- Andrey Zholos
- Santi Zupancic - Santi Zupancic
- Jonas Ådahl - Jonas Ådahl
- Lasse Öörni - Lasse Öörni

2
deps/linmath.h vendored
View File

@ -192,7 +192,7 @@ static inline void mat4x4_rotate(mat4x4 R, mat4x4 M, float x, float y, float z,
vec3 u = {x, y, z}; vec3 u = {x, y, z};
if(vec3_len(u) > 1e-4) { if(vec3_len(u) > 1e-4) {
mat4x4 T, C, S = {0}; mat4x4 T, C, S = {{0}};
vec3_norm(u, u); vec3_norm(u, u);
mat4x4_from_vec3_mul_outer(T, u, u); mat4x4_from_vec3_mul_outer(T, u, u);

2777
deps/nuklear.h vendored

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
/* /*
* Nuklear - v1.32.0 - public domain * Nuklear - v1.32.0 - public domain
* no warrenty implied; use at your own risk. * no warrenty implied; use at your own risk.
* authored from 2015-2016 by Micha Mettke * authored from 2015-2017 by Micha Mettke
*/ */
/* /*
* ============================================================== * ==============================================================
@ -24,7 +24,7 @@ NK_API void nk_glfw3_font_stash_begin(struct nk_font_atlas **atl
NK_API void nk_glfw3_font_stash_end(void); NK_API void nk_glfw3_font_stash_end(void);
NK_API void nk_glfw3_new_frame(void); NK_API void nk_glfw3_new_frame(void);
NK_API void nk_glfw3_render(enum nk_anti_aliasing , int max_vertex_buffer, int max_element_buffer); NK_API void nk_glfw3_render(enum nk_anti_aliasing);
NK_API void nk_glfw3_shutdown(void); NK_API void nk_glfw3_shutdown(void);
NK_API void nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint); NK_API void nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint);
@ -44,6 +44,12 @@ NK_API void nk_gflw3_scroll_callback(GLFWwindow *win, double xof
#ifndef NK_GLFW_TEXT_MAX #ifndef NK_GLFW_TEXT_MAX
#define NK_GLFW_TEXT_MAX 256 #define NK_GLFW_TEXT_MAX 256
#endif #endif
#ifndef NK_GLFW_DOUBLE_CLICK_LO
#define NK_GLFW_DOUBLE_CLICK_LO 0.02
#endif
#ifndef NK_GLFW_DOUBLE_CLICK_HI
#define NK_GLFW_DOUBLE_CLICK_HI 0.2
#endif
struct nk_glfw_device { struct nk_glfw_device {
struct nk_buffer cmds; struct nk_buffer cmds;
@ -67,7 +73,8 @@ static struct nk_glfw {
struct nk_vec2 fb_scale; struct nk_vec2 fb_scale;
unsigned int text[NK_GLFW_TEXT_MAX]; unsigned int text[NK_GLFW_TEXT_MAX];
int text_len; int text_len;
float scroll; struct nk_vec2 scroll;
double last_button_click;
} glfw; } glfw;
NK_INTERN void NK_INTERN void
@ -83,7 +90,7 @@ nk_glfw3_device_upload_atlas(const void *image, int width, int height)
} }
NK_API void NK_API void
nk_glfw3_render(enum nk_anti_aliasing AA, int max_vertex_buffer, int max_element_buffer) nk_glfw3_render(enum nk_anti_aliasing AA)
{ {
/* setup global state */ /* setup global state */
struct nk_glfw_device *dev = &glfw.ogl; struct nk_glfw_device *dev = &glfw.ogl;
@ -200,7 +207,22 @@ NK_API void
nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff) nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff)
{ {
(void)win; (void)xoff; (void)win; (void)xoff;
glfw.scroll += (float)yoff; glfw.scroll.x += (float)xoff;
glfw.scroll.y += (float)yoff;
}
NK_API void
nk_glfw3_mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
double x, y;
if (button != GLFW_MOUSE_BUTTON_LEFT) return;
glfwGetCursorPos(window, &x, &y);
if (action == GLFW_PRESS) {
double dt = glfwGetTime() - glfw.last_button_click;
if (dt > NK_GLFW_DOUBLE_CLICK_LO && dt < NK_GLFW_DOUBLE_CLICK_HI)
nk_input_button(&glfw.ctx, NK_BUTTON_DOUBLE, (int)x, (int)y, nk_true);
glfw.last_button_click = glfwGetTime();
} else nk_input_button(&glfw.ctx, NK_BUTTON_DOUBLE, (int)x, (int)y, nk_false);
} }
NK_INTERN void NK_INTERN void
@ -232,8 +254,8 @@ nk_glfw3_init(GLFWwindow *win, enum nk_glfw_init_state init_state)
if (init_state == NK_GLFW3_INSTALL_CALLBACKS) { if (init_state == NK_GLFW3_INSTALL_CALLBACKS) {
glfwSetScrollCallback(win, nk_gflw3_scroll_callback); glfwSetScrollCallback(win, nk_gflw3_scroll_callback);
glfwSetCharCallback(win, nk_glfw3_char_callback); glfwSetCharCallback(win, nk_glfw3_char_callback);
glfwSetMouseButtonCallback(win, nk_glfw3_mouse_button_callback);
} }
nk_init_default(&glfw.ctx, 0); nk_init_default(&glfw.ctx, 0);
glfw.ctx.clip.copy = nk_glfw3_clipbard_copy; glfw.ctx.clip.copy = nk_glfw3_clipbard_copy;
glfw.ctx.clip.paste = nk_glfw3_clipbard_paste; glfw.ctx.clip.paste = nk_glfw3_clipbard_paste;
@ -333,7 +355,7 @@ nk_glfw3_new_frame(void)
nk_input_scroll(ctx, glfw.scroll); nk_input_scroll(ctx, glfw.scroll);
nk_input_end(&glfw.ctx); nk_input_end(&glfw.ctx);
glfw.text_len = 0; glfw.text_len = 0;
glfw.scroll = 0; glfw.scroll = nk_vec2(0,0);
} }
NK_API NK_API

View File

@ -23,7 +23,7 @@ if (GLFW_DOCUMENT_INTERNALS)
endif() endif()
foreach(arg ${glfw_DOCS_SOURCES}) foreach(arg ${glfw_DOCS_SOURCES})
set(GLFW_DOCS_SOURCES "${GLFW_DOCS_SOURCES} ${arg}") set(GLFW_DOCS_SOURCES "${GLFW_DOCS_SOURCES} \\\n\"${arg}\"")
endforeach() endforeach()
configure_file(Doxyfile.in Doxyfile @ONLY) configure_file(Doxyfile.in Doxyfile @ONLY)

View File

@ -336,6 +336,9 @@ other bugs and features to work on.
If the patch fixes a bug introduced after the last release, it should not get If the patch fixes a bug introduced after the last release, it should not get
a change log entry. a change log entry.
If you haven't already, read the excellent article [How to Write a Git Commit
Message](https://chris.beams.io/posts/git-commit/).
## Contributing a feature ## Contributing a feature
@ -373,6 +376,9 @@ If it adds a new OpenGL, OpenGL ES or Vulkan option or extension, support
for it must be added to `tests/glfwinfo.c` and the behavior of the library when for it must be added to `tests/glfwinfo.c` and the behavior of the library when
the extension is missing documented in `docs/compat.dox`. the extension is missing documented in `docs/compat.dox`.
If you haven't already, read the excellent article [How to Write a Git Commit
Message](https://chris.beams.io/posts/git-commit/).
Features will not be rejected because they don't include all the above parts, Features will not be rejected because they don't include all the above parts,
but please keep in mind that maintainer time is finite and that there are many but please keep in mind that maintainer time is finite and that there are many
other features and bugs to work on. other features and bugs to work on.

View File

@ -52,7 +52,7 @@ PROJECT_LOGO =
# If a relative path is entered, it will be relative to the location # If a relative path is entered, it will be relative to the location
# where doxygen was started. If left blank the current directory will be used. # where doxygen was started. If left blank the current directory will be used.
OUTPUT_DIRECTORY = @GLFW_BINARY_DIR@/docs OUTPUT_DIRECTORY = "@GLFW_BINARY_DIR@/docs"
# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
# 4096 sub-directories (in 2 levels) under the output directory of each output # 4096 sub-directories (in 2 levels) under the output directory of each output
@ -476,7 +476,7 @@ SORT_MEMBER_DOCS = NO
# by member name. If set to NO (the default) the members will appear in # by member name. If set to NO (the default) the members will appear in
# declaration order. # declaration order.
SORT_BRIEF_DOCS = YES SORT_BRIEF_DOCS = NO
# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
# will sort the (brief and detailed) documentation of class members so that # will sort the (brief and detailed) documentation of class members so that
@ -651,7 +651,7 @@ WARN_FORMAT = "$file:$line: $text"
# and error messages should be written. If left blank the output is written # and error messages should be written. If left blank the output is written
# to stderr. # to stderr.
WARN_LOGFILE = @GLFW_BINARY_DIR@/docs/warnings.txt WARN_LOGFILE = "@GLFW_BINARY_DIR@/docs/warnings.txt"
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# configuration options related to the input files # configuration options related to the input files
@ -722,7 +722,7 @@ EXCLUDE_SYMBOLS = APIENTRY GLFWAPI
# directories that contain example code fragments that are included (see # directories that contain example code fragments that are included (see
# the \include command). # the \include command).
EXAMPLE_PATH = @GLFW_SOURCE_DIR@/examples EXAMPLE_PATH = "@GLFW_SOURCE_DIR@/examples"
# If the value of the EXAMPLE_PATH tag contains directories, you can use the # If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
@ -898,13 +898,13 @@ HTML_FILE_EXTENSION = .html
# have to redo this when upgrading to a newer version of doxygen or when # have to redo this when upgrading to a newer version of doxygen or when
# changing the value of configuration settings such as GENERATE_TREEVIEW! # changing the value of configuration settings such as GENERATE_TREEVIEW!
HTML_HEADER = @GLFW_SOURCE_DIR@/docs/header.html HTML_HEADER = "@GLFW_SOURCE_DIR@/docs/header.html"
# The HTML_FOOTER tag can be used to specify a personal HTML footer for # The HTML_FOOTER tag can be used to specify a personal HTML footer for
# each generated HTML page. If it is left blank doxygen will generate a # each generated HTML page. If it is left blank doxygen will generate a
# standard footer. # standard footer.
HTML_FOOTER = @GLFW_SOURCE_DIR@/docs/footer.html HTML_FOOTER = "@GLFW_SOURCE_DIR@/docs/footer.html"
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading # The HTML_STYLESHEET tag can be used to specify a user-defined cascading
# style sheet that is used by each HTML page. It can be used to # style sheet that is used by each HTML page. It can be used to
@ -923,7 +923,7 @@ HTML_STYLESHEET =
# robust against future updates. Doxygen will copy the style sheet file to # robust against future updates. Doxygen will copy the style sheet file to
# the output directory. # the output directory.
HTML_EXTRA_STYLESHEET = @GLFW_SOURCE_DIR@/docs/extra.css HTML_EXTRA_STYLESHEET = "@GLFW_SOURCE_DIR@/docs/extra.css"
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the HTML output directory. Note # other source files which should be copied to the HTML output directory. Note
@ -932,7 +932,7 @@ HTML_EXTRA_STYLESHEET = @GLFW_SOURCE_DIR@/docs/extra.css
# files. In the HTML_STYLESHEET file, use the file name only. Also note that # files. In the HTML_STYLESHEET file, use the file name only. Also note that
# the files will be copied as-is; there are no commands or markers available. # the files will be copied as-is; there are no commands or markers available.
HTML_EXTRA_FILES = @GLFW_SOURCE_DIR@/docs/spaces.svg HTML_EXTRA_FILES = "@GLFW_SOURCE_DIR@/docs/spaces.svg"
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
# Doxygen will adjust the colors in the style sheet and background images # Doxygen will adjust the colors in the style sheet and background images

View File

@ -15,27 +15,27 @@ specific compiler of your chosen development environment. The compilation
and linking process should be explained in your C programming material and in and linking process should be explained in your C programming material and in
the documentation for your development environment. the documentation for your development environment.
@section build_include Including the GLFW header file @section build_include Including the GLFW header file
In the source files of your application where you use OpenGL or GLFW, you should You should include the GLFW header in the source files where you use OpenGL or
include the GLFW header file, i.e.: GLFW.
@code @code
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
@endcode @endcode
The GLFW header declares the GLFW API and by default also includes the OpenGL This header declares the GLFW API and by default also includes the OpenGL header
header of your development environment, which in turn defines all the constants, from your development environment. See below for how to control this.
types and function prototypes of the OpenGL API.
The GLFW header also defines everything necessary for your OpenGL header to The GLFW header also defines any platform-specific macros needed by your OpenGL
function. For example, under Windows you are normally required to include header, so it can be included without needing any window system headers.
`windows.h` before the OpenGL header, which would pollute your code namespace
with the entire Win32 API.
Instead, the GLFW header takes care of this for you, not by including For example, under Windows you are normally required to include `windows.h`
`windows.h`, but by duplicating only the very few necessary parts of it. It before the OpenGL header, which would bring in the whole Win32 API. The GLFW
does this only when needed, so if `windows.h` _is_ included, the GLFW header header duplicates the small number of macros needed.
It does this only when needed, so if `windows.h` _is_ included, the GLFW header
does not try to redefine those symbols. The reverse is not true, i.e. does not try to redefine those symbols. The reverse is not true, i.e.
`windows.h` cannot cope if any of its symbols have already been defined. `windows.h` cannot cope if any of its symbols have already been defined.
@ -49,8 +49,21 @@ In other words:
If you are using an OpenGL extension loading library such as If you are using an OpenGL extension loading library such as
[glad](https://github.com/Dav1dde/glad), the extension loader header should [glad](https://github.com/Dav1dde/glad), the extension loader header should
either be included _before_ the GLFW one, or the @ref GLFW_INCLUDE_NONE macro be included _before_ the GLFW one.
(described below) should be defined.
@code
#include <glad/glad.h>
#include <GLFW/glfw3.h>
@endcode
Alternatively the @ref GLFW_INCLUDE_NONE macro (described below) can be used to
prevent the GLFW header from including the OpenGL header.
@code
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <glad/glad.h>
@endcode
@subsection build_macros GLFW header option macros @subsection build_macros GLFW header option macros
@ -170,8 +183,8 @@ This section is about using CMake to compile and link GLFW along with your
application. If you want to use an installed binary instead, see @ref application. If you want to use an installed binary instead, see @ref
build_link_cmake_package. build_link_cmake_package.
With just a few changes to your `CMakeLists.txt` you can have the GLFW source With a few changes to your `CMakeLists.txt` you can have the GLFW source tree
tree built along with your application. built along with your application.
When including GLFW as part of your build, you probably don't want to build the 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 GLFW tests, examples and documentation. To disable these, set the corresponding
@ -236,13 +249,22 @@ This section is about using CMake to link GLFW after it has been built and
installed. If you want to build it along with your application instead, see installed. If you want to build it along with your application instead, see
@ref build_link_cmake_source. @ref build_link_cmake_source.
With just a few changes to your `CMakeLists.txt`, you can locate the package and With a few changes to your `CMakeLists.txt` you can locate the package and
target files generated when GLFW is installed. target files generated when GLFW is installed.
@code{.cmake} @code{.cmake}
find_package(glfw3 3.3 REQUIRED) find_package(glfw3 3.3 REQUIRED)
@endcode @endcode
Once GLFW has been added to the project, link against it with the `glfw` target.
This adds all link-time dependencies of GLFW as it is currently configured,
the include directory for the GLFW header and, when applicable, the @ref
GLFW_DLL macro.
@code{.cmake}
target_link_libraries(myapp glfw)
@endcode
Note that the dependencies do not include OpenGL or GLU, as GLFW loads any Note that the dependencies do not include OpenGL or GLU, as GLFW loads any
OpenGL, OpenGL ES or Vulkan libraries it needs at runtime and does not use GLU. OpenGL, OpenGL ES or Vulkan libraries it needs at runtime and does not use GLU.
If your application calls OpenGL directly, instead of using a modern If your application calls OpenGL directly, instead of using a modern
@ -290,8 +312,8 @@ GLFW library may look like this:
cc `pkg-config --cflags glfw3` -o myprog myprog.c `pkg-config --static --libs glfw3` cc `pkg-config --cflags glfw3` -o myprog myprog.c `pkg-config --static --libs glfw3`
@endcode @endcode
If you are using the shared version of the GLFW library, simply omit the If you are using the shared version of the GLFW library, omit the `--static`
`--static` flag. flag.
@code{.sh} @code{.sh}
cc `pkg-config --cflags glfw3` -o myprog myprog.c `pkg-config --libs glfw3` cc `pkg-config --cflags glfw3` -o myprog myprog.c `pkg-config --libs glfw3`
@ -328,8 +350,8 @@ cc `pkg-config --cflags glfw3 glu` -o myprog myprog.c `pkg-config --static --lib
@subsection build_link_xcode With Xcode on macOS @subsection build_link_xcode With Xcode on macOS
If you are using the dynamic library version of GLFW, simply add it to the If you are using the dynamic library version of GLFW, add it to the project
project dependencies. dependencies.
If you are using the static library version of GLFW, add it and the Cocoa, If you are using the static library version of GLFW, add it and the Cocoa,
OpenGL, IOKit and CoreVideo frameworks to the project as dependencies. They can OpenGL, IOKit and CoreVideo frameworks to the project as dependencies. They can

View File

@ -80,6 +80,11 @@ GLFW uses the XInput2 extension to provide raw, non-accelerated mouse motion
when the cursor is disabled. If the running X server does not support this when the cursor is disabled. If the running X server does not support this
extension, regular accelerated mouse motion will be used. extension, regular accelerated mouse motion will be used.
GLFW uses both the XRender extension and the compositing manager to support
transparent window framebuffers. If the running X server does not support this
extension or there is no running compositing manager, the
`GLFW_TRANSPARENT_FRAMEBUFFER` framebuffer hint will have no effect.
@section compat_glx GLX extensions @section compat_glx GLX extensions
@ -164,21 +169,21 @@ extensions to provide support for sRGB framebuffers. Where both of these
extension are unavailable, the `GLFW_SRGB_CAPABLE` hint will have no effect. extension are unavailable, the `GLFW_SRGB_CAPABLE` hint will have no effect.
@section compat_osx OpenGL 3.2 and later on macOS @section compat_osx OpenGL on macOS
Support for OpenGL 3.2 and above was introduced with OS X 10.7 and even then Support for OpenGL 3.2 and above was introduced with OS X 10.7 and even then
only forward-compatible, core profile contexts are supported. Support for only forward-compatible, core profile contexts are supported. Support for
OpenGL 4.1 was introduced with OS X 10.9, also limited to forward-compatible, OpenGL 4.1 was introduced with OS X 10.9, also limited to forward-compatible,
core profile contexts. There is also still no mechanism for requesting debug core profile contexts. There is also still no mechanism for requesting debug
contexts. Versions of Mac OS X earlier than 10.7 support at most OpenGL contexts or no-error contexts. Versions of Mac OS X earlier than 10.7 support
version 2.1. at most OpenGL version 2.1.
Because of this, on OS X 10.7 and later, the `GLFW_CONTEXT_VERSION_MAJOR` and Because of this, on OS X 10.7 and later, the `GLFW_CONTEXT_VERSION_MAJOR` and
`GLFW_CONTEXT_VERSION_MINOR` hints will cause @ref glfwCreateWindow to fail if `GLFW_CONTEXT_VERSION_MINOR` hints will cause @ref glfwCreateWindow to fail if
given version 3.0 or 3.1, the `GLFW_OPENGL_FORWARD_COMPAT` hint must be set to given version 3.0 or 3.1. The `GLFW_OPENGL_FORWARD_COMPAT` hint must be set to
`GLFW_TRUE` and the `GLFW_OPENGL_PROFILE` hint must be set to `GLFW_TRUE` and the `GLFW_OPENGL_PROFILE` hint must be set to
`GLFW_OPENGL_CORE_PROFILE` when creating OpenGL 3.2 and later contexts and the `GLFW_OPENGL_CORE_PROFILE` when creating OpenGL 3.2 and later contexts. The
`GLFW_OPENGL_DEBUG_CONTEXT` hint is ignored. `GLFW_OPENGL_DEBUG_CONTEXT` and `GLFW_CONTEXT_NO_ERROR` hints are ignored.
Also, on Mac OS X 10.6 and below, the `GLFW_CONTEXT_VERSION_MAJOR` and Also, on Mac OS X 10.6 and below, the `GLFW_CONTEXT_VERSION_MAJOR` and
`GLFW_CONTEXT_VERSION_MINOR` hints will fail if given a version above 2.1, `GLFW_CONTEXT_VERSION_MINOR` hints will fail if given a version above 2.1,

View File

@ -13,7 +13,7 @@ build applications that use GLFW, see @ref build_guide.
GLFW uses [CMake](http://www.cmake.org/) to generate project files or makefiles GLFW uses [CMake](http://www.cmake.org/) to generate project files or makefiles
for a particular development environment. If you are on a Unix-like system such for a particular development environment. If you are on a Unix-like system such
as Linux or FreeBSD or have a package system like Fink, MacPorts, Cygwin or as Linux or FreeBSD or have a package system like Fink, MacPorts, Cygwin or
Homebrew, you can simply install its CMake package. If not, you can download Homebrew, you can install its CMake package. If not, you can download
installers for Windows and macOS from the installers for Windows and macOS from the
[CMake website](http://www.cmake.org/). [CMake website](http://www.cmake.org/).
@ -33,9 +33,9 @@ below.
@subsubsection compile_deps_msvc Dependencies for Visual C++ on Windows @subsubsection compile_deps_msvc Dependencies for Visual C++ on Windows
The Microsoft Platform SDK that is installed along with Visual C++ already The Windows SDK bundled with Visual C++ already contains all the necessary
contains all the necessary headers, link libraries and tools except for CMake. headers, link libraries and tools except for CMake. Move on to @ref
Move on to @ref compile_generate. compile_generate.
@subsubsection compile_deps_mingw Dependencies for MinGW or MinGW-w64 on Windows @subsubsection compile_deps_mingw Dependencies for MinGW or MinGW-w64 on Windows
@ -265,6 +265,12 @@ If you are linking the Vulkan loader statically into your application then you
must also define @b _GLFW_VULKAN_STATIC. Otherwise, GLFW will attempt to use the must also define @b _GLFW_VULKAN_STATIC. Otherwise, GLFW will attempt to use the
external version. external version.
If you are using a custom name for the Vulkan, EGL, GLX, OSMesa, OpenGL, GLESv1
or GLESv2 library, you can override the default names by defining those you need
of @b _GLFW_VULKAN_LIBRARY, @b _GLFW_EGL_LIBRARY, @b _GLFW_GLX_LIBRARY, @b
_GLFW_OSMESA_LIBRARY, @b _GLFW_OPENGL_LIBRARY, @b _GLFW_GLESV1_LIBRARY and @b
_GLFW_GLESV2_LIBRARY. Otherwise, GLFW will use the built-in default names.
For the EGL context creation API, the following options are available: For the EGL context creation API, the following options are available:
- @b _GLFW_USE_EGLPLATFORM_H to use `EGL/eglplatform.h` for native handle - @b _GLFW_USE_EGLPLATFORM_H to use `EGL/eglplatform.h` for native handle

View File

@ -61,7 +61,7 @@ information. The name and number of this chapter unfortunately varies between
versions and APIs, but has at times been named _Shared Objects and Multiple versions and APIs, but has at times been named _Shared Objects and Multiple
Contexts_. Contexts_.
GLFW comes with a simple object sharing test program called `sharing`. GLFW comes with a barebones object sharing example program called `sharing`.
@subsection context_offscreen Offscreen contexts @subsection context_offscreen Offscreen contexts
@ -103,6 +103,9 @@ Before you can make OpenGL or OpenGL ES calls, you need to have a current
context of the correct type. A context can only be current for a single thread context of the correct type. A context can only be current for a single thread
at a time, and a thread can only have a single context current at a time. at a time, and a thread can only have a single context current at a time.
When moving a context between threads, you must make it non-current on the old
thread before making it current on the new one.
The context of a window is made current with @ref glfwMakeContextCurrent. The context of a window is made current with @ref glfwMakeContextCurrent.
@code @code
@ -126,8 +129,7 @@ error.
@section context_swap Buffer swapping @section context_swap Buffer swapping
Buffer swapping is part of the window and framebuffer, not the context. See See @ref buffer_swap in the window guide.
@ref buffer_swap.
@section context_glext OpenGL and OpenGL ES extensions @section context_glext OpenGL and OpenGL ES extensions

File diff suppressed because one or more lines are too long

View File

@ -76,8 +76,34 @@
border:2px solid desaturate(darken(@base-color, 10%), 20%); border:2px solid desaturate(darken(@base-color, 10%), 20%);
} }
.sm-dox,.sm-dox a,.sm-dox a:focus,.sm-dox a:active,.sm-dox a:hover,.sm-dox a.highlighted,.sm-dox ul a:hover {
background:none;
text-shadow:none;
}
#navrow1,#navrow2,#navrow3,#navrow4,.tablist a,.tablist a:visited,.tablist a:hover,.tablist li,.tablist li.current a,.memdoc,dl.reflist dd,div.toc li,.ah,span.lineno,span.lineno a,span.lineno a:hover,.note code,.pre code,.post code,.invariant code,.warning code,.attention code,.deprecated code,.bug code,.todo code,.test code,.doxtable code { .sm-dox a span.sub-arrow {
border-color:@navbar-link-color transparent transparent transparent;
}
.sm-dox a span.sub-arrow:active,.sm-dox a span.sub-arrow:focus,.sm-dox a span.sub-arrow:hover,.sm-dox a:hover span.sub-arrow {
border-color:@default-link-color transparent transparent transparent;
}
.sm-dox ul a span.sub-arrow:active,.sm-dox ul a span.sub-arrow:focus,.sm-dox ul a span.sub-arrow:hover,.sm-dox ul a:hover span.sub-arrow {
border-color:transparent transparent transparent @default-link-color;
}
.sm-dox ul a:hover {
background:@header-footer-link-color;
text-shadow:none;
}
.sm-dox ul.sm-nowrap a {
color:@default-text-color;
text-shadow:none;
}
#main-nav,#main-menu,#main-menu a,#main-menu a:visited,#main-menu a:hover,#main-menu li,.memdoc,dl.reflist dd,div.toc li,.ah,span.lineno,span.lineno a,span.lineno a:hover,.note code,.pre code,.post code,.invariant code,.warning code,.attention code,.deprecated code,.bug code,.todo code,.test code,.doxtable code {
background:none; background:none;
} }
@ -85,7 +111,7 @@
border:none; border:none;
} }
.tablist a,.tablist a:visited,.tablist a:hover,.tablist li,.tablist li.current a,.reflist dt a.el,.levels span,.directory .levels span { #main-menu a,#main-menu a:visited,#main-menu a:hover,#main-menu li,.reflist dt a.el,.levels span,.directory .levels span {
text-shadow:none; text-shadow:none;
} }
@ -183,28 +209,45 @@ address.footer {
background:@header-footer-link-color; background:@header-footer-link-color;
} }
#navrow1,#navrow2,#navrow3,#navrow4 { #main-nav {
max-width:960px;
min-width:800px;
margin:0 auto;
font-size:13px;
}
#main-menu {
max-width:920px; max-width:920px;
min-width:800px; min-width:800px;
margin:0 auto; margin:0 auto;
font-size:13px; font-size:13px;
} }
.tablist { .memtitle {
display:none;
}
.memproto,.memname {
font-weight:bold;
text-shadow:none;
}
#main-menu {
height:36px; height:36px;
display:block; display:block;
position:relative; position:relative;
} }
.tablist a,.tablist a:visited,.tablist a:hover,.tablist li,.tablist li.current a { #main-menu a,#main-menu a:visited,#main-menu a:hover,#main-menu li {
color:@navbar-link-color; color:@navbar-link-color;
} }
.tablist li.current a { #main-menu li ul.sm-nowrap li a {
background:linear-gradient(to bottom,@tab-background-color2 0%,@tab-background-color1 100%); color:@default-text-color;
box-shadow:inset 0 0 32px @tab-background-color1; }
text-shadow:0 -1px 1px darken(@tab-background-color1, 15%);
color:@tab-text-color; #main-menu li ul.sm-nowrap li a:hover {
color:@default-link-color;
} }
.contents { .contents {
@ -222,6 +265,7 @@ table.doxtable th,dl.reflist dt {
background:linear-gradient(to bottom,@table-background-color2 0%,@table-background-color1 100%); background:linear-gradient(to bottom,@table-background-color2 0%,@table-background-color1 100%);
box-shadow:inset 0 0 32px @table-background-color1; box-shadow:inset 0 0 32px @table-background-color1;
text-shadow:0 -1px 1px darken(@table-background-color1, 15%); text-shadow:0 -1px 1px darken(@table-background-color1, 15%);
text-align:left;
color:@table-text-color; color:@table-text-color;
} }
@ -285,7 +329,7 @@ table.doxtable {
border-radius:4px; border-radius:4px;
} }
a,a:hover,a:visited,a:visited:hover,.contents a:visited,.el,a.el:visited,#glfwhome:hover,.tablist a:hover,span.lineno a:hover { a,a:hover,a:visited,a:visited:hover,.contents a:visited,.el,a.el:visited,#glfwhome:hover,#main-menu a:hover,span.lineno a:hover {
color:@default-link-color; color:@default-link-color;
text-decoration:none; text-decoration:none;
} }

View File

@ -25,7 +25,6 @@ $extrastylesheet
<ul class="glfwnavbar"> <ul class="glfwnavbar">
<li><a href="http://www.glfw.org/documentation.html">Documentation</a></li> <li><a href="http://www.glfw.org/documentation.html">Documentation</a></li>
<li><a href="http://www.glfw.org/download.html">Download</a></li> <li><a href="http://www.glfw.org/download.html">Download</a></li>
<li><a href="http://www.glfw.org/media.html">Media</a></li>
<li><a href="http://www.glfw.org/community.html">Community</a></li> <li><a href="http://www.glfw.org/community.html">Community</a></li>
</ul> </ul>
</div> </div>

View File

@ -170,6 +170,19 @@ When sticky keys mode is enabled, the pollable state of a key will remain
it has been polled, if a key release event had been processed in the meantime, it has been polled, if a key release event had been processed in the meantime,
the state will reset to `GLFW_RELEASE`, otherwise it will remain `GLFW_PRESS`. the state will reset to `GLFW_RELEASE`, otherwise it will remain `GLFW_PRESS`.
@anchor GLFW_LOCK_KEY_MODS
If you wish to know what the state of the Caps Lock and Num Lock keys was when
input events were generated, set the `GLFW_LOCK_KEY_MODS` input mode.
@code
glfwSetInputMode(window, GLFW_LOCK_KEY_MODS, 1);
@endcode
When this input mode is enabled, any callback that receives
[modifier bits](@ref mods) will have the @ref GLFW_MOD_CAPS_LOCK bit set if Caps
Lock was on when the event occurred and the @ref GLFW_MOD_NUM_LOCK bit set if
Num Lock was on.
The `GLFW_KEY_LAST` constant holds the highest value of any The `GLFW_KEY_LAST` constant holds the highest value of any
[named key](@ref keys). [named key](@ref keys).
@ -181,15 +194,12 @@ GLFW supports text input in the form of a stream of
operating system text input system. Unlike key input, text input obeys keyboard operating system text input system. Unlike key input, text input obeys keyboard
layouts and modifier keys and supports composing characters using layouts and modifier keys and supports composing characters using
[dead keys](https://en.wikipedia.org/wiki/Dead_key). Once received, you can [dead keys](https://en.wikipedia.org/wiki/Dead_key). Once received, you can
encode the code points into encode the code points into UTF-8 or any other encoding you prefer.
[UTF-8](https://en.wikipedia.org/wiki/UTF-8) or any other encoding you prefer.
Because an `unsigned int` is 32 bits long on all platforms supported by GLFW, Because an `unsigned int` is 32 bits long on all platforms supported by GLFW,
you can treat the code point argument as native endian you can treat the code point argument as native endian UTF-32.
[UTF-32](https://en.wikipedia.org/wiki/UTF-32).
There are two callbacks for receiving Unicode code points. If you wish to If you wish to offer regular text input, set a character callback.
offer regular text input, set a character callback.
@code @code
glfwSetCharCallback(window, character_callback); glfwSetCharCallback(window, character_callback);
@ -205,23 +215,6 @@ void character_callback(GLFWwindow* window, unsigned int codepoint)
} }
@endcode @endcode
If you wish to receive even those Unicode code points generated with modifier
key combinations that a plain text field would ignore, or just want to know
exactly what modifier keys were used, set a character with modifiers callback.
@code
glfwSetCharModsCallback(window, charmods_callback);
@endcode
The callback function receives Unicode code points and
[modifier bits](@ref mods).
@code
void charmods_callback(GLFWwindow* window, unsigned int codepoint, int mods)
{
}
@endcode
@subsection input_key_name Key names @subsection input_key_name Key names
@ -299,8 +292,8 @@ is provided normally via both the cursor position callback and through polling.
other features of GLFW. It is not supported and will not work as robustly as other features of GLFW. It is not supported and will not work as robustly as
`GLFW_CURSOR_DISABLED`. `GLFW_CURSOR_DISABLED`.
If you just wish the cursor to become hidden when it is over a window, set If you only wish the cursor to become hidden when it is over a window but still
the cursor mode to `GLFW_CURSOR_HIDDEN`. want it to behave normally, set the cursor mode to `GLFW_CURSOR_HIDDEN`.
@code @code
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
@ -346,8 +339,8 @@ If cursor creation fails, `NULL` will be returned, so it is necessary to check
the return value. the return value.
The image data is 32-bit, little-endian, non-premultiplied RGBA, i.e. eight bits The image data is 32-bit, little-endian, non-premultiplied RGBA, i.e. eight bits
per channel. The pixels are arranged canonically as sequential rows, starting per channel with the red channel first. The pixels are arranged canonically as
from the top-left corner. sequential rows, starting from the top-left corner.
@subsubsection cursor_standard Standard cursor creation @subsubsection cursor_standard Standard cursor creation
@ -503,7 +496,7 @@ void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
} }
@endcode @endcode
A simple mouse wheel, being vertical, provides offsets along the Y-axis. A normal mouse wheel, being vertical, provides offsets along the Y-axis.
@section joystick Joystick input @section joystick Joystick input
@ -653,6 +646,158 @@ called by joystick functions. The function will then return whatever it
returns for a disconnected joystick. returns for a disconnected joystick.
@subsection gamepad Gamepad input
The joystick functions provide unlabeled axes, buttons and hats, with no
indication of where they are located on the device. Their order may also vary
between platforms even with the same device.
To solve this problem the SDL community crowdsourced the
[SDL_GameControllerDB](https://github.com/gabomdq/SDL_GameControllerDB) project,
a database of mappings from many different devices to an Xbox-like gamepad.
GLFW supports this mapping format and contains a copy of the mappings
available at the time of release. See @ref gamepad_mapping for how to update
this at runtime. Mappings will be assigned to joysticks automatically any time
a joystick is connected or the mappings are updated.
You can check whether a joystick is both present and has a gamepad mapping with
@ref glfwJoystickIsGamepad.
@code
if (glfwJoystickIsGamepad(GLFW_JOYSTICK_2))
{
// Use as gamepad
}
@endcode
If you are only interested in gamepad input you can use this function instead of
@ref glfwJoystickPresent.
You can query the human-readable name provided by the gamepad mapping with @ref
glfwGetGamepadName. This may or may not be the same as the
[joystick name](@ref joystick_name).
@code
const char* name = glfwGetGamepadName(GLFW_JOYSTICK_7);
@endcode
To retrieve the gamepad state of a joystick, call @ref glfwGetGamepadState.
@code
GLFWgamepadstate state;
if (glfwGetGamepadState(GLFW_JOYSTICK_3, &state))
{
if (state.buttons[GLFW_GAMEPAD_BUTTON_A])
{
input_jump();
}
input_speed(state.axes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER]);
}
@endcode
The @ref GLFWgamepadstate struct has two arrays; one for button states and one
for axis states. The values for each button and axis are the same as for the
@ref glfwGetJoystickButtons and @ref glfwGetJoystickAxes functions, i.e.
`GLFW_PRESS` or `GLFW_RELEASE` for buttons and -1.0 to 1.0 inclusive for axes.
The sizes of the arrays and the positions within each array are fixed.
The [button indices](@ref gamepad_buttons) are `GLFW_GAMEPAD_BUTTON_A`,
`GLFW_GAMEPAD_BUTTON_B`, `GLFW_GAMEPAD_BUTTON_X`, `GLFW_GAMEPAD_BUTTON_Y`,
`GLFW_GAMEPAD_BUTTON_LEFT_BUMPER`, `GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER`,
`GLFW_GAMEPAD_BUTTON_BACK`, `GLFW_GAMEPAD_BUTTON_START`,
`GLFW_GAMEPAD_BUTTON_GUIDE`, `GLFW_GAMEPAD_BUTTON_LEFT_THUMB`,
`GLFW_GAMEPAD_BUTTON_RIGHT_THUMB`, `GLFW_GAMEPAD_BUTTON_DPAD_UP`,
`GLFW_GAMEPAD_BUTTON_DPAD_RIGHT`, `GLFW_GAMEPAD_BUTTON_DPAD_DOWN` and
`GLFW_GAMEPAD_BUTTON_DPAD_LEFT`.
For those who prefer, there are also the `GLFW_GAMEPAD_BUTTON_CROSS`,
`GLFW_GAMEPAD_BUTTON_CIRCLE`, `GLFW_GAMEPAD_BUTTON_SQUARE` and
`GLFW_GAMEPAD_BUTTON_TRIANGLE` aliases for the A, B, X and Y button indices.
The [axis indices](@ref gamepad_axes) are `GLFW_GAMEPAD_AXIS_LEFT_X`,
`GLFW_GAMEPAD_AXIS_LEFT_Y`, `GLFW_GAMEPAD_AXIS_RIGHT_X`,
`GLFW_GAMEPAD_AXIS_RIGHT_Y`, `GLFW_GAMEPAD_AXIS_LEFT_TRIGGER` and
`GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER`.
The `GLFW_GAMEPAD_BUTTON_LAST` and `GLFW_GAMEPAD_AXIS_LAST` constants equal
the largest available index for each array.
@subsection gamepad_mapping Gamepad mappings
GLFW contains a copy of the mappings available in
[SDL_GameControllerDB](https://github.com/gabomdq/SDL_GameControllerDB) at the
time of release. Newer ones can be added at runtime with @ref
glfwUpdateGamepadMappings.
@code
const char* mappings = load_file_contents("gamecontrollerdb.txt");
glfwUpdateGamepadMappings(mappings);
@endcode
This function supports everything from single lines up to and including the
unmodified contents of the whole `gamecontrollerdb.txt` file.
Below is a description of the mapping format. Please keep in mind that __this
description is not authoritative__. The format is defined by the SDL and
SDL_GameControllerDB projects and their documentation and code takes precedence.
Each mapping is a single line of comma-separated values describing the GUID,
name and layout of the gamepad. Lines that do not begin with a hexadecimal
digit are ignored.
The first value is always the gamepad GUID, a 32 character long hexadecimal
string that typically identifies its make, model, revision and the type of
connection to the computer. When this information is not available, the GUID is
generated using the gamepad name. GLFW uses the SDL 2.0.5+ GUID format but can
convert from the older formats.
The second value is always the human-readable name of the gamepad.
All subsequent values are in the form `<field>:<value>` and describe the layout
of the mapping. These fields may not all be present and may occur in any order.
The button fields are `a`, `b`, `c`, `d`, `back`, `start`, `guide`, `dpup`,
`dpright`, `dpdown`, `dpleft`, `leftshoulder`, `rightshoulder`, `leftstick` and
`rightstick`.
The axis fields are `leftx`, `lefty`, `rightx`, `righty`, `lefttrigger` and
`righttrigger`.
The value of an axis or button field can be a joystick button, a joystick axis,
a hat bitmask or empty. Joystick buttons are specified as `bN`, for example
`b2` for the third button. Joystick axes are specified as `aN`, for example
`a7` for the eighth button. Joystick hat bit masks are specified as `hN.N`, for
example `h0.8` for left on the first hat. More than one bit may be set in the
mask.
The hat bit mask match the [hat states](@ref hat_state) in the joystick
functions.
There is also the special `platform` field that specifies which platform the
mapping is valid for. Possible values are `Windows`, `Mac OS X` and `Linux`.
Below is an example of what a gamepad mapping might look like. It is the
one built into GLFW for Xbox controllers accessed via the XInput API on Windows.
This example has been broken into several lines to fit on the page, but real
gamepad mappings must be a single line.
@code{.unparsed}
78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,
b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,
rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,
righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,
@endcode
@note GLFW does not yet support the range and inversion modifiers `+`, `-` and
`~` that were recently added to SDL.
@section time Time input @section time Time input
GLFW provides high-resolution time input, in seconds, with @ref glfwGetTime. GLFW provides high-resolution time input, in seconds, with @ref glfwGetTime.
@ -696,7 +841,7 @@ converted to one, you can retrieve it with @ref glfwGetClipboardString. See the
reference documentation for the lifetime of the returned string. reference documentation for the lifetime of the returned string.
@code @code
const char* text = glfwGetClipboardString(window); const char* text = glfwGetClipboardString(NULL);
if (text) if (text)
{ {
insert_text(text); insert_text(text);
@ -710,13 +855,9 @@ The contents of the system clipboard can be set to a UTF-8 encoded string with
@ref glfwSetClipboardString. @ref glfwSetClipboardString.
@code @code
glfwSetClipboardString(window, "A string with words in it"); glfwSetClipboardString(NULL, "A string with words in it");
@endcode @endcode
The clipboard functions take a window handle argument because some window
systems require a window to communicate with the system clipboard. Any valid
window may be used.
@section path_drop Path drop input @section path_drop Path drop input

View File

@ -30,8 +30,10 @@ successfully initialized, and only from the main thread.
- @ref glfwGetVersion - @ref glfwGetVersion
- @ref glfwGetVersionString - @ref glfwGetVersionString
- @ref glfwGetError
- @ref glfwSetErrorCallback - @ref glfwSetErrorCallback
- @ref glfwInitHint - @ref glfwInitHint
- @ref glfwInitHintString
- @ref glfwInit - @ref glfwInit
- @ref glfwTerminate - @ref glfwTerminate
@ -53,56 +55,73 @@ if (!glfwInit())
If any part of initialization fails, any parts that succeeded are terminated as If any part of initialization fails, any parts that succeeded are terminated as
if @ref glfwTerminate had been called. The library only needs to be initialized if @ref glfwTerminate had been called. The library only needs to be initialized
once and additional calls to an already initialized library will simply return once and additional calls to an already initialized library will return
`GLFW_TRUE` immediately. `GLFW_TRUE` immediately.
Once the library has been successfully initialized, it should be terminated Once the library has been successfully initialized, it should be terminated
before the application exits. Modern systems are very good at freeing resources before the application exits. Modern systems are very good at freeing resources
allocated by programs that simply exit, but GLFW sometimes has to change global allocated by programs that exit, but GLFW sometimes has to change global system
system settings and these might not be restored without termination. settings and these might not be restored without termination.
@subsection init_hints Initialization hints @subsection init_hints Initialization hints
There are a number of hints that can be set before initialization, that affect Initialization hints are set before @ref glfwInit and affect how the library
how the library behaves. behaves until termination. Integer type hints are set with @ref glfwInitHint
and string type hints with @ref glfwInitHintString.
The values you set are not affected by initialization or termination, but they @code
are only read during initialization. Once GLFW has been initialized, setting glfwInitHint(GLFW_JOYSTICK_HAT_BUTTONS, GLFW_FALSE);
new hint values will not affect behavior until the next time it is terminated @endcode
and initialized.
Some hints are platform specific. These are always valid to set on any The values you set hints to are never reset by GLFW, but they only take effect
platform but they will only affect their specific platform. Other platforms during initialization. Once GLFW has been initialized, any values you set will
will simply ignore them. Setting these hints requires no platform specific be ignored until the library is terminated and initialized again.
headers or calls.
Some hints are platform specific. These may be set on any platform but they
will only affect their specific platform. Other platforms will ignore them.
Setting these hints requires no platform specific headers or functions.
@subsubsection init_hints_shared Shared init hints
@anchor GLFW_JOYSTICK_HAT_BUTTONS @anchor GLFW_JOYSTICK_HAT_BUTTONS
__GLFW_JOYSTICK_HAT_BUTTONS__ specifies whether to also expose joystick hats as __GLFW_JOYSTICK_HAT_BUTTONS__ specifies whether to also expose joystick hats as
buttons, for compatibility with earlier versions of GLFW that did not have @ref buttons, for compatibility with earlier versions of GLFW that did not have @ref
glfwGetJoystickHats. glfwGetJoystickHats. Set this with @ref glfwInitHint.
@subsubsection init_hints_osx macOS specific hints @subsubsection init_hints_osx macOS specific init hints
@anchor GLFW_COCOA_CHDIR_RESOURCES @anchor GLFW_COCOA_CHDIR_RESOURCES
__GLFW_COCOA_CHDIR_RESOURCES__ specifies whether to set the current directory to __GLFW_COCOA_CHDIR_RESOURCES__ specifies whether to set the current directory to
the application to the `Contents/Resources` subdirectory of the application's the application to the `Contents/Resources` subdirectory of the application's
bundle, if present. bundle, if present. Set this with @ref glfwInitHint.
@anchor GLFW_COCOA_MENUBAR @anchor GLFW_COCOA_MENUBAR
__GLFW_COCOA_MENUBAR__ specifies whether to create a basic menu bar, either from __GLFW_COCOA_MENUBAR__ specifies whether to create a basic menu bar, either from
a nib or manually, when the first window is created, which is when AppKit is a nib or manually, when the first window is created, which is when AppKit is
initialized. initialized. Set this with @ref glfwInitHint.
@subsubsection init_hints_x11 X11 specific init hints
@anchor GLFW_X11_WM_CLASS_NAME
@anchor GLFW_X11_WM_CLASS_CLASS
__GLFW_X11_WM_CLASS_NAME__ and __GLFW_X11_WM_CLASS_CLASS__ specifies the desired
ASCII encoded name and class parts of the ICCCM `WM_CLASS` hint for all windows.
Set this with @ref glfwInitHintString.
@subsubsection init_hints_values Supported and default values @subsubsection init_hints_values Supported and default values
Init hint | Default value | Supported values Initialization hint | Default value | Supported values
------------------------------- | ------------- | ---------------- ------------------------------- | ------------- | ----------------
@ref GLFW_JOYSTICK_HAT_BUTTONS | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` @ref GLFW_JOYSTICK_HAT_BUTTONS | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`
@ref GLFW_COCOA_CHDIR_RESOURCES | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` @ref GLFW_COCOA_CHDIR_RESOURCES | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`
@ref GLFW_COCOA_MENUBAR | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` @ref GLFW_COCOA_MENUBAR | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`
@ref GLFW_X11_WM_CLASS_NAME | `""` | An ASCII encoded `WM_CLASS` name
@ref GLFW_X11_WM_CLASS_CLASS | `""` | An ASCII encoded `WM_CLASS` class
@subsection intro_init_terminate Terminating GLFW @subsection intro_init_terminate Terminating GLFW
@ -127,47 +146,76 @@ immediately.
@section error_handling Error handling @section error_handling Error handling
Some GLFW functions have return values that indicate an error, but this is often Some GLFW functions have return values that indicate an error, but this is often
not very helpful when trying to figure out _why_ the error occurred. Other not very helpful when trying to figure out what happened or why it occurred.
functions have no return value reserved for errors, so error notification needs Other functions have no return value reserved for errors, so error notification
a separate channel. Finally, far from all GLFW functions have return values. needs a separate channel. Finally, far from all GLFW functions have return
values.
This is where the error callback comes in. This callback is called whenever an The last [error code](@ref errors) for the calling thread can be queried at any
error occurs. It is set with @ref glfwSetErrorCallback, a function that may be time with @ref glfwGetError.
called regardless of whether GLFW is initialized.
@code @code
glfwSetErrorCallback(error_callback); int code = glfwGetError(NULL);
if (code != GLFW_NO_ERROR)
handle_error(code);
@endcode @endcode
The error callback receives a human-readable description of the error and (when If no error has occurred since the last call, @ref GLFW_NO_ERROR (zero) is
possible) its cause. The description encoded as UTF-8. The callback is also returned. The error is cleared before the function returns.
provided with an [error code](@ref errors).
@code
void error_callback(int error, const char* description)
{
puts(description);
}
@endcode
The error code indicates the general category of the error. Some error codes, The error code indicates the general category of the error. Some error codes,
such as @ref GLFW_NOT_INITIALIZED has only a single meaning, whereas others like such as @ref GLFW_NOT_INITIALIZED has only a single meaning, whereas others like
@ref GLFW_PLATFORM_ERROR are used for many different errors. @ref GLFW_PLATFORM_ERROR are used for many different errors.
Reported errors are never fatal. As long as GLFW was successfully initialized, GLFW often has more information about an error than its general category. You
it will remain initialized and in a safe state until terminated regardless of can retrieve a UTF-8 encoded human-readable description along with the error
how many errors occur. If an error occurs during initialization that causes code. If no error has occurred since the last call, the description is set to
@ref glfwInit to fail, any part of the library that was initialized will be `NULL`.
safely terminated.
The description string is only valid until the error callback returns, as it may @code
have been generated specifically for that error. This lets GLFW provide much const char* description;
more specific error descriptions but means you must make a copy if you want to int code = glfwGetError(&description);
keep the description string.
@note Relying on erroneous behavior is not forward compatible. In other words, if (description)
do not rely on a currently invalid call to generate a specific error, as that display_error_message(code, description);
same call may in future versions generate a different error or become valid. @endcode
The retrieved description string is only valid until the next error occurs.
This means you must make a copy of it if you want to keep it.
You can also set an error callback, which will be called each time an error
occurs. It is set with @ref glfwSetErrorCallback.
@code
glfwSetErrorCallback(error_callback);
@endcode
The error callback receives the same error code and human-readable description
returned by @ref glfwGetError.
@code
void error_callback(int code, const char* description)
{
display_error_message(code, description);
}
@endcode
The error callback is called after the error is stored, so calling @ref
glfwGetError from within the error callback returns the same values as the
callback argument.
The description string passed to the callback is only valid until the error
callback returns. This means you must make a copy of it if you want to keep it.
__Reported errors are never fatal.__ As long as GLFW was successfully
initialized, it will remain initialized and in a safe state until terminated
regardless of how many errors occur. If an error occurs during initialization
that causes @ref glfwInit to fail, any part of the library that was initialized
will be safely terminated.
Do not rely on a currently invalid call to generate a specific error, as in the
future that same call may generate a different error or become valid.
@section coordinate_systems Coordinate systems @section coordinate_systems Coordinate systems
@ -238,14 +286,10 @@ releases.
@subsection reentrancy Reentrancy @subsection reentrancy Reentrancy
GLFW event processing and object creation and destruction are not reentrant. GLFW event processing and object destruction are not reentrant. This means that
This means that the following functions must not be called from any callback the following functions must not be called from any callback function:
function:
- @ref glfwCreateWindow
- @ref glfwDestroyWindow - @ref glfwDestroyWindow
- @ref glfwCreateCursor
- @ref glfwCreateStandardCursor
- @ref glfwDestroyCursor - @ref glfwDestroyCursor
- @ref glfwPollEvents - @ref glfwPollEvents
- @ref glfwWaitEvents - @ref glfwWaitEvents
@ -258,35 +302,39 @@ functions not on this list will not be made non-reentrant.
@subsection thread_safety Thread safety @subsection thread_safety Thread safety
Most GLFW functions must only be called from the main thread, but some may be Most GLFW functions must only be called from the main thread (the thread that
called from any thread. However, no GLFW function may be called from any thread calls main), but some may be called from any thread once the library has been
but the main thread until GLFW has been successfully initialized, including initialized. Before initialization the whole library is thread-unsafe.
functions that may called before initialization.
The reference documentation for every GLFW function states whether it is limited The reference documentation for every GLFW function states whether it is limited
to the main thread. to the main thread.
Initialization and termination, event processing and the creation and Initialization, termination, event processing and the creation and
destruction of windows, contexts and cursors are all limited to the main thread destruction of windows, cursors and OpenGL and OpenGL ES contexts are all
due to limitations of one or several platforms. restricted to the main thread due to limitations of one or several platforms.
Because event processing must be performed on the main thread, all callbacks Because event processing must be performed on the main thread, all callbacks
except for the error callback will only be called on that thread. The error except for the error callback will only be called on that thread. The error
callback may be called on any thread, as any GLFW function may generate errors. callback may be called on any thread, as any GLFW function may generate errors.
The posting of empty events may be done from any thread. The window user The error code and description may be queried from any thread.
pointer and close flag may also be accessed and modified from any thread, but
this is not synchronized by GLFW. The following window related functions may - @ref glfwGetError
be called from any thread:
Empty events may be posted from any thread.
- @ref glfwPostEmptyEvent - @ref glfwPostEmptyEvent
The window user pointer and close flag may be read and written from any thread,
but this is not synchronized by GLFW.
- @ref glfwGetWindowUserPointer - @ref glfwGetWindowUserPointer
- @ref glfwSetWindowUserPointer - @ref glfwSetWindowUserPointer
- @ref glfwWindowShouldClose - @ref glfwWindowShouldClose
- @ref glfwSetWindowShouldClose - @ref glfwSetWindowShouldClose
Rendering may be done on any thread. The following context related functions These functions for working with OpenGL and OpenGL ES contexts may be called
may be called from any thread: from any thread, but the window object is not synchronized by GLFW.
- @ref glfwMakeContextCurrent - @ref glfwMakeContextCurrent
- @ref glfwGetCurrentContext - @ref glfwGetCurrentContext
@ -295,27 +343,23 @@ may be called from any thread:
- @ref glfwExtensionSupported - @ref glfwExtensionSupported
- @ref glfwGetProcAddress - @ref glfwGetProcAddress
The raw timer may be queried from any thread. The following raw timer related The raw timer functions may be called from any thread.
functions may be called from any thread:
- @ref glfwGetTimerFrequency - @ref glfwGetTimerFrequency
- @ref glfwGetTimerValue - @ref glfwGetTimerValue
The regular timer may be used from any thread, but the reading and writing of The regular timer may be used from any thread, but reading and writing the timer
the timer offset is not synchronized by GLFW. The following timer related offset is not synchronized by GLFW.
functions may be called from any thread:
- @ref glfwGetTime - @ref glfwGetTime
- @ref glfwSetTime - @ref glfwSetTime
Library version information may be queried from any thread. The following Library version information may be queried from any thread.
version related functions may be called from any thread:
- @ref glfwGetVersion - @ref glfwGetVersion
- @ref glfwGetVersionString - @ref glfwGetVersionString
Vulkan objects may be created and information queried from any thread. The All Vulkan related functions may be called from any thread.
following Vulkan related functions may be called from any thread:
- @ref glfwVulkanSupported - @ref glfwVulkanSupported
- @ref glfwGetRequiredInstanceExtensions - @ref glfwGetRequiredInstanceExtensions
@ -323,9 +367,9 @@ following Vulkan related functions may be called from any thread:
- @ref glfwGetPhysicalDevicePresentationSupport - @ref glfwGetPhysicalDevicePresentationSupport
- @ref glfwCreateWindowSurface - @ref glfwCreateWindowSurface
GLFW uses no synchronization objects internally except for thread-local storage GLFW uses synchronization objects internally only to manage the per-thread
to keep track of the current context for each thread. Synchronization is left context and error states. Additional synchronization is left to the
to the application. application.
Functions that may currently be called from any thread will always remain so, Functions that may currently be called from any thread will always remain so,
but functions that are currently limited to the main thread may be updated to but functions that are currently limited to the main thread may be updated to
@ -347,8 +391,9 @@ Undocumented behavior, i.e. behavior that is not described in the documentation,
may change at any time until it is documented. may change at any time until it is documented.
If the reference documentation and the implementation differ, the reference If the reference documentation and the implementation differ, the reference
documentation is correct and the implementation will be fixed in the next documentation will almost always take precedence and the implementation will be
release. fixed in the next release. The reference documentation will also take
precedence over anything stated in a guide.
@subsection event_order Event order @subsection event_order Event order

View File

@ -8,7 +8,7 @@ GLFW is a free, Open Source, multi-platform library for OpenGL, OpenGL ES and
Vulkan application development. It provides a simple, platform-independent API Vulkan application development. It provides a simple, platform-independent API
for creating windows, contexts and surfaces, reading input, handling events, etc. for creating windows, contexts and surfaces, reading input, handling events, etc.
See @ref news_33 for release highlights or the See @ref news_33 for highlights or the
[version history](http://www.glfw.org/changelog.html) for details. [version history](http://www.glfw.org/changelog.html) for details.
@ref quick_guide is a guide for users new to GLFW. It takes you through how to @ref quick_guide is a guide for users new to GLFW. It takes you through how to

View File

@ -85,8 +85,8 @@ void monitor_callback(GLFWmonitor* monitor, int event)
} }
@endcode @endcode
If a monitor is disconnected, any windows that are full screen on it get forced If a monitor is disconnected, all windows that are full screen on it will be
into windowed mode. switched to windowed mode.
@section monitor_properties Monitor properties @section monitor_properties Monitor properties
@ -131,17 +131,34 @@ current _resolution_, i.e. the width and height of its current
[video mode](@ref monitor_modes). [video mode](@ref monitor_modes).
@code @code
int widthMM, heightMM; int width_mm, height_mm;
glfwGetMonitorPhysicalSize(monitor, &widthMM, &heightMM); glfwGetMonitorPhysicalSize(monitor, &width_mm, &height_mm);
@endcode @endcode
This can, for example, be used together with the current video mode to calculate While this can be used to calculate the raw DPI of a monitor, this is often not
the DPI of a monitor. useful. Instead use the [monitor content scale](@ref monitor_scale) and
[window content scale](@ref window_scale) to scale your content.
@subsection monitor_scale Content scale
The content scale for a monitor can be retrieved with @ref
glfwGetMonitorContentScale.
@code @code
const double dpi = mode->width / (widthMM / 25.4); float xscale, yscale;
glfwGetMonitorContentScale(monitor, &xscale, &yscale);
@endcode @endcode
The content scale is the ratio between the current DPI and the platform's
default DPI. If you scale all pixel dimensions by this scale then your content
should appear at an appropriate size. This is especially important for text and
any UI elements.
The content scale may depend on both the monitor resolution and pixel density
and on user settings. It may be very different from the raw DPI calculated from
the physical size and current resolution.
@subsection monitor_pos Virtual position @subsection monitor_pos Virtual position
@ -195,8 +212,8 @@ glfwSetGammaRamp(monitor, &ramp);
The gamma ramp data is copied before the function returns, so there is no need The gamma ramp data is copied before the function returns, so there is no need
to keep it around once the ramp has been set. to keep it around once the ramp has been set.
@note It is recommended to use gamma ramps of size 256, as that is the size It is recommended that your gamma ramp have the same size as the current gamma
supported by all graphics cards on all platforms. ramp for that monitor.
The current gamma ramp for a monitor is returned by @ref glfwGetGammaRamp. See The current gamma ramp for a monitor is returned by @ref glfwGetGammaRamp. See
the reference documentation for the lifetime of the returned structure. the reference documentation for the lifetime of the returned structure.

View File

@ -221,7 +221,7 @@ GLFW 2, windows and contexts created with GLFW 3 will never be destroyed unless
you choose them to be. Each window now has a close flag that is set to you choose them to be. Each window now has a close flag that is set to
`GLFW_TRUE` when the user attempts to close that window. By default, nothing else `GLFW_TRUE` when the user attempts to close that window. By default, nothing else
happens and the window stays visible. It is then up to you to either destroy happens and the window stays visible. It is then up to you to either destroy
the window, take some other action or simply ignore the request. the window, take some other action or ignore the request.
You can query the close flag at any time with @ref glfwWindowShouldClose and set You can query the close flag at any time with @ref glfwWindowShouldClose and set
it at any time with @ref glfwSetWindowShouldClose. it at any time with @ref glfwSetWindowShouldClose.

View File

@ -1,19 +1,50 @@
/*! /*!
@page news New features @page news Release notes
@section news_33 Release notes for 3.3
@subsection news_33_geterror Error query
GLFW now supports querying the last error code for the calling thread and its
human-readable description with @ref glfwGetError.
@see @ref error_handling
@subsection news_33_gamepad SDL_GameControllerDB support and gamepad input
GLFW now supports remapping of gamepads and controllers to a 360-like controller
layout with @ref glfwJoystickIsGamepad, @ref glfwGetJoystickGUID, @ref
glfwGetGamepadName, @ref glfwGetGamepadState and @ref glfwUpdateGamepadMappings,
and the input state struct @ref GLFWgamepadstate.
@sa @ref gamepad
@subsection news_33_attention User attention request
GLFW now supports requesting user attention to a specific window (on macOS to
the application as a whole) with @ref glfwRequestWindowAttention.
@see @ref window_attention
@section news_33 New features in 3.3
@subsection news_33_maximize Window maximization callback @subsection news_33_maximize Window maximization callback
GLFW now supports window maximization notifications with @ref GLFW now supports notifying the application that the window has been maximized
glfwSetWindowMaximizeCallback. @ref glfwSetWindowMaximizeCallback.
@see @ref window_maximize
@subsection news_33_keyscancode Platform-specific key scancode query @subsection news_33_keyscancode Platform-specific key scancode query
GLFW now supports querying the platform dependent scancode of any key with GLFW now supports querying the platform dependent scancode of any physical key
@ref glfwGetKeyScancode. with @ref glfwGetKeyScancode.
@see @ref input_key
@subsection news_33_setwindowattrib Support for updating window attributes @subsection news_33_setwindowattrib Support for updating window attributes
@ -24,33 +55,69 @@ GLFW now supports changing the [GLFW_DECORATED](@ref GLFW_DECORATED_attrib),
[GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib) attributes for existing [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib) attributes for existing
windows with @ref glfwSetWindowAttrib. windows with @ref glfwSetWindowAttrib.
@see @ref window_attribs
@subsection news_33_joyhats Support for joystick hats
GLFW now supports querying the hats of a joystick with @ref glfwGetJoystickHats @subsection news_33_contentscale Content scale queries for DPI-aware rendering
and controlling whether hats are also exposed as buttons with the @ref
GLFW_JOYSTICK_HAT_BUTTONS init hint. GLFW now supports querying the window and monitor content scale, i.e. the ratio
between the current DPI and the platform's default DPI, with @ref
glfwGetWindowContentScale and @ref glfwGetMonitorContentScale.
@see @ref window_scale
@subsection news_33_inithint Support for initialization hints @subsection news_33_inithint Support for initialization hints
GLFW now supports setting library initialization hints with @ref glfwInitHint. GLFW now supports setting library initialization hints with @ref glfwInitHint
Currently the macOS specific @ref or @ref glfwInitHintString. These must be set before initialization to take
GLFW_COCOA_CHDIR_RESOURCES and @ref GLFW_COCOA_MENUBAR init hints are supported, effect.
replacing the corresponding compile-time options.
@see @ref init_hints
@subsection news_33_platformhints Support for platform specific hints
GLFW now supports platform specific init and window hints to control system
features that are only available on a single platform.
@see @ref init_hints_osx
@see @ref window_hints_osx
@subsection news_33_joyhats Support for joystick hats
GLFW now supports querying the hats (or POVs or D-pads) of a joystick with @ref
glfwGetJoystickHats. Hats are by default also exposed as buttons, but this can
be disabled with the @ref GLFW_JOYSTICK_HAT_BUTTONS init hint.
@see @ref joystick_hat
@subsection news_33_transparent Support for transparent windows and framebuffers
GLFW now supports the creation of windows with transparent framebuffers on
systems with desktop compositing enabled with the @ref
GLFW_TRANSPARENT_FRAMEBUFFER window hint and attribute. This hint must be set
before window creation and leaves any window decorations opaque.
GLFW now also supports whole window transparency with @ref glfwGetWindowOpacity
and @ref glfwSetWindowOpacity. This value controls the opacity of the whole
window including decorations and unlike framebuffer transparency can be changed
at any time after window creation.
@subsection news_33_centercursor Cursor centering window hint @subsection news_33_centercursor Cursor centering window hint
GLFW now supports controlling whether the cursor is centered over newly created GLFW now supports controlling whether the cursor is centered over newly created
full screen windows with the [GLFW_CENTER_CURSOR](@ref GLFW_CENTER_CURSOR_hint) full screen windows with the [GLFW_CENTER_CURSOR](@ref GLFW_CENTER_CURSOR_hint)
window hint. window hint. It is enabled by default.
@subsection news_33_rawmotion Support for raw mouse motion @subsection news_33_rawmotion Support for raw mouse motion
GLFW now supports raw mouse motion in disabled cursor mode on platforms where GLFW now uses raw (unscaled and unaccelerated) mouse motion in disabled cursor
this is available. mode on platforms where this is available, specifically Windows and X11.
@subsection news_33_moltenvk Support for Vulkan on macOS via MoltenVK @subsection news_33_moltenvk Support for Vulkan on macOS via MoltenVK
@ -58,6 +125,8 @@ this is available.
GLFW now supports the `VK_MVK_macos_surface` window surface creation extension GLFW now supports the `VK_MVK_macos_surface` window surface creation extension
provided by [MoltenVK](https://moltengl.com/moltenvk/). provided by [MoltenVK](https://moltengl.com/moltenvk/).
@see @ref vulkan_guide
@subsection news_33_osmesa OSMesa backend for headless software rendering @subsection news_33_osmesa OSMesa backend for headless software rendering
@ -66,12 +135,19 @@ GLFW now supports creating offscreen OpenGL contexts using
[GLFW_CONTEXT_CREATION_API](@ref GLFW_CONTEXT_CREATION_API_hint) to [GLFW_CONTEXT_CREATION_API](@ref GLFW_CONTEXT_CREATION_API_hint) to
`GLFW_OSMESA_CONTEXT_API`. `GLFW_OSMESA_CONTEXT_API`.
There is also a new headless backend that uses OSMesa as its native context There is also a new null backend that uses OSMesa as its native context
creation API, intended for automated testing. This backend does not provide creation API, intended for automated testing. This backend does not provide
input. input.
@section news_32 New features in 3.2 @subsection news_33_primary X11 primary selection access
GLFW now supports querying and setting the X11 primary selection via the native
access functions @ref glfwGetX11SelectionString and @ref
glfwSetX11SelectionString.
@section news_32 Release notes for 3.2
@subsection news_32_vulkan Support for Vulkan @subsection news_32_vulkan Support for Vulkan
@ -162,7 +238,7 @@ GLFW now supports being used as a
easy linking with the library and its dependencies. easy linking with the library and its dependencies.
@section news_31 New features in 3.1 @section news_31 Release notes for 3.1
These are the release highlights. For a full list of changes see the These are the release highlights. For a full list of changes see the
[version history](http://www.glfw.org/changelog.html). [version history](http://www.glfw.org/changelog.html).
@ -273,7 +349,7 @@ GLFW now has an _experimental_ Mir display server backend that can be selected
on Linux with a CMake option. on Linux with a CMake option.
@section news_30 New features in 3.0 @section news_30 Release notes for 3.0
These are the release highlights. For a full list of changes see the These are the release highlights. For a full list of changes see the
[version history](http://www.glfw.org/changelog.html). [version history](http://www.glfw.org/changelog.html).

View File

@ -69,7 +69,7 @@ if (!glfwInit())
} }
@endcode @endcode
Note that `GLFW_TRUE` and `GLFW_FALSE` are and will always be just one and zero. Note that `GLFW_TRUE` and `GLFW_FALSE` are and will always be one and zero.
When you are done using GLFW, typically just before the application exits, you When you are done using GLFW, typically just before the application exits, you
need to terminate GLFW. need to terminate GLFW.
@ -86,14 +86,12 @@ functions that require it.
@subsection quick_capture_error Setting an error callback @subsection quick_capture_error Setting an error callback
Most events are reported through callbacks, whether it's a key being pressed, Most events are reported through callbacks, whether it's a key being pressed,
a GLFW window being moved, or an error occurring. Callbacks are simply a GLFW window being moved, or an error occurring. Callbacks are C functions (or
C functions (or C++ static methods) that are called by GLFW with arguments C++ static methods) that are called by GLFW with arguments describing the event.
describing the event.
In case a GLFW function fails, an error is reported to the GLFW error callback. In case a GLFW function fails, an error is reported to the GLFW error callback.
You can receive these reports with an error callback. This function must have You can receive these reports with an error callback. This function must have
the signature below. This simple error callback just prints the error the signature below but may do anything permitted in other callbacks.
description to `stderr`.
@code @code
void error_callback(int error, const char* description) void error_callback(int error, const char* description)
@ -249,7 +247,7 @@ You can also set a framebuffer size callback using @ref
glfwSetFramebufferSizeCallback and be notified when the size changes. glfwSetFramebufferSizeCallback and be notified when the size changes.
Actual rendering with OpenGL is outside the scope of this tutorial, but there Actual rendering with OpenGL is outside the scope of this tutorial, but there
are [many](https://open.gl/) [excellent](http://learnopengl.com/) are [many](https://open.gl/) [excellent](https://learnopengl.com/)
[tutorial](http://openglbook.com/) [sites](http://ogldev.atspace.co.uk/) that [tutorial](http://openglbook.com/) [sites](http://ogldev.atspace.co.uk/) that
teach modern OpenGL. Some of them use GLFW to create the context and window teach modern OpenGL. Some of them use GLFW to create the context and window
while others use GLUT or SDL, but remember that OpenGL itself always works the while others use GLUT or SDL, but remember that OpenGL itself always works the

View File

@ -20,6 +20,11 @@ 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 However, any Vulkan-specific test and example programs are built only if the
CMake files find a Vulkan SDK. CMake files find a Vulkan SDK.
@macos Because MoltenVK is typically not installed system-wide, you will need to
point CMake to it using the `CMAKE_FRAMEWORK_PATH` variable when configuring the
GLFW source tree. Set this variable to the `MoltenVK/macOS` subdirectory of the
SDK, either on the command-line or in the CMake GUI.
For details on a specific function in this category, see the @ref vulkan. There For details on a specific function in this category, see the @ref vulkan. There
are also guides for the other areas of the GLFW API. are also guides for the other areas of the GLFW API.
@ -34,19 +39,18 @@ are also guides for the other areas of the GLFW API.
By default, GLFW will look for the Vulkan loader on demand at runtime via its By default, GLFW will look for the Vulkan loader on demand at runtime via its
standard name (`vulkan-1.dll` on Windows, `libvulkan.so.1` on Linux and other standard name (`vulkan-1.dll` on Windows, `libvulkan.so.1` on Linux and other
Unix-like systems). This means that GLFW does not need to be linked against the Unix-like systems and `libMoltenVK.dylib` on macOS). This means that GLFW does
loader. However, it also means that if you are using the static library form of not need to be linked against the loader. However, it also means that if you
the Vulkan loader GLFW will either fail to find it or (worse) use the wrong one. are using the static library form of the Vulkan loader GLFW will either fail to
find it or (worse) use the wrong one.
The @ref GLFW_VULKAN_STATIC CMake option makes GLFW link directly against the The @ref GLFW_VULKAN_STATIC CMake option makes GLFW link directly against the
static form. Not linking against the Vulkan loader will then be a compile-time static library form. Not linking against the Vulkan loader will then be
error. a compile-time error.
@macos MoltenVK only provides the static library form of the Vulkan loader, but @macos When using the static library form of MoltenVK (i.e. `MetalVK.framework`
GLFW is able to find it without @ref GLFW_VULKAN_STATIC as long as it is linked and not `libMoltenVK.dylib`) you must also link against its dependencies: the
into any of the binaries already loaded into the process. As it is a static `Cocoa`, `Metal` and `QuartzCore` system frameworks and the `libc++` library.
library, you must also link against its dependencies: the `Cocoa`, `Metal` and
`QuartzCore` frameworks and the `libc++` library.
@section vulkan_include Including the Vulkan and GLFW header files @section vulkan_include Including the Vulkan and GLFW header files

View File

@ -77,7 +77,7 @@ GLFWvidmode.blueBits | @ref GLFW_BLUE_BITS hint
GLFWvidmode.refreshRate | @ref GLFW_REFRESH_RATE hint GLFWvidmode.refreshRate | @ref GLFW_REFRESH_RATE hint
Once you have a full screen window, you can change its resolution, refresh rate 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 and monitor with @ref glfwSetWindowMonitor. If you only need change its
resolution you can also call @ref glfwSetWindowSize. In all cases, the new 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 video mode will be selected the same way as the video mode chosen by @ref
glfwCreateWindow. If the window has an OpenGL or OpenGL ES context, it will be glfwCreateWindow. If the window has an OpenGL or OpenGL ES context, it will be
@ -87,10 +87,10 @@ 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 window iconified if it loses input focus, to allow the user to switch back to
the desktop. This behavior can be disabled with the the desktop. This behavior can be disabled with the
[GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_hint) window hint, for example if you [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_hint) window hint, for example if you
wish to simultaneously cover multiple windows with full screen windows. wish to simultaneously cover multiple monitors with full screen windows.
If a monitor is disconnected, any window that is full screen on that monitor If a monitor is disconnected, all windows that are full screen on that monitor
will be forced into windowed mode. See @ref monitor_event for more information. will be switched to windowed mode. See @ref monitor_event for more information.
@subsubsection window_windowed_full_screen "Windowed full screen" windows @subsubsection window_windowed_full_screen "Windowed full screen" windows
@ -99,7 +99,7 @@ If the closest match for the desired video mode is the current one, the video
mode will not be changed, making window creation faster and application mode will not be changed, making window creation faster and application
switching much smoother. This is sometimes called _windowed full screen_ or switching much smoother. This is sometimes called _windowed full screen_ or
_borderless full screen_ window and counts as a full screen window. To create _borderless full screen_ window and counts as a full screen window. To create
such a window, simply request the current video mode. such a window, request the current video mode.
@code @code
const GLFWvidmode* mode = glfwGetVideoMode(monitor); const GLFWvidmode* mode = glfwGetVideoMode(monitor);
@ -151,8 +151,8 @@ and reset all at once to their defaults with @ref glfwDefaultWindowHints.
Some hints are platform specific. These are always valid to set on any Some hints are platform specific. These are always valid to set on any
platform but they will only affect their specific platform. Other platforms platform but they will only affect their specific platform. Other platforms
will simply ignore them. Setting these hints requires no platform specific will ignore them. Setting these hints requires no platform specific headers or
headers or calls. calls.
Note that hints need to be set _before_ the creation of the window and context Note that hints need to be set _before_ the creation of the window and context
you wish to have the specified attributes. you wish to have the specified attributes.
@ -225,6 +225,13 @@ __GLFW_CENTER_CURSOR__ specifies whether the cursor should be centered over
newly created full screen windows. Possible values are `GLFW_TRUE` and newly created full screen windows. Possible values are `GLFW_TRUE` and
`GLFW_FALSE`. This hint is ignored for windowed mode windows. `GLFW_FALSE`. This hint is ignored for windowed mode windows.
@anchor GLFW_TRANSPARENT_FRAMEBUFFER_hint
__GLFW_TRANSPARENT_FRAMEBUFFER__ specifies whether the window framebuffer will
be transparent. If enabled and supported by the system, the window framebuffer
alpha channel will be used to combine the framebuffer with the background. This
does not affect window decorations. Possible values are `GLFW_TRUE` and
`GLFW_FALSE`.
@subsubsection window_hints_fb Framebuffer related hints @subsubsection window_hints_fb Framebuffer related hints
@ -271,11 +278,17 @@ the application has no preference.
@anchor GLFW_SRGB_CAPABLE @anchor GLFW_SRGB_CAPABLE
__GLFW_SRGB_CAPABLE__ specifies whether the framebuffer should be sRGB capable. __GLFW_SRGB_CAPABLE__ specifies whether the framebuffer should be sRGB capable.
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.
Possible values are `GLFW_TRUE` and `GLFW_FALSE`. Possible values are `GLFW_TRUE` and `GLFW_FALSE`.
@par
__OpenGL:__ If enabled and supported by the system, the `GL_FRAMEBUFFER_SRGB`
enable will control sRGB rendering. By default, sRGB rendering will be
disabled.
@par
__OpenGL ES:__ If enabled and supported by the system, the context will always
have sRGB rendering enabled.
@anchor GLFW_DOUBLEBUFFER @anchor GLFW_DOUBLEBUFFER
__GLFW_DOUBLEBUFFER__ specifies whether the framebuffer should be double __GLFW_DOUBLEBUFFER__ specifies whether the framebuffer should be double
buffered. You nearly always want to use double buffering. This is a hard buffered. You nearly always want to use double buffering. This is a hard
@ -332,6 +345,9 @@ __GLFW_CONTEXT_VERSION_MAJOR__ and __GLFW_CONTEXT_VERSION_MINOR__ specify the
client API version that the created context must be compatible with. The exact client API version that the created context must be compatible with. The exact
behavior of these hints depend on the requested client API. behavior of these hints depend on the requested client API.
@note Do not confuse these hints with `GLFW_VERSION_MAJOR` and
`GLFW_VERSION_MINOR`, which provide the API version of the GLFW header.
@par @par
__OpenGL:__ These hints are not hard constraints, but creation will fail if the __OpenGL:__ These hints are not hard constraints, but creation will fail if the
OpenGL version of the created context is less than the one requested. It is OpenGL version of the created context is less than the one requested. It is
@ -417,16 +433,8 @@ The no error mode for OpenGL and OpenGL ES is described in detail by the
[GL_KHR_no_error](https://www.opengl.org/registry/specs/KHR/no_error.txt) [GL_KHR_no_error](https://www.opengl.org/registry/specs/KHR/no_error.txt)
extension. extension.
@note This hint is experimental in its current state. There are currently
(October 2015) no corresponding WGL or GLX extensions. That makes this hint
a [hard constraint](@ref window_hints_hard) for those backends, as creation will
fail if unsupported context flags are requested. Once the extensions are
available, they will be required and creation of `GL_KHR_no_error` contexts may
fail on early drivers where this flag is supported without those extensions
being listed.
@subsubsection window_hints_osx macOS specific window hints
@subsubsection window_hints_osx macOS specific hints
@anchor GLFW_COCOA_RETINA_FRAMEBUFFER_hint @anchor GLFW_COCOA_RETINA_FRAMEBUFFER_hint
__GLFW_COCOA_RETINA_FRAMEBUFFER__ specifies whether to use full resolution __GLFW_COCOA_RETINA_FRAMEBUFFER__ specifies whether to use full resolution
@ -469,6 +477,7 @@ GLFW_AUTO_ICONIFY | `GLFW_TRUE` | `GLFW_TRUE` or `GL
GLFW_FLOATING | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` GLFW_FLOATING | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE`
GLFW_MAXIMIZED | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` GLFW_MAXIMIZED | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE`
GLFW_CENTER_CURSOR | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` GLFW_CENTER_CURSOR | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`
GLFW_TRANSPARENT_FRAMEBUFFER | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE`
GLFW_RED_BITS | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` 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_GREEN_BITS | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE`
GLFW_BLUE_BITS | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` GLFW_BLUE_BITS | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE`
@ -573,15 +582,15 @@ 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 desired video mode is set immediately. The window is resized to fit the
resolution of the set video mode. resolution of the set video mode.
If you wish to be notified when a window is resized, whether by the user or If you wish to be notified when a window is resized, whether by the user, the
the system, set a size callback. system or your own code, set a size callback.
@code @code
glfwSetWindowSizeCallback(window, window_size_callback); glfwSetWindowSizeCallback(window, window_size_callback);
@endcode @endcode
The callback function receives the new size, in screen coordinates, of the The callback function receives the new size, in screen coordinates, of the
client area of the window when it is resized. client area of the window when the window is resized.
@code @code
void window_size_callback(GLFWwindow* window, int width, int height) void window_size_callback(GLFWwindow* window, int width, int height)
@ -654,6 +663,26 @@ The size of a framebuffer may change independently of the size of a window, for
example if the window is dragged between a regular monitor and a high-DPI one. example if the window is dragged between a regular monitor and a high-DPI one.
@subsection window_scale Window content scale
The content scale for a window can be retrieved with @ref
glfwGetWindowContentScale.
@code
float xscale, yscale;
glfwGetWindowContentScale(window, &xscale, &yscale);
@endcode
The content scale of a window is the ratio between the current DPI and the
platform's default DPI. If you scale all pixel dimensions by this scale then
your content should appear at an appropriate size. This is especially important
for text and any UI elements.
On systems where each monitors can have its own content scale, the window
content scale will depend on which monitor the system considers the window to be
on.
@subsection window_sizelimits Window size limits @subsection window_sizelimits Window size limits
The minimum and maximum size of the client area of a windowed mode window can be The minimum and maximum size of the client area of a windowed mode window can be
@ -685,7 +714,7 @@ glfwSetWindowAspectRatio(window, 16, 9);
The aspect ratio is specified as a numerator and denominator, corresponding to The aspect ratio is specified as a numerator and denominator, corresponding to
the width and height, respectively. If you want a window to maintain its the width and height, respectively. If you want a window to maintain its
current aspect ratio, simply use its current size as the ratio. current aspect ratio, use its current size as the ratio.
@code @code
int width, height; int width, height;
@ -711,15 +740,15 @@ The window system may put limitations on window placement.
glfwSetWindowPos(window, 100, 100); glfwSetWindowPos(window, 100, 100);
@endcode @endcode
If you wish to be notified when a window is moved, whether by the user, system If you wish to be notified when a window is moved, whether by the user, the
or your own code, set a position callback. system or your own code, set a position callback.
@code @code
glfwSetWindowPosCallback(window, window_pos_callback); glfwSetWindowPosCallback(window, window_pos_callback);
@endcode @endcode
The callback function receives the new position of the upper-left corner of the The callback function receives the new position, in screen coordinates, of the
client area when the window is moved. upper-left corner of the client area when the window is moved.
@code @code
void window_pos_callback(GLFWwindow* window, int xpos, int ypos) void window_pos_callback(GLFWwindow* window, int xpos, int ypos)
@ -776,6 +805,10 @@ images[1] = load_icon("my_icon_small.png");
glfwSetWindowIcon(window, 2, images); glfwSetWindowIcon(window, 2, images);
@endcode @endcode
The image data is 32-bit, little-endian, non-premultiplied RGBA, i.e. eight bits
per channel with the red channel first. The pixels are arranged canonically as
sequential rows, starting from the top-left corner.
To revert to the default window icon, pass in an empty image array. To revert to the default window icon, pass in an empty image array.
@code @code
@ -975,6 +1008,10 @@ glfwFocusWindow.
glfwFocusWindow(window); glfwFocusWindow(window);
@endcode @endcode
Keep in mind that it can be very disruptive to the user when a window is forced
to the top. For a less disruptive way of getting the user's attention, see
[attention requests](@ref window_attention).
If you wish to be notified when a window gains or loses input focus, whether by 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. the user, system or your own code, set a focus callback.
@ -1013,6 +1050,20 @@ glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE);
@endcode @endcode
@subsection window_attention Window attention request
If you wish to notify the user of an event without interrupting, you can request
attention with @ref glfwRequestWindowAttention.
@code
glfwRequestWindowAttention(window);
@endcode
The system will highlight the specified window, or on platforms where this is
not supported, the application as a whole. Once the user has given it
attention, the system will automatically end the request.
@subsection window_refresh Window damage and refresh @subsection window_refresh Window damage and refresh
If you wish to be notified when the contents of a window is damaged and needs If you wish to be notified when the contents of a window is damaged and needs
@ -1038,6 +1089,66 @@ window contents are saved off-screen, this callback might only be called when
the window or framebuffer is resized. the window or framebuffer is resized.
@subsection window_transparency Window transparency
GLFW supports two kinds of transparency for windows; framebuffer transparency
and whole window transparency. A single window may not use both methods. The
results of doing this are undefined.
Both methods require the platform to support it and not every version of every
platform GLFW supports does this, so there are mechanisms to check whether the
window really is transparent.
Window framebuffers can be made transparent on a per-pixel per-frame basis with
the [GLFW_TRANSPARENT_FRAMEBUFFER](@ref GLFW_TRANSPARENT_FRAMEBUFFER_hint)
window hint.
@code
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);
@endcode
If supported by the system, the window content area will be composited with the
background using the framebuffer per-pixel alpha channel. This requires desktop
compositing to be enabled on the system. It does not affect window decorations.
You can check whether the window framebuffer was successfully made transparent
with the
[GLFW_TRANSPARENT_FRAMEBUFFER](@ref GLFW_TRANSPARENT_FRAMEBUFFER_attrib)
window attribute.
@code
if (glfwGetWindowAttrib(window, GLFW_TRANSPARENT_FRAMEBUFFER))
{
// window framebuffer is currently transparent
}
@endcode
GLFW comes with an example that enabled framebuffer transparency called `gears`.
The opacity of the whole window, including any decorations, can be set with @ref
glfwSetWindowOpacity.
@code
glfwSetWindowOpacity(window, 0.5f);
@endcode
The opacity (or alpha) value is a positive finite number between zero and one,
where 0 (zero) is fully transparent and 1 (one) is fully opaque. The initial
opacity value for newly created windows is 1.
The current opacity of a window can be queried with @ref glfwGetWindowOpacity.
@code
float opacity = glfwGetWindowOpacity(window);
@endcode
If the system does not support whole window transparency, this function always
returns one.
GLFW comes with a test program that lets you control whole window transparency
at run-time called `opacity`.
@subsection window_attribs Window attributes @subsection window_attribs Window attributes
Windows have a number of attributes that can be returned using @ref Windows have a number of attributes that can be returned using @ref
@ -1107,6 +1218,12 @@ called topmost or always-on-top. This can be set before creation with the
[GLFW_FLOATING](@ref GLFW_FLOATING_hint) window hint or after with @ref [GLFW_FLOATING](@ref GLFW_FLOATING_hint) window hint or after with @ref
glfwSetWindowAttrib. glfwSetWindowAttrib.
@anchor GLFW_TRANSPARENT_FRAMEBUFFER_attrib
__GLFW_TRANSPARENT_FRAMEBUFFER__ indicates whether the specified window has
a transparent framebuffer, i.e. the window contents is composited with the
background using the window framebuffer alpha channel. See @ref
window_transparency for details.
@subsubsection window_attribs_ctx Context related attributes @subsubsection window_attribs_ctx Context related attributes
@ -1126,6 +1243,10 @@ __GLFW_CONTEXT_VERSION_MAJOR__, __GLFW_CONTEXT_VERSION_MINOR__ and
__GLFW_CONTEXT_REVISION__ indicate the client API version of the window's __GLFW_CONTEXT_REVISION__ indicate the client API version of the window's
context. context.
@note Do not confuse these attributes with `GLFW_VERSION_MAJOR`,
`GLFW_VERSION_MINOR` and `GLFW_VERSION_REVISION` which provide the API version
of the GLFW header.
@anchor GLFW_OPENGL_FORWARD_COMPAT_attrib @anchor GLFW_OPENGL_FORWARD_COMPAT_attrib
__GLFW_OPENGL_FORWARD_COMPAT__ is `GLFW_TRUE` if the window's context is an __GLFW_OPENGL_FORWARD_COMPAT__ is `GLFW_TRUE` if the window's context is an
OpenGL forward-compatible one, or `GLFW_FALSE` otherwise. OpenGL forward-compatible one, or `GLFW_FALSE` otherwise.
@ -1213,4 +1334,10 @@ Note that this may not work on all machines, as some drivers have
user-controlled settings that override any swap interval the application user-controlled settings that override any swap interval the application
requests. requests.
A context that supports either the `WGL_EXT_swap_control_tear` or the
`GLX_EXT_swap_control_tear` extension also accepts _negative_ swap intervals,
which allows the driver to swap immediately even if a frame arrives a little bit
late. This trades the risk of visible tears for greater framerate stability.
You can check for these extensions with @ref glfwExtensionSupported.
*/ */

View File

@ -35,6 +35,7 @@ add_executable(gears WIN32 MACOSX_BUNDLE gears.c ${ICON} ${GLAD})
add_executable(heightmap WIN32 MACOSX_BUNDLE heightmap.c ${ICON} ${GLAD}) add_executable(heightmap WIN32 MACOSX_BUNDLE heightmap.c ${ICON} ${GLAD})
add_executable(offscreen offscreen.c ${ICON} ${GLAD}) add_executable(offscreen offscreen.c ${ICON} ${GLAD})
add_executable(particles WIN32 MACOSX_BUNDLE particles.c ${ICON} ${TINYCTHREAD} ${GETOPT} ${GLAD}) add_executable(particles WIN32 MACOSX_BUNDLE particles.c ${ICON} ${TINYCTHREAD} ${GETOPT} ${GLAD})
add_executable(sharing WIN32 MACOSX_BUNDLE sharing.c ${ICON} ${GLAD})
add_executable(simple WIN32 MACOSX_BUNDLE simple.c ${ICON} ${GLAD}) add_executable(simple WIN32 MACOSX_BUNDLE simple.c ${ICON} ${GLAD})
add_executable(splitview WIN32 MACOSX_BUNDLE splitview.c ${ICON} ${GLAD}) add_executable(splitview WIN32 MACOSX_BUNDLE splitview.c ${ICON} ${GLAD})
add_executable(wave WIN32 MACOSX_BUNDLE wave.c ${ICON} ${GLAD}) add_executable(wave WIN32 MACOSX_BUNDLE wave.c ${ICON} ${GLAD})
@ -44,7 +45,7 @@ if (RT_LIBRARY)
target_link_libraries(particles "${RT_LIBRARY}") target_link_libraries(particles "${RT_LIBRARY}")
endif() endif()
set(WINDOWS_BINARIES boing gears heightmap particles simple splitview wave) set(WINDOWS_BINARIES boing gears heightmap particles sharing simple splitview wave)
set(CONSOLE_BINARIES offscreen) set(CONSOLE_BINARIES offscreen)
set_target_properties(${WINDOWS_BINARIES} ${CONSOLE_BINARIES} PROPERTIES set_target_properties(${WINDOWS_BINARIES} ${CONSOLE_BINARIES} PROPERTIES
@ -61,6 +62,7 @@ if (APPLE)
set_target_properties(gears PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Gears") set_target_properties(gears PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Gears")
set_target_properties(heightmap PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Heightmap") set_target_properties(heightmap PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Heightmap")
set_target_properties(particles PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Particles") set_target_properties(particles PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Particles")
set_target_properties(sharing PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Sharing")
set_target_properties(simple PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Simple") set_target_properties(simple PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Simple")
set_target_properties(splitview PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "SplitView") set_target_properties(splitview PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "SplitView")
set_target_properties(wave PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Wave") set_target_properties(wave PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Wave")

View File

@ -172,6 +172,7 @@ static GLfloat angle = 0.f;
/* OpenGL draw function & timing */ /* OpenGL draw function & timing */
static void draw(void) static void draw(void)
{ {
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix(); glPushMatrix();
@ -311,6 +312,7 @@ int main(int argc, char *argv[])
} }
glfwWindowHint(GLFW_DEPTH_BITS, 16); glfwWindowHint(GLFW_DEPTH_BITS, 16);
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);
window = glfwCreateWindow( 300, 300, "Gears", NULL, NULL ); window = glfwCreateWindow( 300, 300, "Gears", NULL, NULL );
if (!window) if (!window)

View File

@ -1,5 +1,5 @@
//======================================================================== //========================================================================
// Context sharing test program // Context sharing example
// Copyright (c) Camilla Löwy <elmindreda@glfw.org> // Copyright (c) Camilla Löwy <elmindreda@glfw.org>
// //
// This software is provided 'as-is', without any express or implied // This software is provided 'as-is', without any express or implied
@ -22,15 +22,10 @@
// distribution. // distribution.
// //
//======================================================================== //========================================================================
//
// This program is used to test sharing of objects between contexts
//
//========================================================================
#include <glad/glad.h> #include <glad/glad.h>
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <time.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -71,17 +66,6 @@ static void error_callback(int error, const char* description)
fprintf(stderr, "Error: %s\n", description); fprintf(stderr, "Error: %s\n", description);
} }
void APIENTRY debug_callback(GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar* message,
const void* user)
{
fprintf(stderr, "Error: %s\n", message);
}
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{ {
if (action == GLFW_PRESS && key == GLFW_KEY_ESCAPE) if (action == GLFW_PRESS && key == GLFW_KEY_ESCAPE)
@ -90,28 +74,15 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action,
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
int ch;
GLFWwindow* windows[2]; GLFWwindow* windows[2];
GLuint texture, program, vertex_buffer; GLuint texture, program, vertex_buffer;
GLint mvp_location, vpos_location, color_location, texture_location; GLint mvp_location, vpos_location, color_location, texture_location;
srand((unsigned int) time(NULL));
glfwSetErrorCallback(error_callback); glfwSetErrorCallback(error_callback);
if (!glfwInit()) if (!glfwInit())
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
while ((ch = getopt(argc, argv, "d")) != -1)
{
switch (ch)
{
case 'd':
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
break;
}
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
@ -134,12 +105,6 @@ int main(int argc, char** argv)
// pointers should be re-usable between them // pointers should be re-usable between them
gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
if (GLAD_GL_KHR_debug)
{
glDebugMessageCallback(debug_callback, NULL);
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE);
}
// Create the OpenGL objects inside the first context, created above // Create the OpenGL objects inside the first context, created above
// All objects will be shared with the second context, created below // All objects will be shared with the second context, created below
{ {
@ -150,6 +115,8 @@ int main(int argc, char** argv)
glGenTextures(1, &texture); glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture); glBindTexture(GL_TEXTURE_2D, texture);
srand((unsigned int) glfwGetTimerValue());
for (y = 0; y < 16; y++) for (y = 0; y < 16; y++)
{ {
for (x = 0; x < 16; x++) for (x = 0; x < 16; x++)
@ -235,8 +202,8 @@ int main(int argc, char** argv)
int i; int i;
const vec3 colors[2] = const vec3 colors[2] =
{ {
{ 0.3f, 0.4f, 1.f }, { 0.8f, 0.4f, 1.f },
{ 0.8f, 0.4f, 1.f } { 0.3f, 0.4f, 1.f }
}; };
for (i = 0; i < 2; i++) for (i = 0; i < 2; i++)

File diff suppressed because it is too large Load Diff

View File

@ -86,7 +86,10 @@ extern "C" {
// This is a workaround for the fact that glfw3.h needs to export APIENTRY (for // This is a workaround for the fact that glfw3.h needs to export APIENTRY (for
// example to allow applications to correctly declare a GL_ARB_debug_output // example to allow applications to correctly declare a GL_ARB_debug_output
// callback) but windows.h assumes no one will define APIENTRY before it does // callback) but windows.h assumes no one will define APIENTRY before it does
#if defined(GLFW_APIENTRY_DEFINED)
#undef APIENTRY #undef APIENTRY
#undef GLFW_APIENTRY_DEFINED
#endif
#include <windows.h> #include <windows.h>
#elif defined(GLFW_EXPOSE_NATIVE_COCOA) #elif defined(GLFW_EXPOSE_NATIVE_COCOA)
#include <ApplicationServices/ApplicationServices.h> #include <ApplicationServices/ApplicationServices.h>
@ -289,6 +292,56 @@ GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor);
* @ingroup native * @ingroup native
*/ */
GLFWAPI Window glfwGetX11Window(GLFWwindow* window); GLFWAPI Window glfwGetX11Window(GLFWwindow* window);
/*! @brief Sets the current primary selection to the specified string.
*
* @param[in] string A UTF-8 encoded string.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @pointer_lifetime The specified string is copied before this function
* returns.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref clipboard
* @sa glfwGetX11SelectionString
* @sa glfwSetClipboardString
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI void glfwSetX11SelectionString(const char* string);
/*! @brief Returns the contents of the current primary selection as a string.
*
* If the selection is empty or if its contents cannot be converted, `NULL`
* is returned and a @ref GLFW_FORMAT_UNAVAILABLE error is generated.
*
* @return The contents of the selection as a UTF-8 encoded string, or `NULL`
* if an [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @pointer_lifetime The returned string is allocated and freed by GLFW. You
* should not free it yourself. It is valid until the next call to @ref
* glfwGetX11SelectionString or @ref glfwSetX11SelectionString, or until the
* library is terminated.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref clipboard
* @sa glfwSetX11SelectionString
* @sa glfwGetClipboardString
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI const char* glfwGetX11SelectionString(void);
#endif #endif
#if defined(GLFW_EXPOSE_NATIVE_GLX) #if defined(GLFW_EXPOSE_NATIVE_GLX)
@ -394,9 +447,9 @@ GLFWAPI MirConnection* glfwGetMirDisplay(void);
*/ */
GLFWAPI int glfwGetMirMonitor(GLFWmonitor* monitor); GLFWAPI int glfwGetMirMonitor(GLFWmonitor* monitor);
/*! @brief Returns the `MirSurface*` of the specified window. /*! @brief Returns the `MirWindow*` of the specified window.
* *
* @return The `MirSurface*` of the specified window, or `NULL` if an * @return The `MirWindow*` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred. * [error](@ref error_handling) occurred.
* *
* @thread_safety This function may be called from any thread. Access is not * @thread_safety This function may be called from any thread. Access is not
@ -406,7 +459,7 @@ GLFWAPI int glfwGetMirMonitor(GLFWmonitor* monitor);
* *
* @ingroup native * @ingroup native
*/ */
GLFWAPI MirSurface* glfwGetMirWindow(GLFWwindow* window); GLFWAPI MirWindow* glfwGetMirWindow(GLFWwindow* window);
#endif #endif
#if defined(GLFW_EXPOSE_NATIVE_EGL) #if defined(GLFW_EXPOSE_NATIVE_EGL)

View File

@ -1,5 +1,5 @@
set(common_HEADERS internal.h set(common_HEADERS internal.h mappings.h
"${GLFW_BINARY_DIR}/src/glfw_config.h" "${GLFW_BINARY_DIR}/src/glfw_config.h"
"${GLFW_SOURCE_DIR}/include/GLFW/glfw3.h" "${GLFW_SOURCE_DIR}/include/GLFW/glfw3.h"
"${GLFW_SOURCE_DIR}/include/GLFW/glfw3native.h") "${GLFW_SOURCE_DIR}/include/GLFW/glfw3native.h")
@ -7,21 +7,21 @@ set(common_SOURCES context.c init.c input.c monitor.c vulkan.c window.c)
if (_GLFW_COCOA) if (_GLFW_COCOA)
set(glfw_HEADERS ${common_HEADERS} cocoa_platform.h cocoa_joystick.h set(glfw_HEADERS ${common_HEADERS} cocoa_platform.h cocoa_joystick.h
posix_tls.h nsgl_context.h egl_context.h osmesa_context.c) posix_thread.h nsgl_context.h egl_context.h osmesa_context.h)
set(glfw_SOURCES ${common_SOURCES} cocoa_init.m cocoa_joystick.m 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_thread.c
nsgl_context.m egl_context.c osmesa_context.c) nsgl_context.m egl_context.c osmesa_context.c)
elseif (_GLFW_WIN32) 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 osmesa_context.h) wgl_context.h egl_context.h osmesa_context.h)
set(glfw_SOURCES ${common_SOURCES} win32_init.c win32_joystick.c 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_thread.c win32_window.c
wgl_context.c egl_context.c osmesa_context.c) wgl_context.c egl_context.c osmesa_context.c)
elseif (_GLFW_X11) elseif (_GLFW_X11)
set(glfw_HEADERS ${common_HEADERS} x11_platform.h xkb_unicode.h posix_time.h set(glfw_HEADERS ${common_HEADERS} x11_platform.h xkb_unicode.h posix_time.h
posix_tls.h glx_context.h egl_context.h osmesa_context.h) posix_thread.h glx_context.h egl_context.h osmesa_context.h)
set(glfw_SOURCES ${common_SOURCES} x11_init.c x11_monitor.c x11_window.c set(glfw_SOURCES ${common_SOURCES} x11_init.c x11_monitor.c x11_window.c
xkb_unicode.c posix_time.c posix_tls.c glx_context.c xkb_unicode.c posix_time.c posix_thread.c glx_context.c
egl_context.c osmesa_context.c) egl_context.c osmesa_context.c)
if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
@ -33,32 +33,36 @@ elseif (_GLFW_X11)
endif() endif()
elseif (_GLFW_WAYLAND) elseif (_GLFW_WAYLAND)
set(glfw_HEADERS ${common_HEADERS} wl_platform.h linux_joystick.h set(glfw_HEADERS ${common_HEADERS} wl_platform.h linux_joystick.h
posix_time.h posix_tls.h xkb_unicode.h egl_context.h posix_time.h posix_thread.h xkb_unicode.h egl_context.h
osmesa_context.h) osmesa_context.h)
set(glfw_SOURCES ${common_SOURCES} wl_init.c wl_monitor.c wl_window.c 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_thread.c xkb_unicode.c
egl_context.c osmesa_context.c) egl_context.c osmesa_context.c)
ecm_add_wayland_client_protocol(glfw_SOURCES ecm_add_wayland_client_protocol(glfw_SOURCES
PROTOCOL PROTOCOL
${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/relative-pointer/relative-pointer-unstable-v1.xml "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/relative-pointer/relative-pointer-unstable-v1.xml"
BASENAME relative-pointer-unstable-v1) BASENAME relative-pointer-unstable-v1)
ecm_add_wayland_client_protocol(glfw_SOURCES ecm_add_wayland_client_protocol(glfw_SOURCES
PROTOCOL PROTOCOL
${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml"
BASENAME pointer-constraints-unstable-v1) BASENAME pointer-constraints-unstable-v1)
ecm_add_wayland_client_protocol(glfw_SOURCES
PROTOCOL
${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml
BASENAME idle-inhibit-unstable-v1)
elseif (_GLFW_MIR) elseif (_GLFW_MIR)
set(glfw_HEADERS ${common_HEADERS} mir_platform.h linux_joystick.h set(glfw_HEADERS ${common_HEADERS} mir_platform.h linux_joystick.h
posix_time.h posix_tls.h xkb_unicode.h egl_context.h posix_time.h posix_thread.h xkb_unicode.h egl_context.h
osmesa_context.h) osmesa_context.h)
set(glfw_SOURCES ${common_SOURCES} mir_init.c mir_monitor.c mir_window.c 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 linux_joystick.c posix_time.c posix_thread.c xkb_unicode.c
egl_context.c osmesa_context.c) egl_context.c osmesa_context.c)
elseif (_GLFW_OSMESA) elseif (_GLFW_OSMESA)
set(glfw_HEADERS ${common_HEADERS} null_platform.h null_joystick.h set(glfw_HEADERS ${common_HEADERS} null_platform.h null_joystick.h
posix_time.h posix_tls.h osmesa_context.h) posix_time.h posix_thread.h osmesa_context.h)
set(glfw_SOURCES ${common_SOURCES} null_init.c null_monitor.c null_window.c set(glfw_SOURCES ${common_SOURCES} null_init.c null_monitor.c null_window.c
null_joystick.c posix_time.c posix_tls.c osmesa_context.c) null_joystick.c posix_time.c posix_thread.c osmesa_context.c)
endif() endif()
if (APPLE) if (APPLE)
@ -66,6 +70,18 @@ if (APPLE)
set_source_files_properties(${glfw_SOURCES} PROPERTIES LANGUAGE C) set_source_files_properties(${glfw_SOURCES} PROPERTIES LANGUAGE C)
endif() endif()
# Make GCC and Clang warn about declarations that VS 2010 and 2012 won't accept
# for all source files that VS will build
if (${CMAKE_C_COMPILER_ID} STREQUAL GNU OR ${CMAKE_C_COMPILER_ID} STREQUAL Clang)
if (WIN32)
set(windows_SOURCES ${glfw_SOURCES})
else()
set(windows_SOURCES ${common_SOURCES})
endif()
set_source_files_properties(${windows_SOURCES} PROPERTIES
COMPILE_FLAGS -Wdeclaration-after-statement)
endif()
add_library(glfw ${glfw_SOURCES} ${glfw_HEADERS}) add_library(glfw ${glfw_SOURCES} ${glfw_HEADERS})
set_target_properties(glfw PROPERTIES set_target_properties(glfw PROPERTIES
OUTPUT_NAME ${GLFW_LIB_NAME} OUTPUT_NAME ${GLFW_LIB_NAME}
@ -74,10 +90,12 @@ set_target_properties(glfw PROPERTIES
POSITION_INDEPENDENT_CODE ON POSITION_INDEPENDENT_CODE ON
FOLDER "GLFW3") FOLDER "GLFW3")
target_compile_definitions(glfw PRIVATE -D_GLFW_USE_CONFIG_H) target_compile_definitions(glfw PRIVATE
_GLFW_USE_CONFIG_H
$<$<BOOL:${UNIX}>:_XOPEN_SOURCE=600>)
target_include_directories(glfw PUBLIC target_include_directories(glfw PUBLIC
$<BUILD_INTERFACE:${GLFW_SOURCE_DIR}/include> "$<BUILD_INTERFACE:${GLFW_SOURCE_DIR}/include>"
$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include>) "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include>")
target_include_directories(glfw PRIVATE target_include_directories(glfw PRIVATE
"${GLFW_SOURCE_DIR}/src" "${GLFW_SOURCE_DIR}/src"
"${GLFW_BINARY_DIR}/src" "${GLFW_BINARY_DIR}/src"
@ -118,7 +136,7 @@ if (BUILD_SHARED_LIBS)
target_compile_options(glfw PRIVATE "-fvisibility=hidden") target_compile_options(glfw PRIVATE "-fvisibility=hidden")
endif() endif()
target_compile_definitions(glfw INTERFACE -DGLFW_DLL) target_compile_definitions(glfw INTERFACE GLFW_DLL)
target_link_libraries(glfw PRIVATE ${glfw_LIBRARIES}) target_link_libraries(glfw PRIVATE ${glfw_LIBRARIES})
else() else()
target_link_libraries(glfw INTERFACE ${glfw_LIBRARIES}) target_link_libraries(glfw INTERFACE ${glfw_LIBRARIES})
@ -129,6 +147,10 @@ if (MSVC)
endif() endif()
if (GLFW_INSTALL) if (GLFW_INSTALL)
install(TARGETS glfw EXPORT glfwTargets DESTINATION lib${LIB_SUFFIX}) install(TARGETS glfw
EXPORT glfwTargets
RUNTIME DESTINATION "bin"
ARCHIVE DESTINATION "lib${LIB_SUFFIX}"
LIBRARY DESTINATION "lib${LIB_SUFFIX}")
endif() endif()

View File

@ -215,7 +215,8 @@ static GLFWbool updateUnicodeDataNS(void)
return GLFW_FALSE; return GLFW_FALSE;
} }
_glfw.ns.unicodeData = TISGetInputSourceProperty(_glfw.ns.inputSource, _glfw.ns.unicodeData =
TISGetInputSourceProperty(_glfw.ns.inputSource,
kTISPropertyUnicodeKeyLayoutData); kTISPropertyUnicodeKeyLayoutData);
if (!_glfw.ns.unicodeData) if (!_glfw.ns.unicodeData)
{ {
@ -232,7 +233,8 @@ static GLFWbool updateUnicodeDataNS(void)
static GLFWbool initializeTIS(void) static GLFWbool initializeTIS(void)
{ {
// This works only because Cocoa has already loaded it properly // This works only because Cocoa has already loaded it properly
_glfw.ns.tis.bundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HIToolbox")); _glfw.ns.tis.bundle =
CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HIToolbox"));
if (!_glfw.ns.tis.bundle) if (!_glfw.ns.tis.bundle)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, _glfwInputError(GLFW_PLATFORM_ERROR,
@ -311,9 +313,6 @@ int _glfwPlatformInit(void)
if (!initializeTIS()) if (!initializeTIS())
return GLFW_FALSE; return GLFW_FALSE;
if (!_glfwInitThreadLocalStoragePOSIX())
return GLFW_FALSE;
_glfwInitTimerNS(); _glfwInitTimerNS();
_glfwInitJoysticksNS(); _glfwInitJoysticksNS();
@ -355,14 +354,10 @@ void _glfwPlatformTerminate(void)
_glfw.ns.listener = nil; _glfw.ns.listener = nil;
} }
[_glfw.ns.cursor release];
_glfw.ns.cursor = nil;
free(_glfw.ns.clipboardString); free(_glfw.ns.clipboardString);
_glfwTerminateNSGL(); _glfwTerminateNSGL();
_glfwTerminateJoysticksNS(); _glfwTerminateJoysticksNS();
_glfwTerminateThreadLocalStoragePOSIX();
[_glfw.ns.autoreleasePool release]; [_glfw.ns.autoreleasePool release];
_glfw.ns.autoreleasePool = nil; _glfw.ns.autoreleasePool = nil;

View File

@ -24,9 +24,6 @@
// //
//======================================================================== //========================================================================
#ifndef _glfw3_cocoa_joystick_h_
#define _glfw3_cocoa_joystick_h_
#include <IOKit/IOKitLib.h> #include <IOKit/IOKitLib.h>
#include <IOKit/IOCFPlugIn.h> #include <IOKit/IOCFPlugIn.h>
#include <IOKit/hid/IOHIDLib.h> #include <IOKit/hid/IOHIDLib.h>
@ -35,6 +32,7 @@
#define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickNS ns #define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickNS ns
#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE #define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE
#define _GLFW_PLATFORM_MAPPING_NAME "Mac OS X"
// Cocoa-specific per-joystick data // Cocoa-specific per-joystick data
// //
@ -50,4 +48,3 @@ typedef struct _GLFWjoystickNS
void _glfwInitJoysticksNS(void); void _glfwInitJoysticksNS(void);
void _glfwTerminateJoysticksNS(void); void _glfwTerminateJoysticksNS(void);
#endif // _glfw3_cocoa_joystick_h_

View File

@ -43,6 +43,8 @@
typedef struct _GLFWjoyelementNS typedef struct _GLFWjoyelementNS
{ {
IOHIDElementRef native; IOHIDElementRef native;
uint32_t usage;
int index;
long minimum; long minimum;
long maximum; long maximum;
@ -53,32 +55,41 @@ typedef struct _GLFWjoyelementNS
// //
static long getElementValue(_GLFWjoystick* js, _GLFWjoyelementNS* element) static long getElementValue(_GLFWjoystick* js, _GLFWjoyelementNS* element)
{ {
IOReturn result = kIOReturnSuccess;
IOHIDValueRef valueRef; IOHIDValueRef valueRef;
long value = 0; long value = 0;
if (js && element && js->ns.device) if (js->ns.device)
{ {
result = IOHIDDeviceGetValue(js->ns.device, if (IOHIDDeviceGetValue(js->ns.device,
element->native, element->native,
&valueRef); &valueRef) == kIOReturnSuccess)
if (kIOReturnSuccess == result)
{ {
value = IOHIDValueGetIntegerValue(valueRef); value = IOHIDValueGetIntegerValue(valueRef);
// Record min and max for auto calibration
if (value < element->minimum)
element->minimum = value;
if (value > element->maximum)
element->maximum = value;
} }
} }
// Auto user scale
return value; return value;
} }
// Comparison function for matching the SDL element order
//
static CFComparisonResult compareElements(const void* fp,
const void* sp,
void* user)
{
const _GLFWjoyelementNS* fe = fp;
const _GLFWjoyelementNS* se = sp;
if (fe->usage < se->usage)
return kCFCompareLessThan;
if (fe->usage > se->usage)
return kCFCompareGreaterThan;
if (fe->index < se->index)
return kCFCompareLessThan;
if (fe->index > se->index)
return kCFCompareGreaterThan;
return kCFCompareEqualTo;
}
// Removes the specified joystick // Removes the specified joystick
// //
static void closeJoystick(_GLFWjoystick* js) static void closeJoystick(_GLFWjoystick* js)
@ -101,7 +112,7 @@ static void closeJoystick(_GLFWjoystick* js)
CFRelease(js->ns.hats); CFRelease(js->ns.hats);
_glfwFreeJoystick(js); _glfwFreeJoystick(js);
_glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_DISCONNECTED); _glfwInputJoystick(js, GLFW_DISCONNECTED);
} }
// Callback for user-initiated joystick addition // Callback for user-initiated joystick addition
@ -113,8 +124,10 @@ static void matchCallback(void* context,
{ {
int jid; int jid;
char name[256]; char name[256];
char guid[33];
CFIndex i; CFIndex i;
CFStringRef productKey; CFTypeRef property;
uint32_t vendor = 0, product = 0, version = 0;
_GLFWjoystick* js; _GLFWjoystick* js;
CFMutableArrayRef axes, buttons, hats; CFMutableArrayRef axes, buttons, hats;
@ -128,10 +141,10 @@ static void matchCallback(void* context,
buttons = CFArrayCreateMutable(NULL, 0, NULL); buttons = CFArrayCreateMutable(NULL, 0, NULL);
hats = CFArrayCreateMutable(NULL, 0, NULL); hats = CFArrayCreateMutable(NULL, 0, NULL);
productKey = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
if (productKey) if (property)
{ {
CFStringGetCString(productKey, CFStringGetCString(property,
name, name,
sizeof(name), sizeof(name),
kCFStringEncodingUTF8); kCFStringEncodingUTF8);
@ -139,12 +152,41 @@ static void matchCallback(void* context,
else else
strncpy(name, "Unknown", sizeof(name)); strncpy(name, "Unknown", sizeof(name));
property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey));
if (property)
CFNumberGetValue(property, kCFNumberSInt32Type, &vendor);
property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey));
if (property)
CFNumberGetValue(property, kCFNumberSInt32Type, &product);
property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVersionNumberKey));
if (property)
CFNumberGetValue(property, kCFNumberSInt32Type, &version);
// Generate a joystick GUID that matches the SDL 2.0.5+ one
if (vendor && product)
{
sprintf(guid, "03000000%02x%02x0000%02x%02x0000%02x%02x0000",
(uint8_t) vendor, (uint8_t) (vendor >> 8),
(uint8_t) product, (uint8_t) (product >> 8),
(uint8_t) version, (uint8_t) (version >> 8));
}
else
{
sprintf(guid, "05000000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00",
name[0], name[1], name[2], name[3],
name[4], name[5], name[6], name[7],
name[8], name[9], name[10]);
}
CFArrayRef elements = CFArrayRef elements =
IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone); IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone);
for (i = 0; i < CFArrayGetCount(elements); i++) for (i = 0; i < CFArrayGetCount(elements); i++)
{ {
IOHIDElementRef native = (IOHIDElementRef) CFArrayGetValueAtIndex(elements, i); IOHIDElementRef native = (IOHIDElementRef)
CFArrayGetValueAtIndex(elements, i);
if (CFGetTypeID(native) != IOHIDElementGetTypeID()) if (CFGetTypeID(native) != IOHIDElementGetTypeID())
continue; continue;
@ -158,11 +200,11 @@ static void matchCallback(void* context,
CFMutableArrayRef target = NULL; CFMutableArrayRef target = NULL;
switch (IOHIDElementGetUsagePage(native)) const uint32_t usage = IOHIDElementGetUsage(native);
const uint32_t page = IOHIDElementGetUsagePage(native);
if (page == kHIDPage_GenericDesktop)
{ {
case kHIDPage_GenericDesktop: switch (usage)
{
switch (IOHIDElementGetUsage(native))
{ {
case kHIDUsage_GD_X: case kHIDUsage_GD_X:
case kHIDUsage_GD_Y: case kHIDUsage_GD_Y:
@ -179,21 +221,16 @@ static void matchCallback(void* context,
target = hats; target = hats;
break; break;
} }
break;
} }
else if (page == kHIDPage_Button)
case kHIDPage_Button:
target = buttons; target = buttons;
break;
default:
break;
}
if (target) if (target)
{ {
_GLFWjoyelementNS* element = calloc(1, sizeof(_GLFWjoyelementNS)); _GLFWjoyelementNS* element = calloc(1, sizeof(_GLFWjoyelementNS));
element->native = native; element->native = native;
element->usage = usage;
element->index = (int) CFArrayGetCount(target);
element->minimum = IOHIDElementGetLogicalMin(native); element->minimum = IOHIDElementGetLogicalMin(native);
element->maximum = IOHIDElementGetLogicalMax(native); element->maximum = IOHIDElementGetLogicalMax(native);
CFArrayAppendValue(target, element); CFArrayAppendValue(target, element);
@ -202,17 +239,24 @@ static void matchCallback(void* context,
CFRelease(elements); CFRelease(elements);
js = _glfwAllocJoystick(name, CFArraySortValues(axes, CFRangeMake(0, CFArrayGetCount(axes)),
CFArrayGetCount(axes), compareElements, NULL);
CFArrayGetCount(buttons), CFArraySortValues(buttons, CFRangeMake(0, CFArrayGetCount(buttons)),
CFArrayGetCount(hats)); compareElements, NULL);
CFArraySortValues(hats, CFRangeMake(0, CFArrayGetCount(hats)),
compareElements, NULL);
js = _glfwAllocJoystick(name, guid,
(int) CFArrayGetCount(axes),
(int) CFArrayGetCount(buttons),
(int) CFArrayGetCount(hats));
js->ns.device = device; js->ns.device = device;
js->ns.axes = axes; js->ns.axes = axes;
js->ns.buttons = buttons; js->ns.buttons = buttons;
js->ns.hats = hats; js->ns.hats = hats;
_glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_CONNECTED); _glfwInputJoystick(js, GLFW_CONNECTED);
} }
// Callback for user-initiated joystick removal // Callback for user-initiated joystick removal
@ -335,11 +379,9 @@ void _glfwTerminateJoysticksNS(void)
////// GLFW platform API ////// ////// GLFW platform API //////
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
int _glfwPlatformPollJoystick(int jid, int mode) int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode)
{ {
_GLFWjoystick* js = _glfw.joysticks + jid; if (mode & _GLFW_POLL_AXES)
if (mode == _GLFW_POLL_AXES)
{ {
CFIndex i; CFIndex i;
@ -348,16 +390,25 @@ int _glfwPlatformPollJoystick(int jid, int mode)
_GLFWjoyelementNS* axis = (_GLFWjoyelementNS*) _GLFWjoyelementNS* axis = (_GLFWjoyelementNS*)
CFArrayGetValueAtIndex(js->ns.axes, i); CFArrayGetValueAtIndex(js->ns.axes, i);
const long value = getElementValue(js, axis); const long raw = getElementValue(js, axis);
const long delta = axis->maximum - axis->minimum; // Perform auto calibration
if (raw < axis->minimum)
axis->minimum = raw;
if (raw > axis->maximum)
axis->maximum = raw;
const long delta = axis->maximum - axis->minimum;
if (delta == 0) if (delta == 0)
_glfwInputJoystickAxis(jid, i, value); _glfwInputJoystickAxis(js, (int) i, 0.f);
else else
_glfwInputJoystickAxis(jid, i, (2.f * (value - axis->minimum) / delta) - 1.f); {
const float value = (2.f * (raw - axis->minimum) / delta) - 1.f;
_glfwInputJoystickAxis(js, (int) i, value);
} }
} }
else if (mode == _GLFW_POLL_BUTTONS) }
if (mode & _GLFW_POLL_BUTTONS)
{ {
CFIndex i; CFIndex i;
@ -365,8 +416,8 @@ int _glfwPlatformPollJoystick(int jid, int mode)
{ {
_GLFWjoyelementNS* button = (_GLFWjoyelementNS*) _GLFWjoyelementNS* button = (_GLFWjoyelementNS*)
CFArrayGetValueAtIndex(js->ns.buttons, i); CFArrayGetValueAtIndex(js->ns.buttons, i);
const char value = getElementValue(js, button) ? 1 : 0; const char value = getElementValue(js, button) - button->minimum;
_glfwInputJoystickButton(jid, i, value); _glfwInputJoystickButton(js, (int) i, value);
} }
for (i = 0; i < CFArrayGetCount(js->ns.hats); i++) for (i = 0; i < CFArrayGetCount(js->ns.hats); i++)
@ -386,14 +437,26 @@ int _glfwPlatformPollJoystick(int jid, int mode)
_GLFWjoyelementNS* hat = (_GLFWjoyelementNS*) _GLFWjoyelementNS* hat = (_GLFWjoyelementNS*)
CFArrayGetValueAtIndex(js->ns.hats, i); CFArrayGetValueAtIndex(js->ns.hats, i);
long state = getElementValue(js, hat); long state = getElementValue(js, hat) - hat->minimum;
if (state < 0 || state > 8) if (state < 0 || state > 8)
state = 8; state = 8;
_glfwInputJoystickHat(jid, i, states[state]); _glfwInputJoystickHat(js, (int) i, states[state]);
} }
} }
return js->present; return js->present;
} }
void _glfwPlatformUpdateGamepadGUID(char* guid)
{
if ((strncmp(guid + 4, "000000000000", 12) == 0) &&
(strncmp(guid + 20, "000000000000", 12) == 0))
{
char original[33];
strcpy(original, guid);
sprintf(guid, "03000000%.4s0000%.4s000000000000",
original, original + 16);
}
}

View File

@ -54,7 +54,8 @@ static char* getDisplayName(CGDirectDisplayID displayID)
while ((service = IOIteratorNext(it)) != 0) while ((service = IOIteratorNext(it)) != 0)
{ {
info = IODisplayCreateInfoDictionary(service, kIODisplayOnlyPreferredName); info = IODisplayCreateInfoDictionary(service,
kIODisplayOnlyPreferredName);
CFNumberRef vendorIDRef = CFNumberRef vendorIDRef =
CFDictionaryGetValue(info, CFSTR(kDisplayVendorID)); CFDictionaryGetValue(info, CFSTR(kDisplayVendorID));
@ -185,7 +186,13 @@ static CGDisplayFadeReservationToken beginFadeReservation(void)
CGDisplayFadeReservationToken token = kCGDisplayFadeReservationInvalidToken; CGDisplayFadeReservationToken token = kCGDisplayFadeReservationInvalidToken;
if (CGAcquireDisplayFadeReservation(5, &token) == kCGErrorSuccess) if (CGAcquireDisplayFadeReservation(5, &token) == kCGErrorSuccess)
CGDisplayFade(token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE); {
CGDisplayFade(token, 0.3,
kCGDisplayBlendNormal,
kCGDisplayBlendSolidColor,
0.0, 0.0, 0.0,
TRUE);
}
return token; return token;
} }
@ -196,7 +203,11 @@ static void endFadeReservation(CGDisplayFadeReservationToken token)
{ {
if (token != kCGDisplayFadeReservationInvalidToken) if (token != kCGDisplayFadeReservationInvalidToken)
{ {
CGDisplayFade(token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); CGDisplayFade(token, 0.5,
kCGDisplayBlendSolidColor,
kCGDisplayBlendNormal,
0.0, 0.0, 0.0,
FALSE);
CGReleaseDisplayFadeReservation(token); CGReleaseDisplayFadeReservation(token);
} }
} }
@ -218,6 +229,9 @@ void _glfwPollMonitorsNS(void)
displays = calloc(displayCount, sizeof(CGDirectDisplayID)); displays = calloc(displayCount, sizeof(CGDirectDisplayID));
CGGetOnlineDisplayList(displayCount, displays, &displayCount); CGGetOnlineDisplayList(displayCount, displays, &displayCount);
for (i = 0; i < _glfw.monitorCount; i++)
_glfw.monitors[i]->ns.screen = nil;
disconnectedCount = _glfw.monitorCount; disconnectedCount = _glfw.monitorCount;
if (disconnectedCount) if (disconnectedCount)
{ {
@ -360,6 +374,48 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
*ypos = (int) bounds.origin.y; *ypos = (int) bounds.origin.y;
} }
void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
float* xscale, float* yscale)
{
if (!monitor->ns.screen)
{
NSUInteger i;
NSArray* screens = [NSScreen screens];
for (i = 0; i < [screens count]; i++)
{
NSScreen* screen = [screens objectAtIndex:i];
NSNumber* displayID =
[[screen deviceDescription] objectForKey:@"NSScreenNumber"];
// HACK: Compare unit numbers instead of display IDs to work around
// display replacement on machines with automatic graphics
// switching
if (monitor->ns.unitNumber ==
CGDisplayUnitNumber([displayID unsignedIntValue]))
{
monitor->ns.screen = screen;
break;
}
}
if (i == [screens count])
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Cocoa: Failed to find a screen for monitor");
return;
}
}
const NSRect points = [monitor->ns.screen frame];
const NSRect pixels = [monitor->ns.screen convertRectToBacking:points];
if (xscale)
*xscale = (float) (pixels.size.width / points.size.width);
if (yscale)
*yscale = (float) (pixels.size.height / points.size.height);
}
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
{ {
CFArrayRef modes; CFArrayRef modes;

View File

@ -24,9 +24,6 @@
// //
//======================================================================== //========================================================================
#ifndef _glfw3_cocoa_platform_h_
#define _glfw3_cocoa_platform_h_
#include <stdint.h> #include <stdint.h>
#include <dlfcn.h> #include <dlfcn.h>
@ -51,7 +48,7 @@ typedef struct VkMacOSSurfaceCreateInfoMVK
typedef VkResult (APIENTRY *PFN_vkCreateMacOSSurfaceMVK)(VkInstance,const VkMacOSSurfaceCreateInfoMVK*,const VkAllocationCallbacks*,VkSurfaceKHR*); typedef VkResult (APIENTRY *PFN_vkCreateMacOSSurfaceMVK)(VkInstance,const VkMacOSSurfaceCreateInfoMVK*,const VkAllocationCallbacks*,VkSurfaceKHR*);
#include "posix_tls.h" #include "posix_thread.h"
#include "cocoa_joystick.h" #include "cocoa_joystick.h"
#include "nsgl_context.h" #include "nsgl_context.h"
#include "egl_context.h" #include "egl_context.h"
@ -66,7 +63,7 @@ typedef VkResult (APIENTRY *PFN_vkCreateMacOSSurfaceMVK)(VkInstance,const VkMacO
#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNS ns #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNS ns
#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryNS ns #define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryNS ns
#define _GLFW_PLATFORM_LIBRARY_TIME_STATE _GLFWtimeNS ns_time #define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerNS ns
#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorNS ns #define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorNS ns
#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorNS ns #define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorNS ns
@ -91,6 +88,10 @@ typedef struct _GLFWwindowNS
GLFWbool maximized; GLFWbool maximized;
// Cached window and framebuffer sizes used to filter out duplicate events
int width, height;
int fbWidth, fbHeight;
// The total sum of the distances the cursor has been warped // The total sum of the distances the cursor has been warped
// since the last cursor motion event was processed // since the last cursor motion event was processed
// This is kept to counteract Cocoa doing the same internally // This is kept to counteract Cocoa doing the same internally
@ -105,7 +106,7 @@ typedef struct _GLFWlibraryNS
CGEventSourceRef eventSource; CGEventSourceRef eventSource;
id delegate; id delegate;
id autoreleasePool; id autoreleasePool;
id cursor; GLFWbool cursorHidden;
TISInputSourceRef inputSource; TISInputSourceRef inputSource;
IOHIDManagerRef hidManager; IOHIDManagerRef hidManager;
id unicodeData; id unicodeData;
@ -138,6 +139,7 @@ typedef struct _GLFWmonitorNS
CGDirectDisplayID displayID; CGDirectDisplayID displayID;
CGDisplayModeRef previousMode; CGDisplayModeRef previousMode;
uint32_t unitNumber; uint32_t unitNumber;
id screen;
} _GLFWmonitorNS; } _GLFWmonitorNS;
@ -151,11 +153,11 @@ typedef struct _GLFWcursorNS
// Cocoa-specific global timer data // Cocoa-specific global timer data
// //
typedef struct _GLFWtimeNS typedef struct _GLFWtimerNS
{ {
uint64_t frequency; uint64_t frequency;
} _GLFWtimeNS; } _GLFWtimerNS;
void _glfwInitTimerNS(void); void _glfwInitTimerNS(void);
@ -164,4 +166,3 @@ void _glfwPollMonitorsNS(void);
GLFWbool _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired); GLFWbool _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired);
void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor); void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor);
#endif // _glfw3_cocoa_platform_h_

View File

@ -40,7 +40,7 @@ void _glfwInitTimerNS(void)
mach_timebase_info_data_t info; mach_timebase_info_data_t info;
mach_timebase_info(&info); mach_timebase_info(&info);
_glfw.ns_time.frequency = (info.denom * 1e9) / info.numer; _glfw.timer.ns.frequency = (info.denom * 1e9) / info.numer;
} }
@ -55,6 +55,6 @@ uint64_t _glfwPlatformGetTimerValue(void)
uint64_t _glfwPlatformGetTimerFrequency(void) uint64_t _glfwPlatformGetTimerFrequency(void)
{ {
return _glfw.ns_time.frequency; return _glfw.timer.ns.frequency;
} }

View File

@ -43,6 +43,7 @@
#define NSEventModifierFlagControl NSControlKeyMask #define NSEventModifierFlagControl NSControlKeyMask
#define NSEventModifierFlagOption NSAlternateKeyMask #define NSEventModifierFlagOption NSAlternateKeyMask
#define NSEventModifierFlagShift NSShiftKeyMask #define NSEventModifierFlagShift NSShiftKeyMask
#define NSEventModifierFlagCapsLock NSAlphaShiftKeyMask
#define NSEventModifierFlagDeviceIndependentFlagsMask NSDeviceIndependentModifierFlagsMask #define NSEventModifierFlagDeviceIndependentFlagsMask NSDeviceIndependentModifierFlagsMask
#define NSEventMaskAny NSAnyEventMask #define NSEventMaskAny NSAnyEventMask
#define NSEventTypeApplicationDefined NSApplicationDefined #define NSEventTypeApplicationDefined NSApplicationDefined
@ -88,19 +89,43 @@ static GLFWbool cursorInClientArea(_GLFWwindow* window)
return [window->ns.view mouse:pos inRect:[window->ns.view frame]]; return [window->ns.view mouse:pos inRect:[window->ns.view frame]];
} }
// Hides the cursor if not already hidden
//
static void hideCursor(_GLFWwindow* window)
{
if (!_glfw.ns.cursorHidden)
{
[NSCursor hide];
_glfw.ns.cursorHidden = GLFW_TRUE;
}
}
// Shows the cursor if not already shown
//
static void showCursor(_GLFWwindow* window)
{
if (_glfw.ns.cursorHidden)
{
[NSCursor unhide];
_glfw.ns.cursorHidden = GLFW_FALSE;
}
}
// Updates the cursor image according to its cursor mode // Updates the cursor image according to its cursor mode
// //
static void updateCursorImage(_GLFWwindow* window) static void updateCursorImage(_GLFWwindow* window)
{ {
if (window->cursorMode == GLFW_CURSOR_NORMAL) if (window->cursorMode == GLFW_CURSOR_NORMAL)
{ {
showCursor(window);
if (window->cursor) if (window->cursor)
[(NSCursor*) window->cursor->ns.object set]; [(NSCursor*) window->cursor->ns.object set];
else else
[[NSCursor arrowCursor] set]; [[NSCursor arrowCursor] set];
} }
else else
[(NSCursor*) _glfw.ns.cursor set]; hideCursor(window);
} }
// Transforms the specified y-coordinate between the CG display and NS screen // Transforms the specified y-coordinate between the CG display and NS screen
@ -153,6 +178,8 @@ static int translateFlags(NSUInteger flags)
mods |= GLFW_MOD_ALT; mods |= GLFW_MOD_ALT;
if (flags & NSEventModifierFlagCommand) if (flags & NSEventModifierFlagCommand)
mods |= GLFW_MOD_SUPER; mods |= GLFW_MOD_SUPER;
if (flags & NSEventModifierFlagCapsLock)
mods |= GLFW_MOD_CAPS_LOCK;
return mods; return mods;
} }
@ -204,13 +231,13 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
_GLFWwindow* window; _GLFWwindow* window;
} }
- (id)initWithGlfwWindow:(_GLFWwindow *)initWindow; - (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow;
@end @end
@implementation GLFWWindowDelegate @implementation GLFWWindowDelegate
- (id)initWithGlfwWindow:(_GLFWwindow *)initWindow - (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow
{ {
self = [super init]; self = [super init];
if (self != nil) if (self != nil)
@ -243,8 +270,21 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
const NSRect contentRect = [window->ns.view frame]; const NSRect contentRect = [window->ns.view frame];
const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect]; const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect];
if (fbRect.size.width != window->ns.fbWidth ||
fbRect.size.height != window->ns.fbHeight)
{
window->ns.fbWidth = fbRect.size.width;
window->ns.fbHeight = fbRect.size.height;
_glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height);
}
if (contentRect.size.width != window->ns.width ||
contentRect.size.height != window->ns.height)
{
window->ns.width = contentRect.size.width;
window->ns.height = contentRect.size.height;
_glfwInputWindowSize(window, contentRect.size.width, contentRect.size.height); _glfwInputWindowSize(window, contentRect.size.width, contentRect.size.height);
}
} }
- (void)windowDidMove:(NSNotification *)notification - (void)windowDidMove:(NSNotification *)notification
@ -357,27 +397,13 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
NSMutableAttributedString* markedText; NSMutableAttributedString* markedText;
} }
- (id)initWithGlfwWindow:(_GLFWwindow *)initWindow; - (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow;
@end @end
@implementation GLFWContentView @implementation GLFWContentView
+ (void)initialize - (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow
{
if (self == [GLFWContentView class])
{
if (_glfw.ns.cursor == nil)
{
NSImage* data = [[NSImage alloc] initWithSize:NSMakeSize(16, 16)];
_glfw.ns.cursor = [[NSCursor alloc] initWithImage:data
hotSpot:NSZeroPoint];
[data release];
}
}
}
- (id)initWithGlfwWindow:(_GLFWwindow *)initWindow
{ {
self = [super init]; self = [super init];
if (self != nil) if (self != nil)
@ -403,7 +429,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
- (BOOL)isOpaque - (BOOL)isOpaque
{ {
return YES; return [window->ns.object isOpaque];
} }
- (BOOL)canBecomeKeyView - (BOOL)canBecomeKeyView
@ -423,7 +449,10 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
- (id)makeBackingLayer - (id)makeBackingLayer
{ {
if (window->ns.layer)
return window->ns.layer; return window->ns.layer;
return [super makeBackingLayer];
} }
- (void)cursorUpdate:(NSEvent *)event - (void)cursorUpdate:(NSEvent *)event
@ -519,11 +548,17 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
- (void)mouseExited:(NSEvent *)event - (void)mouseExited:(NSEvent *)event
{ {
if (window->cursorMode == GLFW_CURSOR_HIDDEN)
showCursor(window);
_glfwInputCursorEnter(window, GLFW_FALSE); _glfwInputCursorEnter(window, GLFW_FALSE);
} }
- (void)mouseEntered:(NSEvent *)event - (void)mouseEntered:(NSEvent *)event
{ {
if (window->cursorMode == GLFW_CURSOR_HIDDEN)
hideCursor(window);
_glfwInputCursorEnter(window, GLFW_TRUE); _glfwInputCursorEnter(window, GLFW_TRUE);
} }
@ -532,7 +567,13 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
const NSRect contentRect = [window->ns.view frame]; const NSRect contentRect = [window->ns.view frame];
const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect]; const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect];
if (fbRect.size.width != window->ns.fbWidth ||
fbRect.size.height != window->ns.fbHeight)
{
window->ns.fbWidth = fbRect.size.width;
window->ns.fbHeight = fbRect.size.height;
_glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height);
}
} }
- (void)drawRect:(NSRect)rect - (void)drawRect:(NSRect)rect
@ -648,17 +689,17 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
[sender draggingLocation].x, [sender draggingLocation].x,
contentRect.size.height - [sender draggingLocation].y); contentRect.size.height - [sender draggingLocation].y);
const int count = [files count]; const NSUInteger count = [files count];
if (count) if (count)
{ {
NSEnumerator* e = [files objectEnumerator]; NSEnumerator* e = [files objectEnumerator];
char** paths = calloc(count, sizeof(char*)); char** paths = calloc(count, sizeof(char*));
int i; NSUInteger i;
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
paths[i] = strdup([[e nextObject] UTF8String]); paths[i] = strdup([[e nextObject] UTF8String]);
_glfwInputDrop(window, count, (const char**) paths); _glfwInputDrop(window, (int) count, (const char**) paths);
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
free(paths[i]); free(paths[i]);
@ -695,10 +736,11 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
selectedRange:(NSRange)selectedRange selectedRange:(NSRange)selectedRange
replacementRange:(NSRange)replacementRange replacementRange:(NSRange)replacementRange
{ {
[markedText release];
if ([string isKindOfClass:[NSAttributedString class]]) if ([string isKindOfClass:[NSAttributedString class]])
[markedText initWithAttributedString:string]; markedText = [[NSMutableAttributedString alloc] initWithAttributedString:string];
else else
[markedText initWithString:string]; markedText = [[NSMutableAttributedString alloc] initWithString:string];
} }
- (void)unmarkText - (void)unmarkText
@ -777,6 +819,11 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
return YES; return YES;
} }
- (BOOL)canBecomeMainWindow
{
return YES;
}
@end @end
@ -974,13 +1021,21 @@ static GLFWbool initializeAppKit(void)
[NSApp setDelegate:_glfw.ns.delegate]; [NSApp setDelegate:_glfw.ns.delegate];
[NSApp run]; [NSApp run];
// Press and Hold prevents some keys from emitting repeated characters
NSDictionary* defaults =
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO],
@"ApplePressAndHoldEnabled",
nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
return GLFW_TRUE; return GLFW_TRUE;
} }
// Create the Cocoa window // Create the Cocoa window
// //
static GLFWbool createNativeWindow(_GLFWwindow* window, static GLFWbool createNativeWindow(_GLFWwindow* window,
const _GLFWwndconfig* wndconfig) const _GLFWwndconfig* wndconfig,
const _GLFWfbconfig* fbconfig)
{ {
window->ns.delegate = [[GLFWWindowDelegate alloc] initWithGlfwWindow:window]; window->ns.delegate = [[GLFWWindowDelegate alloc] initWithGlfwWindow:window];
if (window->ns.delegate == nil) if (window->ns.delegate == nil)
@ -1022,7 +1077,9 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
else else
{ {
[window->ns.object center]; [window->ns.object center];
_glfw.ns.cascadePoint = [window->ns.object cascadeTopLeftFromPoint:_glfw.ns.cascadePoint]; _glfw.ns.cascadePoint =
NSPointToCGPoint([window->ns.object cascadeTopLeftFromPoint:
NSPointFromCGPoint(_glfw.ns.cascadePoint)]);
if (wndconfig->resizable) if (wndconfig->resizable)
{ {
@ -1047,6 +1104,12 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
if (wndconfig->ns.retina) if (wndconfig->ns.retina)
[window->ns.view setWantsBestResolutionOpenGLSurface:YES]; [window->ns.view setWantsBestResolutionOpenGLSurface:YES];
if (fbconfig->transparent)
{
[window->ns.object setOpaque:NO];
[window->ns.object setBackgroundColor:[NSColor clearColor]];
}
[window->ns.object setContentView:window->ns.view]; [window->ns.object setContentView:window->ns.view];
[window->ns.object makeFirstResponder:window->ns.view]; [window->ns.object makeFirstResponder:window->ns.view];
[window->ns.object setTitle:[NSString stringWithUTF8String:wndconfig->title]]; [window->ns.object setTitle:[NSString stringWithUTF8String:wndconfig->title]];
@ -1054,6 +1117,9 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
[window->ns.object setAcceptsMouseMovedEvents:YES]; [window->ns.object setAcceptsMouseMovedEvents:YES];
[window->ns.object setRestorable:NO]; [window->ns.object setRestorable:NO];
_glfwPlatformGetWindowSize(window, &window->ns.width, &window->ns.height);
_glfwPlatformGetFramebufferSize(window, &window->ns.fbWidth, &window->ns.fbHeight);
return GLFW_TRUE; return GLFW_TRUE;
} }
@ -1070,7 +1136,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
if (!initializeAppKit()) if (!initializeAppKit())
return GLFW_FALSE; return GLFW_FALSE;
if (!createNativeWindow(window, wndconfig)) if (!createNativeWindow(window, wndconfig, fbconfig))
return GLFW_FALSE; return GLFW_FALSE;
if (ctxconfig->client != GLFW_NO_API) if (ctxconfig->client != GLFW_NO_API)
@ -1141,7 +1207,11 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char *title) void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char *title)
{ {
[window->ns.object setTitle:[NSString stringWithUTF8String:title]]; NSString* string = [NSString stringWithUTF8String:title];
[window->ns.object setTitle:string];
// HACK: Set the miniwindow title explicitly as setTitle: doesn't update it
// if the window lacks NSWindowStyleMaskTitled
[window->ns.object setMiniwindowTitle:string];
} }
void _glfwPlatformSetWindowIcon(_GLFWwindow* window, void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
@ -1243,6 +1313,18 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
*bottom = contentRect.origin.y - frameRect.origin.y; *bottom = contentRect.origin.y - frameRect.origin.y;
} }
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
float* xscale, float* yscale)
{
const NSRect points = [window->ns.view frame];
const NSRect pixels = [window->ns.view convertRectToBacking:points];
if (xscale)
*xscale = (float) (pixels.size.width / points.size.width);
if (yscale)
*yscale = (float) (pixels.size.height / points.size.height);
}
void _glfwPlatformIconifyWindow(_GLFWwindow* window) void _glfwPlatformIconifyWindow(_GLFWwindow* window)
{ {
[window->ns.object miniaturize:nil]; [window->ns.object miniaturize:nil];
@ -1272,6 +1354,11 @@ void _glfwPlatformHideWindow(_GLFWwindow* window)
[window->ns.object orderOut:nil]; [window->ns.object orderOut:nil];
} }
void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
{
[NSApp requestUserAttention:NSInformationalRequest];
}
void _glfwPlatformFocusWindow(_GLFWwindow* window) void _glfwPlatformFocusWindow(_GLFWwindow* window)
{ {
// Make us the active application // Make us the active application
@ -1313,7 +1400,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
if (window->monitor) if (window->monitor)
releaseMonitor(window); releaseMonitor(window);
_glfwInputWindowMonitorChange(window, monitor); _glfwInputWindowMonitor(window, monitor);
// HACK: Allow the state cached in Cocoa to catch up to reality // HACK: Allow the state cached in Cocoa to catch up to reality
// TODO: Solve this in a less terrible way // TODO: Solve this in a less terrible way
@ -1323,28 +1410,6 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
[window->ns.object setStyleMask:styleMask]; [window->ns.object setStyleMask:styleMask];
[window->ns.object makeFirstResponder:window->ns.view]; [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) if (monitor)
{ {
[window->ns.object setLevel:NSMainMenuWindowLevel + 1]; [window->ns.object setLevel:NSMainMenuWindowLevel + 1];
@ -1354,6 +1419,12 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
} }
else else
{ {
NSRect 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 (window->numer != GLFW_DONT_CARE && if (window->numer != GLFW_DONT_CARE &&
window->denom != GLFW_DONT_CARE) window->denom != GLFW_DONT_CARE)
{ {
@ -1381,6 +1452,9 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
[window->ns.object setLevel:NSNormalWindowLevel]; [window->ns.object setLevel:NSNormalWindowLevel];
[window->ns.object setHasShadow:YES]; [window->ns.object setHasShadow:YES];
// HACK: Clearing NSWindowStyleMaskTitled resets and disables the window
// title property but the miniwindow title property is unaffected
[window->ns.object setTitle:[window->ns.object miniwindowTitle]];
} }
} }
@ -1404,6 +1478,11 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window)
return [window->ns.object isZoomed]; return [window->ns.object isZoomed];
} }
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
{
return ![window->ns.object isOpaque] && ![window->ns.view isOpaque];
}
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
{ {
[window->ns.object setStyleMask:getStyleMask(window)]; [window->ns.object setStyleMask:getStyleMask(window)];
@ -1423,6 +1502,16 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
[window->ns.object setLevel:NSNormalWindowLevel]; [window->ns.object setLevel:NSNormalWindowLevel];
} }
float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
{
return (float) [window->ns.object alphaValue];
}
void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
{
[window->ns.object setAlphaValue:opacity];
}
void _glfwPlatformPollEvents(void) void _glfwPlatformPollEvents(void)
{ {
for (;;) for (;;)
@ -1545,14 +1634,8 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
updateCursorImage(window); updateCursorImage(window);
} }
const char* _glfwPlatformGetKeyName(int key, int scancode) const char* _glfwPlatformGetScancodeName(int scancode)
{ {
if (key != GLFW_KEY_UNKNOWN)
scancode = _glfw.ns.scancodes[key];
if (!_glfwIsPrintable(_glfw.ns.keycodes[scancode]))
return NULL;
UInt32 deadKeyState = 0; UInt32 deadKeyState = 0;
UniChar characters[8]; UniChar characters[8];
UniCharCount characterCount = 0; UniCharCount characterCount = 0;
@ -1676,7 +1759,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
updateCursorImage(window); updateCursorImage(window);
} }
void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) void _glfwPlatformSetClipboardString(const char* string)
{ {
NSArray* types = [NSArray arrayWithObjects:NSStringPboardType, nil]; NSArray* types = [NSArray arrayWithObjects:NSStringPboardType, nil];
@ -1686,7 +1769,7 @@ void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string)
forType:NSStringPboardType]; forType:NSStringPboardType];
} }
const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) const char* _glfwPlatformGetClipboardString(void)
{ {
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];

View File

@ -45,7 +45,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
ctxconfig->source != GLFW_OSMESA_CONTEXT_API) ctxconfig->source != GLFW_OSMESA_CONTEXT_API)
{ {
_glfwInputError(GLFW_INVALID_ENUM, _glfwInputError(GLFW_INVALID_ENUM,
"Invalid context creation API %i", "Invalid context creation API 0x%08X",
ctxconfig->source); ctxconfig->source);
return GLFW_FALSE; return GLFW_FALSE;
} }
@ -55,7 +55,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
ctxconfig->client != GLFW_OPENGL_ES_API) ctxconfig->client != GLFW_OPENGL_ES_API)
{ {
_glfwInputError(GLFW_INVALID_ENUM, _glfwInputError(GLFW_INVALID_ENUM,
"Invalid client API %i", "Invalid client API 0x%08X",
ctxconfig->client); ctxconfig->client);
return GLFW_FALSE; return GLFW_FALSE;
} }
@ -85,7 +85,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
ctxconfig->profile != GLFW_OPENGL_COMPAT_PROFILE) ctxconfig->profile != GLFW_OPENGL_COMPAT_PROFILE)
{ {
_glfwInputError(GLFW_INVALID_ENUM, _glfwInputError(GLFW_INVALID_ENUM,
"Invalid OpenGL profile %i", "Invalid OpenGL profile 0x%08X",
ctxconfig->profile); ctxconfig->profile);
return GLFW_FALSE; return GLFW_FALSE;
} }
@ -134,7 +134,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
ctxconfig->robustness != GLFW_LOSE_CONTEXT_ON_RESET) ctxconfig->robustness != GLFW_LOSE_CONTEXT_ON_RESET)
{ {
_glfwInputError(GLFW_INVALID_ENUM, _glfwInputError(GLFW_INVALID_ENUM,
"Invalid context robustness mode %i", "Invalid context robustness mode 0x%08X",
ctxconfig->robustness); ctxconfig->robustness);
return GLFW_FALSE; return GLFW_FALSE;
} }
@ -146,7 +146,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
ctxconfig->release != GLFW_RELEASE_BEHAVIOR_FLUSH) ctxconfig->release != GLFW_RELEASE_BEHAVIOR_FLUSH)
{ {
_glfwInputError(GLFW_INVALID_ENUM, _glfwInputError(GLFW_INVALID_ENUM,
"Invalid context release behavior %i", "Invalid context release behavior 0x%08X",
ctxconfig->release); ctxconfig->release);
return GLFW_FALSE; return GLFW_FALSE;
} }
@ -208,6 +208,9 @@ const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired,
// not important to us here, so we count them as one // not important to us here, so we count them as one
missing++; missing++;
} }
if (desired->transparent != current->transparent)
missing++;
} }
// These polynomials make many small channel size differences matter // These polynomials make many small channel size differences matter
@ -331,7 +334,7 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig)
NULL NULL
}; };
window = _glfwPlatformGetCurrentContext(); window = _glfwPlatformGetTls(&_glfw.contextSlot);
window->context.source = ctxconfig->source; window->context.source = ctxconfig->source;
window->context.client = GLFW_OPENGL_API; window->context.client = GLFW_OPENGL_API;
@ -578,7 +581,7 @@ GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions
GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle) GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle)
{ {
_GLFWwindow* window = (_GLFWwindow*) handle; _GLFWwindow* window = (_GLFWwindow*) handle;
_GLFWwindow* previous = _glfwPlatformGetCurrentContext(); _GLFWwindow* previous = _glfwPlatformGetTls(&_glfw.contextSlot);
_GLFW_REQUIRE_INIT(); _GLFW_REQUIRE_INIT();
@ -601,7 +604,7 @@ GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle)
GLFWAPI GLFWwindow* glfwGetCurrentContext(void) GLFWAPI GLFWwindow* glfwGetCurrentContext(void)
{ {
_GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
return (GLFWwindow*) _glfwPlatformGetCurrentContext(); return _glfwPlatformGetTls(&_glfw.contextSlot);
} }
GLFWAPI void glfwSwapBuffers(GLFWwindow* handle) GLFWAPI void glfwSwapBuffers(GLFWwindow* handle)
@ -626,7 +629,7 @@ GLFWAPI void glfwSwapInterval(int interval)
_GLFW_REQUIRE_INIT(); _GLFW_REQUIRE_INIT();
window = _glfwPlatformGetCurrentContext(); window = _glfwPlatformGetTls(&_glfw.contextSlot);
if (!window) if (!window)
{ {
_glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL); _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL);
@ -643,7 +646,7 @@ GLFWAPI int glfwExtensionSupported(const char* extension)
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
window = _glfwPlatformGetCurrentContext(); window = _glfwPlatformGetTls(&_glfw.contextSlot);
if (!window) if (!window)
{ {
_glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL); _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL);
@ -708,7 +711,7 @@ GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname)
_GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
window = _glfwPlatformGetCurrentContext(); window = _glfwPlatformGetTls(&_glfw.contextSlot);
if (!window) if (!window)
{ {
_glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL); _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL);

View File

@ -121,9 +121,25 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig,
continue; continue;
#if defined(_GLFW_X11) #if defined(_GLFW_X11)
XVisualInfo vi = {0};
// Only consider EGLConfigs with associated Visuals // Only consider EGLConfigs with associated Visuals
if (!getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID)) vi.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID);
if (!vi.visualid)
continue; continue;
if (desired->transparent)
{
int count;
XVisualInfo* vis = XGetVisualInfo(_glfw.x11.display,
VisualIDMask, &vi,
&count);
if (vis)
{
u->transparent = _glfwIsVisualTransparentX11(vis[0].visual);
XFree(vis);
}
}
#endif // _GLFW_X11 #endif // _GLFW_X11
if (ctxconfig->client == GLFW_OPENGL_ES_API) if (ctxconfig->client == GLFW_OPENGL_ES_API)
@ -199,12 +215,12 @@ static void makeContextCurrentEGL(_GLFWwindow* window)
} }
} }
_glfwPlatformSetCurrentContext(window); _glfwPlatformSetTls(&_glfw.contextSlot, window);
} }
static void swapBuffersEGL(_GLFWwindow* window) static void swapBuffersEGL(_GLFWwindow* window)
{ {
if (window != _glfwPlatformGetCurrentContext()) if (window != _glfwPlatformGetTls(&_glfw.contextSlot))
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, _glfwInputError(GLFW_PLATFORM_ERROR,
"EGL: The context must be current on the calling thread when swapping buffers"); "EGL: The context must be current on the calling thread when swapping buffers");
@ -233,7 +249,7 @@ static int extensionSupportedEGL(const char* extension)
static GLFWglproc getProcAddressEGL(const char* procname) static GLFWglproc getProcAddressEGL(const char* procname)
{ {
_GLFWwindow* window = _glfwPlatformGetCurrentContext(); _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot);
if (window->context.egl.client) if (window->context.egl.client)
{ {
@ -286,11 +302,15 @@ GLFWbool _glfwInitEGL(void)
int i; int i;
const char* sonames[] = const char* sonames[] =
{ {
#if defined(_GLFW_WIN32) #if defined(_GLFW_EGL_LIBRARY)
_GLFW_EGL_LIBRARY,
#elif defined(_GLFW_WIN32)
"libEGL.dll", "libEGL.dll",
"EGL.dll", "EGL.dll",
#elif defined(_GLFW_COCOA) #elif defined(_GLFW_COCOA)
"libEGL.dylib", "libEGL.dylib",
#elif defined(__CYGWIN__)
"libEGL-1.so",
#else #else
"libEGL.so.1", "libEGL.so.1",
#endif #endif
@ -401,6 +421,8 @@ GLFWbool _glfwInitEGL(void)
extensionSupportedEGL("EGL_KHR_gl_colorspace"); extensionSupportedEGL("EGL_KHR_gl_colorspace");
_glfw.egl.KHR_get_all_proc_addresses = _glfw.egl.KHR_get_all_proc_addresses =
extensionSupportedEGL("EGL_KHR_get_all_proc_addresses"); extensionSupportedEGL("EGL_KHR_get_all_proc_addresses");
_glfw.egl.KHR_context_flush_control =
extensionSupportedEGL("EGL_KHR_context_flush_control");
return GLFW_TRUE; return GLFW_TRUE;
} }
@ -422,11 +444,11 @@ void _glfwTerminateEGL(void)
} }
} }
#define setEGLattrib(attribName, attribValue) \ #define setAttrib(a, v) \
{ \ { \
attribs[index++] = attribName; \ assert((size_t) (index + 1) < sizeof(attribs) / sizeof(attribs[0])); \
attribs[index++] = attribValue; \ attribs[index++] = a; \
assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ attribs[index++] = v; \
} }
// Create the OpenGL or OpenGL ES context // Create the OpenGL or OpenGL ES context
@ -438,6 +460,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
EGLint attribs[40]; EGLint attribs[40];
EGLConfig config; EGLConfig config;
EGLContext share = NULL; EGLContext share = NULL;
int index = 0;
if (!_glfw.egl.display) if (!_glfw.egl.display)
{ {
@ -478,7 +501,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
if (_glfw.egl.KHR_create_context) if (_glfw.egl.KHR_create_context)
{ {
int index = 0, mask = 0, flags = 0; int mask = 0, flags = 0;
if (ctxconfig->client == GLFW_OPENGL_API) if (ctxconfig->client == GLFW_OPENGL_API)
{ {
@ -489,12 +512,6 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
mask |= EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR; mask |= EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR;
else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE)
mask |= EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR; mask |= EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR;
if (_glfw.egl.KHR_create_context_no_error)
{
if (ctxconfig->noerror)
flags |= EGL_CONTEXT_OPENGL_NO_ERROR_KHR;
}
} }
if (ctxconfig->debug) if (ctxconfig->debug)
@ -504,44 +521,57 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
{ {
if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION)
{ {
setEGLattrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR,
EGL_NO_RESET_NOTIFICATION_KHR); EGL_NO_RESET_NOTIFICATION_KHR);
} }
else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET)
{ {
setEGLattrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR,
EGL_LOSE_CONTEXT_ON_RESET_KHR); EGL_LOSE_CONTEXT_ON_RESET_KHR);
} }
flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR; flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR;
} }
if (ctxconfig->noerror)
{
if (_glfw.egl.KHR_create_context_no_error)
setAttrib(EGL_CONTEXT_OPENGL_NO_ERROR_KHR, GLFW_TRUE);
}
if (ctxconfig->major != 1 || ctxconfig->minor != 0) if (ctxconfig->major != 1 || ctxconfig->minor != 0)
{ {
setEGLattrib(EGL_CONTEXT_MAJOR_VERSION_KHR, ctxconfig->major); setAttrib(EGL_CONTEXT_MAJOR_VERSION_KHR, ctxconfig->major);
setEGLattrib(EGL_CONTEXT_MINOR_VERSION_KHR, ctxconfig->minor); setAttrib(EGL_CONTEXT_MINOR_VERSION_KHR, ctxconfig->minor);
} }
if (mask) if (mask)
setEGLattrib(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, mask); setAttrib(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, mask);
if (flags) if (flags)
setEGLattrib(EGL_CONTEXT_FLAGS_KHR, flags); setAttrib(EGL_CONTEXT_FLAGS_KHR, flags);
setEGLattrib(EGL_NONE, EGL_NONE);
} }
else else
{ {
int index = 0;
if (ctxconfig->client == GLFW_OPENGL_ES_API) if (ctxconfig->client == GLFW_OPENGL_ES_API)
setEGLattrib(EGL_CONTEXT_CLIENT_VERSION, ctxconfig->major); setAttrib(EGL_CONTEXT_CLIENT_VERSION, ctxconfig->major);
setEGLattrib(EGL_NONE, EGL_NONE);
} }
// Context release behaviors (GL_KHR_context_flush_control) are not yet if (_glfw.egl.KHR_context_flush_control)
// supported on EGL but are not a hard constraint, so ignore and continue {
if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE)
{
setAttrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR,
EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR);
}
else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH)
{
setAttrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR,
EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR);
}
}
setAttrib(EGL_NONE, EGL_NONE);
window->context.egl.handle = eglCreateContext(_glfw.egl.display, window->context.egl.handle = eglCreateContext(_glfw.egl.display,
config, share, attribs); config, share, attribs);
@ -561,12 +591,10 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
if (fbconfig->sRGB) if (fbconfig->sRGB)
{ {
if (_glfw.egl.KHR_gl_colorspace) if (_glfw.egl.KHR_gl_colorspace)
{ setAttrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR);
setEGLattrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR);
}
} }
setEGLattrib(EGL_NONE, EGL_NONE); setAttrib(EGL_NONE, EGL_NONE);
} }
window->context.egl.surface = window->context.egl.surface =
@ -591,7 +619,9 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
const char** sonames; const char** sonames;
const char* es1sonames[] = const char* es1sonames[] =
{ {
#if defined(_GLFW_WIN32) #if defined(_GLFW_GLESV1_LIBRARY)
_GLFW_GLESV1_LIBRARY,
#elif defined(_GLFW_WIN32)
"GLESv1_CM.dll", "GLESv1_CM.dll",
"libGLES_CM.dll", "libGLES_CM.dll",
#elif defined(_GLFW_COCOA) #elif defined(_GLFW_COCOA)
@ -604,11 +634,15 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
}; };
const char* es2sonames[] = const char* es2sonames[] =
{ {
#if defined(_GLFW_WIN32) #if defined(_GLFW_GLESV2_LIBRARY)
_GLFW_GLESV2_LIBRARY,
#elif defined(_GLFW_WIN32)
"GLESv2.dll", "GLESv2.dll",
"libGLESv2.dll", "libGLESv2.dll",
#elif defined(_GLFW_COCOA) #elif defined(_GLFW_COCOA)
"libGLESv2.dylib", "libGLESv2.dylib",
#elif defined(__CYGWIN__)
"libGLESv2-2.so",
#else #else
"libGLESv2.so.2", "libGLESv2.so.2",
#endif #endif
@ -616,7 +650,9 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
}; };
const char* glsonames[] = const char* glsonames[] =
{ {
#if defined(_GLFW_WIN32) #if defined(_GLFW_OPENGL_LIBRARY)
_GLFW_OPENGL_LIBRARY,
#elif defined(_GLFW_WIN32)
#elif defined(_GLFW_COCOA) #elif defined(_GLFW_COCOA)
#else #else
"libGL.so.1", "libGL.so.1",
@ -664,12 +700,13 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
return GLFW_TRUE; return GLFW_TRUE;
} }
#undef setEGLattrib #undef setAttrib
// Returns the Visual and depth of the chosen EGLConfig // Returns the Visual and depth of the chosen EGLConfig
// //
#if defined(_GLFW_X11) #if defined(_GLFW_X11)
GLFWbool _glfwChooseVisualEGL(const _GLFWctxconfig* ctxconfig, GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig, const _GLFWfbconfig* fbconfig,
Visual** visual, int* depth) Visual** visual, int* depth)
{ {

View File

@ -25,9 +25,6 @@
// //
//======================================================================== //========================================================================
#ifndef _glfw3_egl_context_h_
#define _glfw3_egl_context_h_
#if defined(_GLFW_USE_EGLPLATFORM_H) #if defined(_GLFW_USE_EGLPLATFORM_H)
#include <EGL/eglplatform.h> #include <EGL/eglplatform.h>
#elif defined(_GLFW_WIN32) #elif defined(_GLFW_WIN32)
@ -110,6 +107,9 @@ typedef MirEGLNativeWindowType EGLNativeWindowType;
#define EGL_CONTEXT_OPENGL_NO_ERROR_KHR 0x31b3 #define EGL_CONTEXT_OPENGL_NO_ERROR_KHR 0x31b3
#define EGL_GL_COLORSPACE_KHR 0x309d #define EGL_GL_COLORSPACE_KHR 0x309d
#define EGL_GL_COLORSPACE_SRGB_KHR 0x3089 #define EGL_GL_COLORSPACE_SRGB_KHR 0x3089
#define EGL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x2097
#define EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR 0
#define EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x2098
typedef int EGLint; typedef int EGLint;
typedef unsigned int EGLBoolean; typedef unsigned int EGLBoolean;
@ -181,6 +181,7 @@ typedef struct _GLFWlibraryEGL
GLFWbool KHR_create_context_no_error; GLFWbool KHR_create_context_no_error;
GLFWbool KHR_gl_colorspace; GLFWbool KHR_gl_colorspace;
GLFWbool KHR_get_all_proc_addresses; GLFWbool KHR_get_all_proc_addresses;
GLFWbool KHR_context_flush_control;
void* handle; void* handle;
@ -210,9 +211,9 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig, const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig); const _GLFWfbconfig* fbconfig);
#if defined(_GLFW_X11) #if defined(_GLFW_X11)
GLFWbool _glfwChooseVisualEGL(const _GLFWctxconfig* ctxconfig, GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig, const _GLFWfbconfig* fbconfig,
Visual** visual, int* depth); Visual** visual, int* depth);
#endif /*_GLFW_X11*/ #endif /*_GLFW_X11*/
#endif // _glfw3_egl_context_h_

View File

@ -55,3 +55,6 @@
// Define this to 1 to force use of high-performance GPU on hybrid systems // Define this to 1 to force use of high-performance GPU on hybrid systems
#cmakedefine _GLFW_USE_HYBRID_HPG #cmakedefine _GLFW_USE_HYBRID_HPG
// Define this to 1 if xkbcommon supports the compose key
#cmakedefine HAVE_XKBCOMMON_COMPOSE_H

View File

@ -47,7 +47,8 @@ static int getGLXFBConfigAttrib(GLXFBConfig fbconfig, int attrib)
// Return the GLXFBConfig most closely matching the specified hints // Return the GLXFBConfig most closely matching the specified hints
// //
static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* result) static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired,
GLXFBConfig* result)
{ {
GLXFBConfig* nativeConfigs; GLXFBConfig* nativeConfigs;
_GLFWfbconfig* usableConfigs; _GLFWfbconfig* usableConfigs;
@ -64,7 +65,7 @@ static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* res
nativeConfigs = nativeConfigs =
glXGetFBConfigs(_glfw.x11.display, _glfw.x11.screen, &nativeCount); glXGetFBConfigs(_glfw.x11.display, _glfw.x11.screen, &nativeCount);
if (!nativeCount) if (!nativeConfigs || !nativeCount)
{ {
_glfwInputError(GLFW_API_UNAVAILABLE, "GLX: No GLXFBConfigs returned"); _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: No GLXFBConfigs returned");
return GLFW_FALSE; return GLFW_FALSE;
@ -89,6 +90,16 @@ static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* res
continue; continue;
} }
if (desired->transparent)
{
XVisualInfo* vi = glXGetVisualFromFBConfig(_glfw.x11.display, n);
if (vi)
{
u->transparent = _glfwIsVisualTransparentX11(vi->visual);
XFree(vi);
}
}
u->redBits = getGLXFBConfigAttrib(n, GLX_RED_SIZE); u->redBits = getGLXFBConfigAttrib(n, GLX_RED_SIZE);
u->greenBits = getGLXFBConfigAttrib(n, GLX_GREEN_SIZE); u->greenBits = getGLXFBConfigAttrib(n, GLX_GREEN_SIZE);
u->blueBits = getGLXFBConfigAttrib(n, GLX_BLUE_SIZE); u->blueBits = getGLXFBConfigAttrib(n, GLX_BLUE_SIZE);
@ -165,7 +176,7 @@ static void makeContextCurrentGLX(_GLFWwindow* window)
} }
} }
_glfwPlatformSetCurrentContext(window); _glfwPlatformSetTls(&_glfw.contextSlot, window);
} }
static void swapBuffersGLX(_GLFWwindow* window) static void swapBuffersGLX(_GLFWwindow* window)
@ -175,7 +186,7 @@ static void swapBuffersGLX(_GLFWwindow* window)
static void swapIntervalGLX(int interval) static void swapIntervalGLX(int interval)
{ {
_GLFWwindow* window = _glfwPlatformGetCurrentContext(); _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot);
if (_glfw.glx.EXT_swap_control) if (_glfw.glx.EXT_swap_control)
{ {
@ -244,7 +255,9 @@ GLFWbool _glfwInitGLX(void)
int i; int i;
const char* sonames[] = const char* sonames[] =
{ {
#if defined(__CYGWIN__) #if defined(_GLFW_GLX_LIBRARY)
_GLFW_GLX_LIBRARY,
#elif defined(__CYGWIN__)
"libGL-1.so", "libGL-1.so",
#else #else
"libGL.so.1", "libGL.so.1",
@ -397,6 +410,9 @@ GLFWbool _glfwInitGLX(void)
if (extensionSupportedGLX("GLX_EXT_create_context_es2_profile")) if (extensionSupportedGLX("GLX_EXT_create_context_es2_profile"))
_glfw.glx.EXT_create_context_es2_profile = GLFW_TRUE; _glfw.glx.EXT_create_context_es2_profile = GLFW_TRUE;
if (extensionSupportedGLX("GLX_ARB_create_context_no_error"))
_glfw.glx.ARB_create_context_no_error = GLFW_TRUE;
if (extensionSupportedGLX("GLX_ARB_context_flush_control")) if (extensionSupportedGLX("GLX_ARB_context_flush_control"))
_glfw.glx.ARB_context_flush_control = GLFW_TRUE; _glfw.glx.ARB_context_flush_control = GLFW_TRUE;
@ -417,11 +433,11 @@ void _glfwTerminateGLX(void)
} }
} }
#define setGLXattrib(attribName, attribValue) \ #define setAttrib(a, v) \
{ \ { \
attribs[index++] = attribName; \ assert((size_t) (index + 1) < sizeof(attribs) / sizeof(attribs[0])); \
attribs[index++] = attribValue; \ attribs[index++] = a; \
assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ attribs[index++] = v; \
} }
// Create the OpenGL or OpenGL ES context // Create the OpenGL or OpenGL ES context
@ -498,8 +514,6 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
if (ctxconfig->debug) if (ctxconfig->debug)
flags |= GLX_CONTEXT_DEBUG_BIT_ARB; flags |= GLX_CONTEXT_DEBUG_BIT_ARB;
if (ctxconfig->noerror)
flags |= GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR;
if (ctxconfig->robustness) if (ctxconfig->robustness)
{ {
@ -507,12 +521,12 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
{ {
if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION)
{ {
setGLXattrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, setAttrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
GLX_NO_RESET_NOTIFICATION_ARB); GLX_NO_RESET_NOTIFICATION_ARB);
} }
else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET)
{ {
setGLXattrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, setAttrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
GLX_LOSE_CONTEXT_ON_RESET_ARB); GLX_LOSE_CONTEXT_ON_RESET_ARB);
} }
@ -526,33 +540,39 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
{ {
if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE)
{ {
setGLXattrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, setAttrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB,
GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB);
} }
else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH)
{ {
setGLXattrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, setAttrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB,
GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB);
} }
} }
} }
if (ctxconfig->noerror)
{
if (_glfw.glx.ARB_create_context_no_error)
setAttrib(GLX_CONTEXT_OPENGL_NO_ERROR_ARB, GLFW_TRUE);
}
// NOTE: Only request an explicitly versioned context when necessary, as // NOTE: Only request an explicitly versioned context when necessary, as
// explicitly requesting version 1.0 does not always return the // explicitly requesting version 1.0 does not always return the
// highest version supported by the driver // highest version supported by the driver
if (ctxconfig->major != 1 || ctxconfig->minor != 0) if (ctxconfig->major != 1 || ctxconfig->minor != 0)
{ {
setGLXattrib(GLX_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); setAttrib(GLX_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major);
setGLXattrib(GLX_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); setAttrib(GLX_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor);
} }
if (mask) if (mask)
setGLXattrib(GLX_CONTEXT_PROFILE_MASK_ARB, mask); setAttrib(GLX_CONTEXT_PROFILE_MASK_ARB, mask);
if (flags) if (flags)
setGLXattrib(GLX_CONTEXT_FLAGS_ARB, flags); setAttrib(GLX_CONTEXT_FLAGS_ARB, flags);
setGLXattrib(None, None); setAttrib(None, None);
window->context.glx.handle = window->context.glx.handle =
_glfw.glx.CreateContextAttribsARB(_glfw.x11.display, _glfw.glx.CreateContextAttribsARB(_glfw.x11.display,
@ -609,11 +629,12 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
return GLFW_TRUE; return GLFW_TRUE;
} }
#undef setGLXattrib #undef setAttrib
// Returns the Visual and depth of the chosen GLXFBConfig // Returns the Visual and depth of the chosen GLXFBConfig
// //
GLFWbool _glfwChooseVisualGLX(const _GLFWctxconfig* ctxconfig, GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig, const _GLFWfbconfig* fbconfig,
Visual** visual, int* depth) Visual** visual, int* depth)
{ {

View File

@ -25,9 +25,6 @@
// //
//======================================================================== //========================================================================
#ifndef _glfw3_glx_context_h_
#define _glfw3_glx_context_h_
#define GLX_VENDOR 1 #define GLX_VENDOR 1
#define GLX_RGBA_BIT 0x00000001 #define GLX_RGBA_BIT 0x00000001
#define GLX_WINDOW_BIT 0x00000001 #define GLX_WINDOW_BIT 0x00000001
@ -67,6 +64,7 @@
#define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 #define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097
#define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 #define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0
#define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 #define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098
#define GLX_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3
typedef XID GLXWindow; typedef XID GLXWindow;
typedef XID GLXDrawable; typedef XID GLXDrawable;
@ -165,19 +163,19 @@ typedef struct _GLFWlibraryGLX
GLFWbool ARB_create_context_profile; GLFWbool ARB_create_context_profile;
GLFWbool ARB_create_context_robustness; GLFWbool ARB_create_context_robustness;
GLFWbool EXT_create_context_es2_profile; GLFWbool EXT_create_context_es2_profile;
GLFWbool ARB_create_context_no_error;
GLFWbool ARB_context_flush_control; GLFWbool ARB_context_flush_control;
} _GLFWlibraryGLX; } _GLFWlibraryGLX;
GLFWbool _glfwInitGLX(void); GLFWbool _glfwInitGLX(void);
void _glfwTerminateGLX(void); void _glfwTerminateGLX(void);
GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig, const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig); const _GLFWfbconfig* fbconfig);
void _glfwDestroyContextGLX(_GLFWwindow* window); void _glfwDestroyContextGLX(_GLFWwindow* window);
GLFWbool _glfwChooseVisualGLX(const _GLFWctxconfig* ctxconfig, GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig, const _GLFWfbconfig* fbconfig,
Visual** visual, int* depth); Visual** visual, int* depth);
#endif // _glfw3_glx_context_h_

View File

@ -26,11 +26,13 @@
//======================================================================== //========================================================================
#include "internal.h" #include "internal.h"
#include "mappings.h"
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#include <assert.h>
// The global variables below comprise all global data in GLFW. // The global variables below comprise all global data in GLFW.
@ -43,21 +45,26 @@ _GLFWlibrary _glfw = { GLFW_FALSE };
// These are outside of _glfw so they can be used before initialization and // These are outside of _glfw so they can be used before initialization and
// after termination // after termination
// //
static _GLFWerror _glfwMainThreadError;
static GLFWerrorfun _glfwErrorCallback; static GLFWerrorfun _glfwErrorCallback;
static _GLFWinitconfig _glfwInitHints = static _GLFWinitconfig _glfwInitHints =
{ {
GLFW_TRUE, // hat buttons GLFW_TRUE, // hat buttons
{ {
GLFW_TRUE, // menubar GLFW_TRUE, // macOS menu bar
GLFW_TRUE // chdir GLFW_TRUE // macOS bundle chdir
},
{
"", // X11 WM_CLASS name
"" // X11 WM_CLASS class
} }
}; };
// Returns a generic string representation of the specified error // Returns a generic string representation of the specified error
// //
static const char* getErrorString(int error) static const char* getErrorString(int code)
{ {
switch (error) switch (code)
{ {
case GLFW_NOT_INITIALIZED: case GLFW_NOT_INITIALIZED:
return "The GLFW library is not initialized"; return "The GLFW library is not initialized";
@ -110,9 +117,26 @@ static void terminate(void)
_glfw.monitors = NULL; _glfw.monitors = NULL;
_glfw.monitorCount = 0; _glfw.monitorCount = 0;
free(_glfw.mappings);
_glfw.mappings = NULL;
_glfw.mappingCount = 0;
_glfwTerminateVulkan(); _glfwTerminateVulkan();
_glfwPlatformTerminate(); _glfwPlatformTerminate();
_glfw.initialized = GLFW_FALSE;
while (_glfw.errorListHead)
{
_GLFWerror* error = _glfw.errorListHead;
_glfw.errorListHead = error->next;
free(error);
}
_glfwPlatformDestroyTls(&_glfw.contextSlot);
_glfwPlatformDestroyTls(&_glfw.errorSlot);
_glfwPlatformDestroyMutex(&_glfw.errorLock);
memset(&_glfw, 0, sizeof(_glfw)); memset(&_glfw, 0, sizeof(_glfw));
} }
@ -121,32 +145,45 @@ static void terminate(void)
////// GLFW event API ////// ////// GLFW event API //////
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
void _glfwInputError(int error, const char* format, ...) void _glfwInputError(int code, const char* format, ...)
{ {
if (_glfwErrorCallback) _GLFWerror* error;
{ char description[_GLFW_MESSAGE_SIZE];
char buffer[8192];
const char* description;
if (format) if (format)
{ {
int count;
va_list vl; va_list vl;
va_start(vl, format); va_start(vl, format);
count = vsnprintf(buffer, sizeof(buffer), format, vl); vsnprintf(description, sizeof(description), format, vl);
va_end(vl); va_end(vl);
if (count < 0) description[sizeof(description) - 1] = '\0';
buffer[sizeof(buffer) - 1] = '\0';
description = buffer;
} }
else else
description = getErrorString(error); strcpy(description, getErrorString(code));
_glfwErrorCallback(error, description); if (_glfw.initialized)
{
error = _glfwPlatformGetTls(&_glfw.errorSlot);
if (!error)
{
error = calloc(1, sizeof(_GLFWerror));
_glfwPlatformSetTls(&_glfw.errorSlot, error);
_glfwPlatformLockMutex(&_glfw.errorLock);
error->next = _glfw.errorListHead;
_glfw.errorListHead = error;
_glfwPlatformUnlockMutex(&_glfw.errorLock);
} }
}
else
error = &_glfwMainThreadError;
error->code = code;
strcpy(error->description, description);
if (_glfwErrorCallback)
_glfwErrorCallback(code, description);
} }
@ -168,12 +205,34 @@ GLFWAPI int glfwInit(void)
return GLFW_FALSE; return GLFW_FALSE;
} }
_glfw.initialized = GLFW_TRUE; if (!_glfwPlatformCreateMutex(&_glfw.errorLock) ||
_glfw.timerOffset = _glfwPlatformGetTimerValue(); !_glfwPlatformCreateTls(&_glfw.errorSlot) ||
!_glfwPlatformCreateTls(&_glfw.contextSlot))
{
terminate();
return GLFW_FALSE;
}
_glfwPlatformSetTls(&_glfw.errorSlot, &_glfwMainThreadError);
_glfw.initialized = GLFW_TRUE;
_glfw.timer.offset = _glfwPlatformGetTimerValue();
// Not all window hints have zero as their default value
glfwDefaultWindowHints(); glfwDefaultWindowHints();
{
int i;
for (i = 0; _glfwDefaultMappings[i]; i++)
{
if (!glfwUpdateGamepadMappings(_glfwDefaultMappings[i]))
{
terminate();
return GLFW_FALSE;
}
}
}
return GLFW_TRUE; return GLFW_TRUE;
} }
@ -200,7 +259,28 @@ GLFWAPI void glfwInitHint(int hint, int value)
return; return;
} }
_glfwInputError(GLFW_INVALID_ENUM, "Invalid init hint %i", hint); _glfwInputError(GLFW_INVALID_ENUM,
"Invalid integer type init hint 0x%08X", hint);
}
GLFWAPI void glfwInitHintString(int hint, const char* value)
{
assert(value != NULL);
switch (hint)
{
case GLFW_X11_WM_CLASS_NAME:
strncpy(_glfwInitHints.x11.className, value,
sizeof(_glfwInitHints.x11.className) - 1);
return;
case GLFW_X11_WM_CLASS_CLASS:
strncpy(_glfwInitHints.x11.classClass, value,
sizeof(_glfwInitHints.x11.classClass) - 1);
return;
}
_glfwInputError(GLFW_INVALID_ENUM,
"Invalid string type init hint 0x%08X", hint);
} }
GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev) GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev)
@ -218,6 +298,30 @@ GLFWAPI const char* glfwGetVersionString(void)
return _glfwPlatformGetVersionString(); return _glfwPlatformGetVersionString();
} }
GLFWAPI int glfwGetError(const char** description)
{
_GLFWerror* error;
int code = GLFW_NO_ERROR;
if (description)
*description = NULL;
if (_glfw.initialized)
error = _glfwPlatformGetTls(&_glfw.errorSlot);
else
error = &_glfwMainThreadError;
if (error)
{
code = error->code;
error->code = GLFW_NO_ERROR;
if (description && code)
*description = error->description;
}
return code;
}
GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun) GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun)
{ {
_GLFW_SWAP_POINTERS(_glfwErrorCallback, cbfun); _GLFW_SWAP_POINTERS(_glfwErrorCallback, cbfun);

View File

@ -29,12 +29,196 @@
#include <assert.h> #include <assert.h>
#include <float.h> #include <float.h>
#include <math.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <ctype.h>
// Internal key state used for sticky keys // Internal key state used for sticky keys
#define _GLFW_STICK 3 #define _GLFW_STICK 3
// Internal constants for gamepad mapping source types
#define _GLFW_JOYSTICK_AXIS 1
#define _GLFW_JOYSTICK_BUTTON 2
#define _GLFW_JOYSTICK_HATBIT 3
// Finds a mapping based on joystick GUID
//
static _GLFWmapping* findMapping(const char* guid)
{
int i;
for (i = 0; i < _glfw.mappingCount; i++)
{
if (strcmp(_glfw.mappings[i].guid, guid) == 0)
return _glfw.mappings + i;
}
return NULL;
}
// Checks whether a gamepad mapping element is present in the hardware
//
static GLFWbool isValidElementForJoystick(const _GLFWmapelement* e,
const _GLFWjoystick* js)
{
if (e->type == _GLFW_JOYSTICK_HATBIT && (e->value >> 4) >= js->hatCount)
return GLFW_FALSE;
else if (e->type == _GLFW_JOYSTICK_BUTTON && e->value >= js->buttonCount)
return GLFW_FALSE;
else if (e->type == _GLFW_JOYSTICK_AXIS && e->value >= js->axisCount)
return GLFW_FALSE;
return GLFW_TRUE;
}
// Finds a mapping based on joystick GUID and verifies element indices
//
static _GLFWmapping* findValidMapping(const _GLFWjoystick* js)
{
_GLFWmapping* mapping = findMapping(js->guid);
if (mapping)
{
int i;
for (i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++)
{
if (!isValidElementForJoystick(mapping->buttons + i, js))
{
_glfwInputError(GLFW_INVALID_VALUE,
"Invalid button in gamepad mapping %s (%s)",
mapping->guid,
mapping->name);
return NULL;
}
}
for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++)
{
if (!isValidElementForJoystick(mapping->axes + i, js))
{
_glfwInputError(GLFW_INVALID_VALUE,
"Invalid axis in gamepad mapping %s (%s)",
mapping->guid,
mapping->name);
return NULL;
}
}
}
return mapping;
}
// Parses an SDL_GameControllerDB line and adds it to the mapping list
//
static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
{
const char* c = string;
size_t i, length;
struct
{
const char* name;
_GLFWmapelement* element;
} fields[] =
{
{ "platform", NULL },
{ "a", mapping->buttons + GLFW_GAMEPAD_BUTTON_A },
{ "b", mapping->buttons + GLFW_GAMEPAD_BUTTON_B },
{ "x", mapping->buttons + GLFW_GAMEPAD_BUTTON_X },
{ "y", mapping->buttons + GLFW_GAMEPAD_BUTTON_Y },
{ "back", mapping->buttons + GLFW_GAMEPAD_BUTTON_BACK },
{ "start", mapping->buttons + GLFW_GAMEPAD_BUTTON_START },
{ "guide", mapping->buttons + GLFW_GAMEPAD_BUTTON_GUIDE },
{ "leftshoulder", mapping->buttons + GLFW_GAMEPAD_BUTTON_LEFT_BUMPER },
{ "rightshoulder", mapping->buttons + GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER },
{ "leftstick", mapping->buttons + GLFW_GAMEPAD_BUTTON_LEFT_THUMB },
{ "rightstick", mapping->buttons + GLFW_GAMEPAD_BUTTON_RIGHT_THUMB },
{ "dpup", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_UP },
{ "dpright", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_RIGHT },
{ "dpdown", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_DOWN },
{ "dpleft", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_LEFT },
{ "lefttrigger", mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_TRIGGER },
{ "righttrigger", mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER },
{ "leftx", mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_X },
{ "lefty", mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_Y },
{ "rightx", mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_X },
{ "righty", mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_Y }
};
length = strcspn(c, ",");
if (length != 32 || c[length] != ',')
{
_glfwInputError(GLFW_INVALID_VALUE, NULL);
return GLFW_FALSE;
}
memcpy(mapping->guid, c, length);
c += length + 1;
length = strcspn(c, ",");
if (length >= sizeof(mapping->name) || c[length] != ',')
{
_glfwInputError(GLFW_INVALID_VALUE, NULL);
return GLFW_FALSE;
}
memcpy(mapping->name, c, length);
c += length + 1;
while (*c)
{
for (i = 0; i < sizeof(fields) / sizeof(fields[0]); i++)
{
length = strlen(fields[i].name);
if (strncmp(c, fields[i].name, length) != 0 || c[length] != ':')
continue;
c += length + 1;
if (fields[i].element)
{
if (*c == 'a')
fields[i].element->type = _GLFW_JOYSTICK_AXIS;
else if (*c == 'b')
fields[i].element->type = _GLFW_JOYSTICK_BUTTON;
else if (*c == 'h')
fields[i].element->type = _GLFW_JOYSTICK_HATBIT;
else
break;
if (fields[i].element->type == _GLFW_JOYSTICK_HATBIT)
{
const unsigned long hat = strtoul(c + 1, (char**) &c, 10);
const unsigned long bit = strtoul(c + 1, (char**) &c, 10);
fields[i].element->value = (uint8_t) ((hat << 4) | bit);
}
else
fields[i].element->value = (uint8_t) strtoul(c + 1, (char**) &c, 10);
}
else
{
length = strlen(_GLFW_PLATFORM_MAPPING_NAME);
if (strncmp(c, _GLFW_PLATFORM_MAPPING_NAME, length) != 0)
return GLFW_FALSE;
}
break;
}
c += strcspn(c, ",");
c += strspn(c, ",");
}
for (i = 0; i < 32; i++)
{
if (mapping->guid[i] >= 'A' && mapping->guid[i] <= 'F')
mapping->guid[i] += 'a' - 'A';
}
_glfwPlatformUpdateGamepadGUID(mapping->guid);
return GLFW_TRUE;
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
////// GLFW event API ////// ////// GLFW event API //////
@ -61,6 +245,9 @@ void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int m
action = GLFW_REPEAT; action = GLFW_REPEAT;
} }
if (!window->lockKeyMods)
mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
if (window->callbacks.key) if (window->callbacks.key)
window->callbacks.key((GLFWwindow*) window, key, scancode, action, mods); window->callbacks.key((GLFWwindow*) window, key, scancode, action, mods);
} }
@ -70,6 +257,9 @@ void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint, int mods, GLFWb
if (codepoint < 32 || (codepoint > 126 && codepoint < 160)) if (codepoint < 32 || (codepoint > 126 && codepoint < 160))
return; return;
if (!window->lockKeyMods)
mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
if (window->callbacks.charmods) if (window->callbacks.charmods)
window->callbacks.charmods((GLFWwindow*) window, codepoint, mods); window->callbacks.charmods((GLFWwindow*) window, codepoint, mods);
@ -91,6 +281,9 @@ void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods)
if (button < 0 || button > GLFW_MOUSE_BUTTON_LAST) if (button < 0 || button > GLFW_MOUSE_BUTTON_LAST)
return; return;
if (!window->lockKeyMods)
mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
if (action == GLFW_RELEASE && window->stickyMouseButtons) if (action == GLFW_RELEASE && window->stickyMouseButtons)
window->mouseButtons[button] = _GLFW_STICK; window->mouseButtons[button] = _GLFW_STICK;
else else
@ -124,25 +317,26 @@ void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths)
window->callbacks.drop((GLFWwindow*) window, count, paths); window->callbacks.drop((GLFWwindow*) window, count, paths);
} }
void _glfwInputJoystick(int jid, int event) void _glfwInputJoystick(_GLFWjoystick* js, int event)
{ {
const int jid = (int) (js - _glfw.joysticks);
if (_glfw.callbacks.joystick) if (_glfw.callbacks.joystick)
_glfw.callbacks.joystick(jid, event); _glfw.callbacks.joystick(jid, event);
} }
void _glfwInputJoystickAxis(int jid, int axis, float value) void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value)
{ {
_glfw.joysticks[jid].axes[axis] = value; js->axes[axis] = value;
} }
void _glfwInputJoystickButton(int jid, int button, char value) void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value)
{ {
_glfw.joysticks[jid].buttons[button] = value; js->buttons[button] = value;
} }
void _glfwInputJoystickHat(int jid, int hat, char value) void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value)
{ {
_GLFWjoystick* js = _glfw.joysticks + jid;
const int base = js->buttonCount + hat * 4; const int base = js->buttonCount + hat * 4;
js->buttons[base + 0] = (value & 0x01) ? GLFW_PRESS : GLFW_RELEASE; js->buttons[base + 0] = (value & 0x01) ? GLFW_PRESS : GLFW_RELEASE;
@ -158,14 +352,8 @@ void _glfwInputJoystickHat(int jid, int hat, char value)
////// GLFW internal API ////// ////// GLFW internal API //////
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
GLFWbool _glfwIsPrintable(int key)
{
return (key >= GLFW_KEY_APOSTROPHE && key <= GLFW_KEY_WORLD_2) ||
(key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_ADD) ||
key == GLFW_KEY_KP_EQUAL;
}
_GLFWjoystick* _glfwAllocJoystick(const char* name, _GLFWjoystick* _glfwAllocJoystick(const char* name,
const char* guid,
int axisCount, int axisCount,
int buttonCount, int buttonCount,
int hatCount) int hatCount)
@ -192,6 +380,9 @@ _GLFWjoystick* _glfwAllocJoystick(const char* name,
js->buttonCount = buttonCount; js->buttonCount = buttonCount;
js->hatCount = hatCount; js->hatCount = hatCount;
strcpy(js->guid, guid);
js->mapping = findValidMapping(js);
return js; return js;
} }
@ -224,10 +415,12 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode)
return window->stickyKeys; return window->stickyKeys;
case GLFW_STICKY_MOUSE_BUTTONS: case GLFW_STICKY_MOUSE_BUTTONS:
return window->stickyMouseButtons; return window->stickyMouseButtons;
default: case GLFW_LOCK_KEY_MODS:
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode %i", mode); return window->lockKeyMods;
return 0;
} }
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode);
return 0;
} }
GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value) GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
@ -237,16 +430,14 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
_GLFW_REQUIRE_INIT(); _GLFW_REQUIRE_INIT();
switch (mode) if (mode == GLFW_CURSOR)
{
case GLFW_CURSOR:
{ {
if (value != GLFW_CURSOR_NORMAL && if (value != GLFW_CURSOR_NORMAL &&
value != GLFW_CURSOR_HIDDEN && value != GLFW_CURSOR_HIDDEN &&
value != GLFW_CURSOR_DISABLED) value != GLFW_CURSOR_DISABLED)
{ {
_glfwInputError(GLFW_INVALID_ENUM, _glfwInputError(GLFW_INVALID_ENUM,
"Invalid cursor mode %i", "Invalid cursor mode 0x%08X",
value); value);
return; return;
} }
@ -262,12 +453,10 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
if (_glfwPlatformWindowFocused(window)) if (_glfwPlatformWindowFocused(window))
_glfwPlatformSetCursorMode(window, value); _glfwPlatformSetCursorMode(window, value);
return;
} }
else if (mode == GLFW_STICKY_KEYS)
case GLFW_STICKY_KEYS:
{ {
value = value ? GLFW_TRUE : GLFW_FALSE;
if (window->stickyKeys == value) if (window->stickyKeys == value)
return; return;
@ -283,12 +472,11 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
} }
} }
window->stickyKeys = value ? GLFW_TRUE : GLFW_FALSE; window->stickyKeys = value;
return;
} }
else if (mode == GLFW_STICKY_MOUSE_BUTTONS)
case GLFW_STICKY_MOUSE_BUTTONS:
{ {
value = value ? GLFW_TRUE : GLFW_FALSE;
if (window->stickyMouseButtons == value) if (window->stickyMouseButtons == value)
return; return;
@ -304,18 +492,31 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
} }
} }
window->stickyMouseButtons = value ? GLFW_TRUE : GLFW_FALSE; window->stickyMouseButtons = value;
return;
} }
} else if (mode == GLFW_LOCK_KEY_MODS)
window->lockKeyMods = value ? GLFW_TRUE : GLFW_FALSE;
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode %i", mode); else
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode);
} }
GLFWAPI const char* glfwGetKeyName(int key, int scancode) GLFWAPI const char* glfwGetKeyName(int key, int scancode)
{ {
_GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
return _glfwPlatformGetKeyName(key, scancode);
if (key != GLFW_KEY_UNKNOWN)
{
if (key != GLFW_KEY_KP_EQUAL &&
(key < GLFW_KEY_KP_0 || key > GLFW_KEY_KP_ADD) &&
(key < GLFW_KEY_APOSTROPHE || key > GLFW_KEY_WORLD_2))
{
return NULL;
}
scancode = _glfwPlatformGetKeyScancode(key);
}
return _glfwPlatformGetScancodeName(scancode);
} }
GLFWAPI int glfwGetKeyScancode(int key) GLFWAPI int glfwGetKeyScancode(int key)
@ -466,7 +667,7 @@ GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape)
shape != GLFW_HRESIZE_CURSOR && shape != GLFW_HRESIZE_CURSOR &&
shape != GLFW_VRESIZE_CURSOR) shape != GLFW_VRESIZE_CURSOR)
{ {
_glfwInputError(GLFW_INVALID_ENUM, "Invalid standard cursor %i", shape); _glfwInputError(GLFW_INVALID_ENUM, "Invalid standard cursor 0x%08X", shape);
return NULL; return NULL;
} }
@ -617,6 +818,8 @@ GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun)
GLFWAPI int glfwJoystickPresent(int jid) GLFWAPI int glfwJoystickPresent(int jid)
{ {
_GLFWjoystick* js;
assert(jid >= GLFW_JOYSTICK_1); assert(jid >= GLFW_JOYSTICK_1);
assert(jid <= GLFW_JOYSTICK_LAST); assert(jid <= GLFW_JOYSTICK_LAST);
@ -624,18 +827,21 @@ GLFWAPI int glfwJoystickPresent(int jid)
if (jid < 0 || jid > GLFW_JOYSTICK_LAST) if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
{ {
_glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", jid); _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
return GLFW_FALSE; return GLFW_FALSE;
} }
if (!_glfw.joysticks[jid].present) js = _glfw.joysticks + jid;
if (!js->present)
return GLFW_FALSE; return GLFW_FALSE;
return _glfwPlatformPollJoystick(jid, _GLFW_POLL_PRESENCE); return _glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE);
} }
GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count) GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count)
{ {
_GLFWjoystick* js;
assert(jid >= GLFW_JOYSTICK_1); assert(jid >= GLFW_JOYSTICK_1);
assert(jid <= GLFW_JOYSTICK_LAST); assert(jid <= GLFW_JOYSTICK_LAST);
assert(count != NULL); assert(count != NULL);
@ -646,22 +852,25 @@ GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count)
if (jid < 0 || jid > GLFW_JOYSTICK_LAST) if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
{ {
_glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", jid); _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
return NULL; return NULL;
} }
if (!_glfw.joysticks[jid].present) js = _glfw.joysticks + jid;
if (!js->present)
return NULL; return NULL;
if (!_glfwPlatformPollJoystick(jid, _GLFW_POLL_AXES)) if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_AXES))
return NULL; return NULL;
*count = _glfw.joysticks[jid].axisCount; *count = js->axisCount;
return _glfw.joysticks[jid].axes; return js->axes;
} }
GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count) GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count)
{ {
_GLFWjoystick* js;
assert(jid >= GLFW_JOYSTICK_1); assert(jid >= GLFW_JOYSTICK_1);
assert(jid <= GLFW_JOYSTICK_LAST); assert(jid <= GLFW_JOYSTICK_LAST);
assert(count != NULL); assert(count != NULL);
@ -672,29 +881,29 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count)
if (jid < 0 || jid > GLFW_JOYSTICK_LAST) if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
{ {
_glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", jid); _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
return NULL; return NULL;
} }
if (!_glfw.joysticks[jid].present) js = _glfw.joysticks + jid;
if (!js->present)
return NULL; return NULL;
if (!_glfwPlatformPollJoystick(jid, _GLFW_POLL_BUTTONS)) if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_BUTTONS))
return NULL; return NULL;
if (_glfw.hints.init.hatButtons) if (_glfw.hints.init.hatButtons)
{ *count = js->buttonCount + js->hatCount * 4;
*count = _glfw.joysticks[jid].buttonCount +
_glfw.joysticks[jid].hatCount * 4;
}
else else
*count = _glfw.joysticks[jid].buttonCount; *count = js->buttonCount;
return _glfw.joysticks[jid].buttons; return js->buttons;
} }
GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count) GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count)
{ {
_GLFWjoystick* js;
assert(jid >= GLFW_JOYSTICK_1); assert(jid >= GLFW_JOYSTICK_1);
assert(jid <= GLFW_JOYSTICK_LAST); assert(jid <= GLFW_JOYSTICK_LAST);
assert(count != NULL); assert(count != NULL);
@ -705,22 +914,25 @@ GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count)
if (jid < 0 || jid > GLFW_JOYSTICK_LAST) if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
{ {
_glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", jid); _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
return NULL; return NULL;
} }
if (!_glfw.joysticks[jid].present) js = _glfw.joysticks + jid;
if (!js->present)
return NULL; return NULL;
if (!_glfwPlatformPollJoystick(jid, _GLFW_POLL_BUTTONS)) if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_BUTTONS))
return NULL; return NULL;
*count = _glfw.joysticks[jid].hatCount; *count = js->hatCount;
return _glfw.joysticks[jid].hats; return js->hats;
} }
GLFWAPI const char* glfwGetJoystickName(int jid) GLFWAPI const char* glfwGetJoystickName(int jid)
{ {
_GLFWjoystick* js;
assert(jid >= GLFW_JOYSTICK_1); assert(jid >= GLFW_JOYSTICK_1);
assert(jid <= GLFW_JOYSTICK_LAST); assert(jid <= GLFW_JOYSTICK_LAST);
@ -728,17 +940,43 @@ GLFWAPI const char* glfwGetJoystickName(int jid)
if (jid < 0 || jid > GLFW_JOYSTICK_LAST) if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
{ {
_glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", jid); _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
return NULL; return NULL;
} }
if (!_glfw.joysticks[jid].present) js = _glfw.joysticks + jid;
if (!js->present)
return NULL; return NULL;
if (!_glfwPlatformPollJoystick(jid, _GLFW_POLL_PRESENCE)) if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE))
return NULL; return NULL;
return _glfw.joysticks[jid].name; return js->name;
}
GLFWAPI const char* glfwGetJoystickGUID(int jid)
{
_GLFWjoystick* js;
assert(jid >= GLFW_JOYSTICK_1);
assert(jid <= GLFW_JOYSTICK_LAST);
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
{
_glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
return NULL;
}
js = _glfw.joysticks + jid;
if (!js->present)
return NULL;
if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE))
return NULL;
return js->guid;
} }
GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun) GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun)
@ -748,29 +986,200 @@ GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun)
return cbfun; return cbfun;
} }
GLFWAPI int glfwUpdateGamepadMappings(const char* string)
{
int jid;
const char* c = string;
assert(string != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
while (*c)
{
if (isxdigit(*c))
{
char line[1024];
const size_t length = strcspn(c, "\r\n");
if (length < sizeof(line))
{
_GLFWmapping mapping = {{0}};
memcpy(line, c, length);
line[length] = '\0';
if (parseMapping(&mapping, line))
{
_GLFWmapping* previous = findMapping(mapping.guid);
if (previous)
*previous = mapping;
else
{
_glfw.mappingCount++;
_glfw.mappings =
realloc(_glfw.mappings,
sizeof(_GLFWmapping) * _glfw.mappingCount);
_glfw.mappings[_glfw.mappingCount - 1] = mapping;
}
}
}
c += length;
}
else
{
c += strcspn(c, "\r\n");
c += strspn(c, "\r\n");
}
}
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{
_GLFWjoystick* js = _glfw.joysticks + jid;
if (js->present)
js->mapping = findValidMapping(js);
}
return GLFW_TRUE;
}
GLFWAPI int glfwJoystickIsGamepad(int jid)
{
_GLFWjoystick* js;
assert(jid >= GLFW_JOYSTICK_1);
assert(jid <= GLFW_JOYSTICK_LAST);
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
{
_glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
return GLFW_FALSE;
}
js = _glfw.joysticks + jid;
if (!js->present)
return GLFW_FALSE;
if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE))
return GLFW_FALSE;
return js->mapping != NULL;
}
GLFWAPI const char* glfwGetGamepadName(int jid)
{
_GLFWjoystick* js;
assert(jid >= GLFW_JOYSTICK_1);
assert(jid <= GLFW_JOYSTICK_LAST);
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
{
_glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
return NULL;
}
js = _glfw.joysticks + jid;
if (!js->present)
return NULL;
if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE))
return NULL;
if (!js->mapping)
return NULL;
return js->mapping->name;
}
GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state)
{
int i;
_GLFWjoystick* js;
assert(jid >= GLFW_JOYSTICK_1);
assert(jid <= GLFW_JOYSTICK_LAST);
assert(state != NULL);
memset(state, 0, sizeof(GLFWgamepadstate));
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
{
_glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
return GLFW_FALSE;
}
js = _glfw.joysticks + jid;
if (!js->present)
return GLFW_FALSE;
if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_ALL))
return GLFW_FALSE;
if (!js->mapping)
return GLFW_FALSE;
for (i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++)
{
if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_AXIS)
{
if (fabs(js->axes[js->mapping->buttons[i].value]) > 0.5)
state->buttons[i] = GLFW_PRESS;
}
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_HATBIT)
{
const unsigned int hat = js->mapping->buttons[i].value >> 4;
const unsigned int bit = js->mapping->buttons[i].value & 0xf;
if (js->hats[hat] & bit)
state->buttons[i] = GLFW_PRESS;
}
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_BUTTON)
state->buttons[i] = js->buttons[js->mapping->buttons[i].value];
}
for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++)
{
if (js->mapping->axes[i].type == _GLFW_JOYSTICK_AXIS)
state->axes[i] = js->axes[js->mapping->axes[i].value];
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_HATBIT)
{
const unsigned int hat = js->mapping->axes[i].value >> 4;
const unsigned int bit = js->mapping->axes[i].value & 0xf;
if (js->hats[hat] & bit)
state->axes[i] = 1.f;
}
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_BUTTON)
state->axes[i] = (float) js->buttons[js->mapping->axes[i].value];
}
return GLFW_TRUE;
}
GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string) GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string)
{ {
_GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL);
assert(string != NULL); assert(string != NULL);
_GLFW_REQUIRE_INIT(); _GLFW_REQUIRE_INIT();
_glfwPlatformSetClipboardString(window, string); _glfwPlatformSetClipboardString(string);
} }
GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle) GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle)
{ {
_GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
return _glfwPlatformGetClipboardString(window); return _glfwPlatformGetClipboardString();
} }
GLFWAPI double glfwGetTime(void) GLFWAPI double glfwGetTime(void)
{ {
_GLFW_REQUIRE_INIT_OR_RETURN(0.0); _GLFW_REQUIRE_INIT_OR_RETURN(0.0);
return (double) (_glfwPlatformGetTimerValue() - _glfw.timerOffset) / return (double) (_glfwPlatformGetTimerValue() - _glfw.timer.offset) /
_glfwPlatformGetTimerFrequency(); _glfwPlatformGetTimerFrequency();
} }
@ -784,7 +1193,7 @@ GLFWAPI void glfwSetTime(double time)
return; return;
} }
_glfw.timerOffset = _glfwPlatformGetTimerValue() - _glfw.timer.offset = _glfwPlatformGetTimerValue() -
(uint64_t) (time * _glfwPlatformGetTimerFrequency()); (uint64_t) (time * _glfwPlatformGetTimerFrequency());
} }

View File

@ -25,9 +25,7 @@
// //
//======================================================================== //========================================================================
#ifndef _glfw3_internal_h_ #pragma once
#define _glfw3_internal_h_
#if defined(_GLFW_USE_CONFIG_H) #if defined(_GLFW_USE_CONFIG_H)
#include "glfw_config.h" #include "glfw_config.h"
@ -56,9 +54,13 @@
#define _GLFW_POLL_PRESENCE 0 #define _GLFW_POLL_PRESENCE 0
#define _GLFW_POLL_AXES 1 #define _GLFW_POLL_AXES 1
#define _GLFW_POLL_BUTTONS 2 #define _GLFW_POLL_BUTTONS 2
#define _GLFW_POLL_ALL (_GLFW_POLL_AXES | _GLFW_POLL_BUTTONS)
#define _GLFW_MESSAGE_SIZE 1024
typedef int GLFWbool; typedef int GLFWbool;
typedef struct _GLFWerror _GLFWerror;
typedef struct _GLFWinitconfig _GLFWinitconfig; typedef struct _GLFWinitconfig _GLFWinitconfig;
typedef struct _GLFWwndconfig _GLFWwndconfig; typedef struct _GLFWwndconfig _GLFWwndconfig;
typedef struct _GLFWctxconfig _GLFWctxconfig; typedef struct _GLFWctxconfig _GLFWctxconfig;
@ -68,7 +70,11 @@ typedef struct _GLFWwindow _GLFWwindow;
typedef struct _GLFWlibrary _GLFWlibrary; typedef struct _GLFWlibrary _GLFWlibrary;
typedef struct _GLFWmonitor _GLFWmonitor; typedef struct _GLFWmonitor _GLFWmonitor;
typedef struct _GLFWcursor _GLFWcursor; typedef struct _GLFWcursor _GLFWcursor;
typedef struct _GLFWmapelement _GLFWmapelement;
typedef struct _GLFWmapping _GLFWmapping;
typedef struct _GLFWjoystick _GLFWjoystick; typedef struct _GLFWjoystick _GLFWjoystick;
typedef struct _GLFWtls _GLFWtls;
typedef struct _GLFWmutex _GLFWmutex;
typedef void (* _GLFWmakecontextcurrentfun)(_GLFWwindow*); typedef void (* _GLFWmakecontextcurrentfun)(_GLFWwindow*);
typedef void (* _GLFWswapbuffersfun)(_GLFWwindow*); typedef void (* _GLFWswapbuffersfun)(_GLFWwindow*);
@ -252,14 +258,18 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void);
y = t; \ y = t; \
} }
// Maps a joystick pointer to an ID
#define _GLFW_JOYSTICK_ID(js) ((int) ((js) - _glfw.joysticks))
//======================================================================== //========================================================================
// Platform-independent structures // Platform-independent structures
//======================================================================== //========================================================================
struct _GLFWerror
{
_GLFWerror* next;
int code;
char description[_GLFW_MESSAGE_SIZE];
};
/*! @brief Initialization configuration. /*! @brief Initialization configuration.
* *
* Parameters relating to the initialization of the library. * Parameters relating to the initialization of the library.
@ -271,6 +281,10 @@ struct _GLFWinitconfig
GLFWbool menubar; GLFWbool menubar;
GLFWbool chdir; GLFWbool chdir;
} ns; } ns;
struct {
char className[256];
char classClass[256];
} x11;
}; };
/*! @brief Window configuration. /*! @brief Window configuration.
@ -347,6 +361,7 @@ struct _GLFWfbconfig
int samples; int samples;
GLFWbool sRGB; GLFWbool sRGB;
GLFWbool doublebuffer; GLFWbool doublebuffer;
GLFWbool transparent;
uintptr_t handle; uintptr_t handle;
}; };
@ -404,6 +419,7 @@ struct _GLFWwindow
GLFWbool stickyKeys; GLFWbool stickyKeys;
GLFWbool stickyMouseButtons; GLFWbool stickyMouseButtons;
GLFWbool lockKeyMods;
int cursorMode; int cursorMode;
char mouseButtons[GLFW_MOUSE_BUTTON_LAST + 1]; char mouseButtons[GLFW_MOUSE_BUTTON_LAST + 1];
char keys[GLFW_KEY_LAST + 1]; char keys[GLFW_KEY_LAST + 1];
@ -468,6 +484,24 @@ struct _GLFWcursor
_GLFW_PLATFORM_CURSOR_STATE; _GLFW_PLATFORM_CURSOR_STATE;
}; };
/*! @brief Gamepad mapping element structure
*/
struct _GLFWmapelement
{
uint8_t type;
uint8_t value;
};
/*! @brief Gamepad mapping structure
*/
struct _GLFWmapping
{
char name[128];
char guid[33];
_GLFWmapelement buttons[15];
_GLFWmapelement axes[6];
};
/*! @brief Joystick structure /*! @brief Joystick structure
*/ */
struct _GLFWjoystick struct _GLFWjoystick
@ -480,11 +514,29 @@ struct _GLFWjoystick
unsigned char* hats; unsigned char* hats;
int hatCount; int hatCount;
char* name; char* name;
char guid[33];
_GLFWmapping* mapping;
// This is defined in the joystick API's joystick.h // This is defined in the joystick API's joystick.h
_GLFW_PLATFORM_JOYSTICK_STATE; _GLFW_PLATFORM_JOYSTICK_STATE;
}; };
/*! @brief Thread local storage structure.
*/
struct _GLFWtls
{
// This is defined in the platform's thread.h
_GLFW_PLATFORM_TLS_STATE;
};
/*! @brief Mutex structure.
*/
struct _GLFWmutex
{
// This is defined in the platform's thread.h
_GLFW_PLATFORM_MUTEX_STATE;
};
/*! @brief Library global data. /*! @brief Library global data.
*/ */
struct _GLFWlibrary struct _GLFWlibrary
@ -499,16 +551,26 @@ struct _GLFWlibrary
int refreshRate; int refreshRate;
} hints; } hints;
_GLFWerror* errorListHead;
_GLFWcursor* cursorListHead; _GLFWcursor* cursorListHead;
_GLFWwindow* windowListHead; _GLFWwindow* windowListHead;
_GLFWmonitor** monitors; _GLFWmonitor** monitors;
int monitorCount; int monitorCount;
_GLFWjoystick joysticks[GLFW_JOYSTICK_LAST + 1]; _GLFWjoystick joysticks[GLFW_JOYSTICK_LAST + 1];
_GLFWmapping* mappings;
int mappingCount;
uint64_t timerOffset; _GLFWtls errorSlot;
_GLFWtls contextSlot;
_GLFWmutex errorLock;
struct {
uint64_t offset;
// This is defined in the platform's time.h
_GLFW_PLATFORM_LIBRARY_TIMER_STATE;
} timer;
struct { struct {
GLFWbool available; GLFWbool available;
@ -542,12 +604,8 @@ struct _GLFWlibrary
_GLFW_PLATFORM_LIBRARY_WINDOW_STATE; _GLFW_PLATFORM_LIBRARY_WINDOW_STATE;
// This is defined in the context API's context.h // This is defined in the context API's context.h
_GLFW_PLATFORM_LIBRARY_CONTEXT_STATE; _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE;
// This is defined in the platform's time.h
_GLFW_PLATFORM_LIBRARY_TIME_STATE;
// This is defined in the platform's joystick.h // This is defined in the platform's joystick.h
_GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE; _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 // This is defined in egl_context.h
_GLFW_EGL_LIBRARY_CONTEXT_STATE; _GLFW_EGL_LIBRARY_CONTEXT_STATE;
// This is defined in osmesa_context.h // This is defined in osmesa_context.h
@ -582,19 +640,21 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape);
void _glfwPlatformDestroyCursor(_GLFWcursor* cursor); void _glfwPlatformDestroyCursor(_GLFWcursor* cursor);
void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor); void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor);
const char* _glfwPlatformGetKeyName(int key, int scancode); const char* _glfwPlatformGetScancodeName(int scancode);
int _glfwPlatformGetKeyScancode(int key); int _glfwPlatformGetKeyScancode(int key);
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos); void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos);
void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, float* xscale, float* yscale);
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count); GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count);
void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode); void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode);
void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp); void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp);
void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp); void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp);
void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string); void _glfwPlatformSetClipboardString(const char* string);
const char* _glfwPlatformGetClipboardString(_GLFWwindow* window); const char* _glfwPlatformGetClipboardString(void);
int _glfwPlatformPollJoystick(int jid, int mode); int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode);
void _glfwPlatformUpdateGamepadGUID(char* guid);
uint64_t _glfwPlatformGetTimerValue(void); uint64_t _glfwPlatformGetTimerValue(void);
uint64_t _glfwPlatformGetTimerFrequency(void); uint64_t _glfwPlatformGetTimerFrequency(void);
@ -614,33 +674,45 @@ void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, int minwidth, int min
void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom); void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom);
void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height); void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height);
void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, int* left, int* top, int* right, int* bottom); void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, int* left, int* top, int* right, int* bottom);
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, float* xscale, float* yscale);
void _glfwPlatformIconifyWindow(_GLFWwindow* window); void _glfwPlatformIconifyWindow(_GLFWwindow* window);
void _glfwPlatformRestoreWindow(_GLFWwindow* window); void _glfwPlatformRestoreWindow(_GLFWwindow* window);
void _glfwPlatformMaximizeWindow(_GLFWwindow* window); void _glfwPlatformMaximizeWindow(_GLFWwindow* window);
void _glfwPlatformShowWindow(_GLFWwindow* window); void _glfwPlatformShowWindow(_GLFWwindow* window);
void _glfwPlatformHideWindow(_GLFWwindow* window); void _glfwPlatformHideWindow(_GLFWwindow* window);
void _glfwPlatformRequestWindowAttention(_GLFWwindow* window);
void _glfwPlatformFocusWindow(_GLFWwindow* window); void _glfwPlatformFocusWindow(_GLFWwindow* window);
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate);
int _glfwPlatformWindowFocused(_GLFWwindow* window); int _glfwPlatformWindowFocused(_GLFWwindow* window);
int _glfwPlatformWindowIconified(_GLFWwindow* window); int _glfwPlatformWindowIconified(_GLFWwindow* window);
int _glfwPlatformWindowVisible(_GLFWwindow* window); int _glfwPlatformWindowVisible(_GLFWwindow* window);
int _glfwPlatformWindowMaximized(_GLFWwindow* window); int _glfwPlatformWindowMaximized(_GLFWwindow* window);
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window);
float _glfwPlatformGetWindowOpacity(_GLFWwindow* window);
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled);
void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled);
void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled);
void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity);
void _glfwPlatformPollEvents(void); void _glfwPlatformPollEvents(void);
void _glfwPlatformWaitEvents(void); void _glfwPlatformWaitEvents(void);
void _glfwPlatformWaitEventsTimeout(double timeout); void _glfwPlatformWaitEventsTimeout(double timeout);
void _glfwPlatformPostEmptyEvent(void); void _glfwPlatformPostEmptyEvent(void);
void _glfwPlatformSetCurrentContext(_GLFWwindow* context);
_GLFWwindow* _glfwPlatformGetCurrentContext(void);
void _glfwPlatformGetRequiredInstanceExtensions(char** extensions); void _glfwPlatformGetRequiredInstanceExtensions(char** extensions);
int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily);
VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface);
GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls);
void _glfwPlatformDestroyTls(_GLFWtls* tls);
void* _glfwPlatformGetTls(_GLFWtls* tls);
void _glfwPlatformSetTls(_GLFWtls* tls, void* value);
GLFWbool _glfwPlatformCreateMutex(_GLFWmutex* mutex);
void _glfwPlatformDestroyMutex(_GLFWmutex* mutex);
void _glfwPlatformLockMutex(_GLFWmutex* mutex);
void _glfwPlatformUnlockMutex(_GLFWmutex* mutex);
/*! @} */ /*! @} */
@ -712,7 +784,7 @@ void _glfwInputWindowCloseRequest(_GLFWwindow* window);
* @param[in] monitor The new desired monitor, or `NULL`. * @param[in] monitor The new desired monitor, or `NULL`.
* @ingroup event * @ingroup event
*/ */
void _glfwInputWindowMonitorChange(_GLFWwindow* window, _GLFWmonitor* monitor); void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor);
/*! @brief Notifies shared code of a physical key event. /*! @brief Notifies shared code of a physical key event.
* @param[in] window The window that received the event. * @param[in] window The window that received the event.
@ -786,15 +858,15 @@ void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement);
void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window); void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window);
/*! @brief Notifies shared code of an error. /*! @brief Notifies shared code of an error.
* @param[in] error The error code most suitable for the error. * @param[in] code The error code most suitable for the error.
* @param[in] format The `printf` style format string of the error * @param[in] format The `printf` style format string of the error
* description. * description.
* @ingroup event * @ingroup event
*/ */
#if defined(__GNUC__) #if defined(__GNUC__)
void _glfwInputError(int error, const char* format, ...) __attribute__((format(printf, 2, 3))); void _glfwInputError(int code, const char* format, ...) __attribute__((format(printf, 2, 3)));
#else #else
void _glfwInputError(int error, const char* format, ...); void _glfwInputError(int code, const char* format, ...);
#endif #endif
/*! @brief Notifies shared code of files or directories dropped on a window. /*! @brief Notifies shared code of files or directories dropped on a window.
@ -806,32 +878,32 @@ void _glfwInputError(int error, const char* format, ...);
void _glfwInputDrop(_GLFWwindow* window, int count, const char** names); void _glfwInputDrop(_GLFWwindow* window, int count, const char** names);
/*! @brief Notifies shared code of a joystick connection or disconnection. /*! @brief Notifies shared code of a joystick connection or disconnection.
* @param[in] jid The joystick that was connected or disconnected. * @param[in] js The joystick that was connected or disconnected.
* @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`.
* @ingroup event * @ingroup event
*/ */
void _glfwInputJoystick(int jid, int event); void _glfwInputJoystick(_GLFWjoystick* js, int event);
/*! @brief Notifies shared code of the new value of a joystick axis. /*! @brief Notifies shared code of the new value of a joystick axis.
* @param[in] jid The joystick whose axis to update. * @param[in] js The joystick whose axis to update.
* @param[in] axis The index of the axis to update. * @param[in] axis The index of the axis to update.
* @param[in] value The new value of the axis. * @param[in] value The new value of the axis.
*/ */
void _glfwInputJoystickAxis(int jid, int axis, float value); void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value);
/*! @brief Notifies shared code of the new value of a joystick button. /*! @brief Notifies shared code of the new value of a joystick button.
* @param[in] jid The joystick whose button to update. * @param[in] js The joystick whose button to update.
* @param[in] button The index of the button to update. * @param[in] button The index of the button to update.
* @param[in] value The new value of the button. * @param[in] value The new value of the button.
*/ */
void _glfwInputJoystickButton(int jid, int button, char value); void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value);
/*! @brief Notifies shared code of the new value of a joystick hat. /*! @brief Notifies shared code of the new value of a joystick hat.
* @param[in] jid The joystick whose hat to update. * @param[in] js The joystick whose hat to update.
* @param[in] button The index of the hat to update. * @param[in] button The index of the hat to update.
* @param[in] value The new value of the hat. * @param[in] value The new value of the hat.
*/ */
void _glfwInputJoystickHat(int jid, int hat, char value); void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value);
//======================================================================== //========================================================================
@ -922,17 +994,17 @@ void _glfwFreeMonitor(_GLFWmonitor* monitor);
/*! @brief Returns an available joystick object with arrays and name allocated. /*! @brief Returns an available joystick object with arrays and name allocated.
* @ingroup utility * @ingroup utility
*/ */
_GLFWjoystick* _glfwAllocJoystick(const char* name, int axisCount, int buttonCount, int hatCount); _GLFWjoystick* _glfwAllocJoystick(const char* name,
const char* guid,
int axisCount,
int buttonCount,
int hatCount);
/*! @brief Frees arrays and name and flags the joystick object as unused. /*! @brief Frees arrays and name and flags the joystick object as unused.
* @ingroup utility * @ingroup utility
*/ */
void _glfwFreeJoystick(_GLFWjoystick* js); void _glfwFreeJoystick(_GLFWjoystick* js);
/*! @ingroup utility
*/
GLFWbool _glfwIsPrintable(int key);
/*! @ingroup utility /*! @ingroup utility
*/ */
GLFWbool _glfwInitVulkan(int mode); GLFWbool _glfwInitVulkan(int mode);
@ -945,4 +1017,3 @@ void _glfwTerminateVulkan(void);
*/ */
const char* _glfwGetVulkanResultString(VkResult result); const char* _glfwGetVulkanResultString(VkResult result);
#endif // _glfw3_internal_h_

View File

@ -27,8 +27,6 @@
#include "internal.h" #include "internal.h"
#include <linux/joystick.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/inotify.h> #include <sys/inotify.h>
@ -40,15 +38,99 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
// Apply an EV_KEY event to the specified joystick
//
static void handleKeyEvent(_GLFWjoystick* js, int code, int value)
{
_glfwInputJoystickButton(js,
js->linjs.keyMap[code - BTN_MISC],
value ? GLFW_PRESS : GLFW_RELEASE);
}
// Apply an EV_ABS event to the specified joystick
//
static void handleAbsEvent(_GLFWjoystick* js, int code, int value)
{
const int index = js->linjs.absMap[code];
if (code >= ABS_HAT0X && code <= ABS_HAT3Y)
{
static const char stateMap[3][3] =
{
{ GLFW_HAT_CENTERED, GLFW_HAT_UP, GLFW_HAT_DOWN },
{ GLFW_HAT_LEFT, GLFW_HAT_LEFT_UP, GLFW_HAT_LEFT_DOWN },
{ GLFW_HAT_RIGHT, GLFW_HAT_RIGHT_UP, GLFW_HAT_RIGHT_DOWN },
};
const int hat = (code - ABS_HAT0X) / 2;
const int axis = (code - ABS_HAT0X) % 2;
int* state = js->linjs.hats[hat];
// NOTE: Looking at several input drivers, it seems all hat events use
// -1 for left / up, 0 for centered and 1 for right / down
if (value == 0)
state[axis] = 0;
else if (value < 0)
state[axis] = 1;
else if (value > 0)
state[axis] = 2;
_glfwInputJoystickHat(js, index, stateMap[state[0]][state[1]]);
}
else
{
const struct input_absinfo* info = &js->linjs.absInfo[code];
float normalized = value;
const int range = info->maximum - info->minimum;
if (range)
{
// Normalize to 0.0 -> 1.0
normalized = (normalized - info->minimum) / range;
// Normalize to -1.0 -> 1.0
normalized = normalized * 2.0f - 1.0f;
}
_glfwInputJoystickAxis(js, index, normalized);
}
}
// Poll state of absolute axes
//
static void pollAbsState(_GLFWjoystick* js)
{
int code;
for (code = 0; code < ABS_CNT; code++)
{
if (js->linjs.absMap[code] < 0)
continue;
struct input_absinfo* info = &js->linjs.absInfo[code];
if (ioctl(js->linjs.fd, EVIOCGABS(code), info) < 0)
continue;
handleAbsEvent(js, code, info->value);
}
}
#define isBitSet(bit, arr) (arr[(bit) / 8] & (1 << ((bit) % 8)))
// Attempt to open the specified joystick device // Attempt to open the specified joystick device
// //
static GLFWbool openJoystickDevice(const char* path) static GLFWbool openJoystickDevice(const char* path)
{ {
char axisCount, buttonCount; int jid, code;
char name[256] = ""; char name[256] = "";
int jid, fd, version; char guid[33] = "";
_GLFWjoystick* js; char evBits[(EV_CNT + 7) / 8] = {0};
char keyBits[(KEY_CNT + 7) / 8] = {0};
char absBits[(ABS_CNT + 7) / 8] = {0};
int axisCount = 0, buttonCount = 0, hatCount = 0;
struct input_id id;
_GLFWjoystickLinux linjs = {0};
_GLFWjoystick* js = NULL;
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{ {
@ -58,47 +140,107 @@ static GLFWbool openJoystickDevice(const char* path)
return GLFW_FALSE; return GLFW_FALSE;
} }
fd = open(path, O_RDONLY | O_NONBLOCK); linjs.fd = open(path, O_RDONLY | O_NONBLOCK);
if (fd == -1) if (linjs.fd == -1)
return GLFW_FALSE; return GLFW_FALSE;
// Verify that the joystick driver version is at least 1.0 if (ioctl(linjs.fd, EVIOCGBIT(0, sizeof(evBits)), evBits) < 0 ||
ioctl(fd, JSIOCGVERSION, &version); ioctl(linjs.fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits) < 0 ||
if (version < 0x010000) ioctl(linjs.fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits) < 0 ||
ioctl(linjs.fd, EVIOCGID, &id) < 0)
{ {
// It's an old 0.x interface (we don't support it) _glfwInputError(GLFW_PLATFORM_ERROR,
close(fd); "Linux: Failed to query input device: %s",
strerror(errno));
close(linjs.fd);
return GLFW_FALSE; return GLFW_FALSE;
} }
if (ioctl(fd, JSIOCGNAME(sizeof(name)), name) < 0) // Ensure this device supports the events expected of a joystick
if (!isBitSet(EV_KEY, evBits) || !isBitSet(EV_ABS, evBits))
{
close(linjs.fd);
return GLFW_FALSE;
}
if (ioctl(linjs.fd, EVIOCGNAME(sizeof(name)), name) < 0)
strncpy(name, "Unknown", sizeof(name)); strncpy(name, "Unknown", sizeof(name));
ioctl(fd, JSIOCGAXES, &axisCount); // Generate a joystick GUID that matches the SDL 2.0.5+ one
ioctl(fd, JSIOCGBUTTONS, &buttonCount); if (id.vendor && id.product && id.version)
{
sprintf(guid, "%02x%02x0000%02x%02x0000%02x%02x0000%02x%02x0000",
id.bustype & 0xff, id.bustype >> 8,
id.vendor & 0xff, id.vendor >> 8,
id.product & 0xff, id.product >> 8,
id.version & 0xff, id.version >> 8);
}
else
{
sprintf(guid, "%02x%02x0000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00",
id.bustype & 0xff, id.bustype >> 8,
name[0], name[1], name[2], name[3],
name[4], name[5], name[6], name[7],
name[8], name[9], name[10]);
}
js = _glfwAllocJoystick(name, axisCount, buttonCount, 0); for (code = BTN_MISC; code < KEY_CNT; code++)
{
if (!isBitSet(code, keyBits))
continue;
linjs.keyMap[code - BTN_MISC] = buttonCount;
buttonCount++;
}
for (code = 0; code < ABS_CNT; code++)
{
linjs.absMap[code] = -1;
if (!isBitSet(code, absBits))
continue;
if (code >= ABS_HAT0X && code <= ABS_HAT3Y)
{
linjs.absMap[code] = hatCount;
hatCount++;
// Skip the Y axis
code++;
}
else
{
if (ioctl(linjs.fd, EVIOCGABS(code), &linjs.absInfo[code]) < 0)
continue;
linjs.absMap[code] = axisCount;
axisCount++;
}
}
js = _glfwAllocJoystick(name, guid, axisCount, buttonCount, hatCount);
if (!js) if (!js)
{ {
close(fd); close(linjs.fd);
return GLFW_FALSE; return GLFW_FALSE;
} }
js->linjs.path = strdup(path); strncpy(linjs.path, path, sizeof(linjs.path));
js->linjs.fd = fd; memcpy(&js->linjs, &linjs, sizeof(linjs));
_glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_CONNECTED); pollAbsState(js);
_glfwInputJoystick(js, GLFW_CONNECTED);
return GLFW_TRUE; return GLFW_TRUE;
} }
#undef isBitSet
// Frees all resources associated with the specified joystick // Frees all resources associated with the specified joystick
// //
static void closeJoystick(_GLFWjoystick* js) static void closeJoystick(_GLFWjoystick* js)
{ {
close(js->linjs.fd); close(js->linjs.fd);
free(js->linjs.path);
_glfwFreeJoystick(js); _glfwFreeJoystick(js);
_glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_DISCONNECTED); _glfwInputJoystick(js, GLFW_DISCONNECTED);
} }
// Lexically compare joysticks by name; used by qsort // Lexically compare joysticks by name; used by qsort
@ -124,30 +266,19 @@ GLFWbool _glfwInitJoysticksLinux(void)
const char* dirname = "/dev/input"; const char* dirname = "/dev/input";
_glfw.linjs.inotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); _glfw.linjs.inotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (_glfw.linjs.inotify == -1) if (_glfw.linjs.inotify > 0)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, // HACK: Register for IN_ATTRIB to get notified when udev is done
"Linux: Failed to initialize inotify: %s",
strerror(errno));
return GLFW_FALSE;
}
// HACK: Register for IN_ATTRIB as well to get notified when udev is done
// This works well in practice but the true way is libudev // This works well in practice but the true way is libudev
_glfw.linjs.watch = inotify_add_watch(_glfw.linjs.inotify, _glfw.linjs.watch = inotify_add_watch(_glfw.linjs.inotify,
dirname, dirname,
IN_CREATE | IN_ATTRIB | IN_DELETE); IN_CREATE | IN_ATTRIB | IN_DELETE);
if (_glfw.linjs.watch == -1)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Linux: Failed to watch for joystick connections in %s: %s",
dirname,
strerror(errno));
// Continue without device connection notifications
} }
if (regcomp(&_glfw.linjs.regex, "^js[0-9]\\+$", 0) != 0) // Continue without device connection notifications if inotify fails
if (regcomp(&_glfw.linjs.regex, "^event[0-9]\\+$", 0) != 0)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, "Linux: Failed to compile regex"); _glfwInputError(GLFW_PLATFORM_ERROR, "Linux: Failed to compile regex");
return GLFW_FALSE; return GLFW_FALSE;
@ -160,27 +291,23 @@ GLFWbool _glfwInitJoysticksLinux(void)
while ((entry = readdir(dir))) while ((entry = readdir(dir)))
{ {
char path[20];
regmatch_t match; regmatch_t match;
if (regexec(&_glfw.linjs.regex, entry->d_name, 1, &match, 0) != 0) if (regexec(&_glfw.linjs.regex, entry->d_name, 1, &match, 0) != 0)
continue; continue;
char path[PATH_MAX];
snprintf(path, sizeof(path), "%s/%s", dirname, entry->d_name); snprintf(path, sizeof(path), "%s/%s", dirname, entry->d_name);
if (openJoystickDevice(path)) if (openJoystickDevice(path))
count++; count++;
} }
closedir(dir); closedir(dir);
} }
else
{ // Continue with no joysticks if enumeration fails
_glfwInputError(GLFW_PLATFORM_ERROR,
"Linux: Failed to open joystick device directory %s: %s",
dirname,
strerror(errno));
// Continue with no joysticks detected
}
qsort(_glfw.joysticks, count, sizeof(_GLFWjoystick), compareJoysticks); qsort(_glfw.joysticks, count, sizeof(_GLFWjoystick), compareJoysticks);
return GLFW_TRUE; return GLFW_TRUE;
@ -215,6 +342,9 @@ void _glfwDetectJoystickConnectionLinux(void)
ssize_t offset = 0; ssize_t offset = 0;
char buffer[16384]; char buffer[16384];
if (_glfw.linjs.inotify <= 0)
return;
const ssize_t size = read(_glfw.linjs.inotify, buffer, sizeof(buffer)); const ssize_t size = read(_glfw.linjs.inotify, buffer, sizeof(buffer));
while (size > offset) while (size > offset)
@ -222,9 +352,12 @@ void _glfwDetectJoystickConnectionLinux(void)
regmatch_t match; regmatch_t match;
const struct inotify_event* e = (struct inotify_event*) (buffer + offset); const struct inotify_event* e = (struct inotify_event*) (buffer + offset);
if (regexec(&_glfw.linjs.regex, e->name, 1, &match, 0) == 0) offset += sizeof(struct inotify_event) + e->len;
{
char path[20]; if (regexec(&_glfw.linjs.regex, e->name, 1, &match, 0) != 0)
continue;
char path[PATH_MAX];
snprintf(path, sizeof(path), "/dev/input/%s", e->name); snprintf(path, sizeof(path), "/dev/input/%s", e->name);
if (e->mask & (IN_CREATE | IN_ATTRIB)) if (e->mask & (IN_CREATE | IN_ATTRIB))
@ -243,9 +376,6 @@ void _glfwDetectJoystickConnectionLinux(void)
} }
} }
} }
offset += sizeof(struct inotify_event) + e->len;
}
} }
@ -253,14 +383,12 @@ void _glfwDetectJoystickConnectionLinux(void)
////// GLFW platform API ////// ////// GLFW platform API //////
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
int _glfwPlatformPollJoystick(int jid, int mode) int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode)
{ {
_GLFWjoystick* js = _glfw.joysticks + jid;
// Read all queued events (non-blocking) // Read all queued events (non-blocking)
for (;;) for (;;)
{ {
struct js_event e; struct input_event e;
errno = 0; errno = 0;
if (read(js->linjs.fd, &e, sizeof(e)) < 0) if (read(js->linjs.fd, &e, sizeof(e)) < 0)
@ -272,15 +400,30 @@ int _glfwPlatformPollJoystick(int jid, int mode)
break; break;
} }
// Clear the initial-state bit if (e.type == EV_SYN)
e.type &= ~JS_EVENT_INIT; {
if (e.code == SYN_DROPPED)
_glfw.linjs.dropped = GLFW_TRUE;
else if (e.code == SYN_REPORT)
{
_glfw.linjs.dropped = GLFW_FALSE;
pollAbsState(js);
}
}
if (e.type == JS_EVENT_AXIS) if (_glfw.linjs.dropped)
_glfwInputJoystickAxis(jid, e.number, e.value / 32767.0f); continue;
else if (e.type == JS_EVENT_BUTTON)
_glfwInputJoystickButton(jid, e.number, e.value ? 1 : 0); if (e.type == EV_KEY)
handleKeyEvent(js, e.code, e.value);
else if (e.type == EV_ABS)
handleAbsEvent(js, e.code, e.value);
} }
return js->present; return js->present;
} }
void _glfwPlatformUpdateGamepadGUID(char* guid)
{
}

View File

@ -24,21 +24,25 @@
// //
//======================================================================== //========================================================================
#ifndef _glfw3_linux_joystick_h_ #include <linux/input.h>
#define _glfw3_linux_joystick_h_ #include <linux/limits.h>
#include <regex.h> #include <regex.h>
#define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickLinux linjs #define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickLinux linjs
#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE _GLFWlibraryLinux linjs #define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE _GLFWlibraryLinux linjs
#define _GLFW_PLATFORM_MAPPING_NAME "Linux"
// Linux-specific joystick data // Linux-specific joystick data
// //
typedef struct _GLFWjoystickLinux typedef struct _GLFWjoystickLinux
{ {
int fd; int fd;
char* path; char path[PATH_MAX];
int keyMap[KEY_CNT - BTN_MISC];
int absMap[ABS_CNT];
struct input_absinfo absInfo[ABS_CNT];
int hats[4][2];
} _GLFWjoystickLinux; } _GLFWjoystickLinux;
// Linux-specific joystick API data // Linux-specific joystick API data
@ -48,6 +52,7 @@ typedef struct _GLFWlibraryLinux
int inotify; int inotify;
int watch; int watch;
regex_t regex; regex_t regex;
GLFWbool dropped;
} _GLFWlibraryLinux; } _GLFWlibraryLinux;
@ -55,4 +60,3 @@ GLFWbool _glfwInitJoysticksLinux(void);
void _glfwTerminateJoysticksLinux(void); void _glfwTerminateJoysticksLinux(void);
void _glfwDetectJoystickConnectionLinux(void); void _glfwDetectJoystickConnectionLinux(void);
#endif // _glfw3_linux_joystick_h_

310
src/mappings.h Normal file
View File

@ -0,0 +1,310 @@
//========================================================================
// GLFW 3.3 - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2006-2016 Camilla Löwy <elmindreda@glfw.org>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
//
//========================================================================
// As mappings.h.in, this file is used by CMake to produce the mappings.h
// header file. If you are adding a GLFW specific gamepad mapping, this is
// where to put it.
//========================================================================
// As mappings.h, this provides all pre-defined gamepad mappings, including
// all available in SDL_GameControllerDB. Do not edit this file. Any gamepad
// mappings not specific to GLFW should be submitted to SDL_GameControllerDB.
// This file can be re-generated from mappings.h.in and the upstream
// gamecontrollerdb.txt with the GenerateMappings.cmake script.
//========================================================================
// All gamepad mappings not labeled GLFW are copied from the
// SDL_GameControllerDB project under the following license:
//
// Simple DirectMedia Layer
// Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the
// use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
const char* _glfwDefaultMappings[] =
{
"02200090000000000000504944564944,8Bitdo NES30 PRO USB,platform:Windows,a:b0,b:b1,x:b3,y:b4,leftshoulder:b6,rightshoulder:b7,lefttrigger:b8,righttrigger:b9,back:b10,start:b11,leftstick:b13,rightstick:b14,leftx:a0,lefty:a1,rightx:a3,righty:a4,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"20380900000000000000504944564944,8Bitdo NES30 PRO Wireless,platform:Windows,a:b0,b:b1,x:b3,y:b4,leftshoulder:b6,rightshoulder:b7,lefttrigger:b8,righttrigger:b9,back:b10,start:b11,leftstick:b13,rightstick:b14,leftx:a0,lefty:a1,rightx:a3,righty:a4,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"10280900000000000000504944564944,8Bitdo SFC30 GamePad,a:b1,b:b0,y:b3,x:b4,start:b11,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,platform:Windows,",
"8f0e1200000000000000504944564944,Acme,platform:Windows,x:b2,a:b0,b:b1,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
"341a3608000000000000504944564944,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"c0111352000000000000504944564944,Battalife Joystick,platform:Windows,x:b4,a:b6,b:b7,y:b5,back:b2,start:b3,leftshoulder:b0,rightshoulder:b1,leftx:a0,lefty:a1,",
"d81d0b00000000000000504944564944,BUFFALO BSGP1601 Series ,platform:Windows,x:b4,a:b5,b:b3,y:b2,back:b12,start:b13,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b6,rightshoulder:b9,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"5e048e02000000000000504944564944,Controller (XBOX 360 For Windows),platform:Windows,x:b2,a:b0,b:b1,y:b3,back:b6,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,righttrigger:a2,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"4f0423b3000000000000504944564944,Dual Trigger 3-in-1,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"341a0108000000000000504944564944,EXEQ RF USB Gamepad 8206,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,leftstick:b8,rightstick:b7,back:b8,start:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Windows,",
"0d0f8500000000000000504944564944,Fighting Commander 2016 PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"0d0f5f00000000000000504944564944,Fighting Commander 4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"0d0f5e00000000000000504944564944,Fighting Commander 4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b5,rightshoulder:b4,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a4,righttrigger:a3,platform:Windows,",
"0d0f8400000000000000504944564944,Fighting Commander 5,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"0d0f8700000000000000504944564944,Fighting Stick mini 4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"0d0f8800000000000000504944564944,Fighting Stick mini 4,a:b1,b:b2,x:b0,y:b3,back:b9,guide:b12,start:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"0d0f2700000000000000504944564944,FIGHTING STICK V3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"79000600000000000000504944564944,G-Shark GS-GP702,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a4,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"28040140000000000000504944564944,GamePad Pro USB,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,lefttrigger:b6,righttrigger:b7,",
"ffff0000000000000000504944564944,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
"6d0416c2000000000000504944564944,Generic DirectInput Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"45130010000000000000504944564944,Generic USB Joystick,a:b0,b:b1,x:b2,y:b3,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"0d0f4900000000000000504944564944,Hatsune Miku Sho Controller,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"d8140862000000000000504944564944,HitBox Edition Cthulhu+,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b5,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b4,righttrigger:b6,platform:Windows,",
"0d0f4000000000000000504944564944,Hori Fighting Stick Mini 3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b5,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b4,righttrigger:b6,platform:Windows,",
"0d0f6e00000000000000504944564944,HORIPAD 4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"0d0f4d00000000000000504944564944,HORIPAD3 A,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"25090017000000000000504944564944,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,x:b3,y:b0,back:b9,start:b8,leftshoulder:b5,rightshoulder:b7,leftx:a0,lefty:a1,lefttrigger:b4,righttrigger:b6,platform:Windows,",
"d81d0f00000000000000504944564944,iBUFFALO BSGP1204 Series,platform:Windows,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"d81d1000000000000000504944564944,iBUFFALO BSGP1204P Series,platform:Windows,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"83056020000000000000504944564944,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,y:b2,x:b3,start:b7,back:b6,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Windows,",
"6f0e2401000000000000504944564944,INJUSTICE FightStick for PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,lefttrigger:b6,righttrigger:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,platform:Windows",
"49190204000000000000504944564944,Ipega PG-9023,a:b0,b:b1,x:b3,y:b4,back:b10,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b8,righttrigger:b9,platform:Windows,",
"6d0419c2000000000000504944564944,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
"6d0418c2000000000000504944564944,Logitech RumblePad 2 USB,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"38075032000000000000504944564944,Mad Catz FightPad PRO PS3,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"38075082000000000000504944564944,Mad Catz FightPad PRO PS4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b13,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5,",
"38078433000000000000504944564944,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"38078483000000000000504944564944,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:b6,platform:Windows,",
"38078134000000000000504944564944,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b7,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b4,platform:Windows,",
"38078184000000000000504944564944,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b5,rightshoulder:b4,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a4,righttrigger:b7,platform:Windows,",
"38078034000000000000504944564944,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"38078084000000000000504944564944,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows,",
"38078532000000000000504944564944,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"38073888000000000000504944564944,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"38071888000000000000504944564944,MadCatz SFIV FightStick PS3,a:b0,b:b1,x:b2,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b5,rightshoulder:b4,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b7,righttrigger:b6,platform:Windows,",
"03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"25090128000000000000504944564944,Mayflash Arcade Stick,a:b1,b:b2,x:b5,y:b6,back:b8,start:b9,leftshoulder:b0,rightshoulder:b3,leftx:a0,lefty:a1,rightx:h0.4,righty:h0.0,lefttrigger:b4,righttrigger:b7,platform:Windows,",
"79004318000000000000504944564944,Mayflash GameCube Controller Adapter,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b0,start:b9,guide:b0,leftshoulder:b4,rightshoulder:b7,leftstick:b0,rightstick:b0,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,",
"8f0e1030000000000000504944564944,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,x:b3,y:b4,start:b9,leftshoulder:b6,rightshoulder:b2,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b5,righttrigger:b7,platform:Windows,",
"2509e803000000000000504944564944,Mayflash Wii Classic Controller,a:b1,b:b0,x:b3,y:b2,back:b8,guide:b10,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:b11,dpdown:b13,dpleft:b12,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"79000018000000000000504944564944,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"8f0e0d31000000000000504944564944,Multilaser JS071 USB,platform:Windows,a:b1,b:b2,y:b3,x:b0,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
"100801e5000000000000504944564944,NEXT Classic USB Game Controller,a:b0,b:b1,back:b8,start:b9,rightx:a2,righty:a3,leftx:a0,lefty:a1,platform:Windows,",
"bd1215d0000000000000504944564944,Nintendo Retrolink USB Super SNES Classic Controller,y:b0,b:b1,a:b2,x:b3,leftshoulder:b4,rightshoulder:b5,start:b9,back:b8,leftx:a0,lefty:a1,platform:Windows,",
"4b12014d000000000000504944564944,NYKO AIRFLO,a:b0,b:b1,x:b2,y:b3,back:b8,guide:b10,start:b9,leftstick:a0,rightstick:a2,leftshoulder:a3,rightshoulder:b5,dpup:h0.1,dpdown:h0.0,dpleft:h0.8,dpright:h0.2,leftx:h0.6,lefty:h0.12,rightx:h0.9,righty:h0.4,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"36280100000000000000504944564944,OUYA Controller,platform:Windows,a:b0,b:b3,y:b2,x:b1,start:b14,guide:b15,leftstick:b6,rightstick:b7,leftshoulder:b4,rightshoulder:b5,dpup:b8,dpleft:b10,dpdown:b9,dpright:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b12,righttrigger:b13,",
"120cf60e000000000000504944564944,P4 Wired Gamepad,a:b1,b:b2,x:b0,y:b3,back:b12,guide:b8,start:b9,leftshoulder:b5,rightshoulder:b4,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:h0.0,lefttrigger:b7,righttrigger:b6,platform:Windows,",
"8f0e0300000000000000504944564944,Piranha xtreme,platform:Windows,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
"d6206dca000000000000504944564944,PowerA Pro Ex,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.0,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"10080100000000000000504944564944,PS1 USB,platform:Windows,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftshoulder:b6,rightshoulder:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,",
"10080300000000000000504944564944,PS2 USB,platform:Windows,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a4,righty:a2,lefttrigger:b4,righttrigger:b5,",
"88880803000000000000504944564944,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,",
"4c056802000000000000504944564944,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows,",
"25090500000000000000504944564944,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows,",
"10008200000000000000504944564944,PS360+ v1.66,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:h0.4,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"4c05c405000000000000504944564944,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
"300f0011000000000000504944564944,QanBa Arcade JoyStick 1008,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,start:b10,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftx:a0,lefty:a1,",
"300f1611000000000000504944564944,QanBa Arcade JoyStick 4018,a:b1,b:b2,x:b0,y:b3,back:b10,guide:b9,start:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"222c0020000000000000504944564944,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:a3,righttrigger:a4,platform:Windows,",
"300f1210000000000000504944564944,QanBa Joystick Plus,a:b0,b:b1,x:b2,y:b3,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,platform:Windows,",
"341a0104000000000000504944564944,QanBa Joystick Q4RAF,a:b5,b:b6,x:b1,y:b2,back:b8,guide:b10,start:b9,leftshoulder:b0,rightshoulder:b3,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,lefttrigger:b4,righttrigger:b7,platform:Windows,",
"222c0223000000000000504944564944,Qanba Obsidian Arcade Joystick PS3 Mode,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"222c0023000000000000504944564944,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,x:b0,y:b3,back:b13,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows,",
"0d0f1100000000000000504944564944,REAL ARCADE PRO.3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"0d0f8b00000000000000504944564944,Real Arcade Pro.4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"0d0f8a00000000000000504944564944,Real Arcade Pro.4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5,",
"0d0f6b00000000000000504944564944,Real Arcade Pro.4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"0d0f6a00000000000000504944564944,Real Arcade Pro.4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5,",
"0d0f7000000000000000504944564944,REAL ARCADE PRO.4 VLX,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"0d0f2200000000000000504944564944,REAL ARCADE Pro.V3,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"00f00300000000000000504944564944,RetroUSB.com RetroPad,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Windows,",
"00f0f100000000000000504944564944,RetroUSB.com Super RetroPort,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Windows,",
"6f0e1e01000000000000504944564944,Rock Candy Gamepad for PS3,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,guide:b12,leftshoulder:b4,rightshoulder:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,",
"300f1201000000000000504944564944,Saitek Dual Analog Pad,a:b2,b:b3,x:b0,y:b1,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b5,righttrigger:b7,platform:Windows,",
"a3060cff000000000000504944564944,Saitek P2500,a:b2,b:b3,y:b1,x:b0,start:b4,guide:b10,back:b5,leftstick:b8,rightstick:b9,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Windows,",
"300f1001000000000000504944564944,Saitek P480 Rumble Pad,a:b2,b:b3,x:b0,y:b1,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b5,righttrigger:b7,platform:Windows,",
"9b280500000000000000504944564944,Saturn_Adapter_2.0,a:b1,b:b2,x:b0,y:b3,start:b9,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,lefttrigger:b4,righttrigger:b5,platform:Windows,",
"79001100000000000000504944564944,Sega Saturn Gamepad,a:b1,b:b2,x:b4,y:b5,start:b8,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a4,lefttrigger:b3,righttrigger:b0,platform:Windows,",
"4c05cc09000000000000504944564944,Sony DualShock 4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows,",
"4c05a00b000000000000504944564944,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows,",
"ff113133000000000000504944564944,SVEN X-PAD,platform:Windows,a:b2,b:b3,y:b1,x:b0,start:b5,back:b4,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a4,lefttrigger:b8,righttrigger:b9,",
"4f0415b3000000000000504944564944,Thrustmaster Dual Analog 3.2,platform:Windows,x:b1,a:b0,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"4f0400b3000000000000504944564944,Thrustmaster Firestorm Dual Power,a:b0,b:b2,y:b3,x:b1,start:b10,guide:b8,back:b9,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Windows,",
"66660488000000000000504944564944,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,x:b3,y:b0,back:b9,start:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:b12,dpdown:b14,dpleft:b15,dpright:b13,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b4,righttrigger:b5,platform:Windows,",
"38076652000000000000504944564944,UnKnown,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"63252305000000000000504944564944,USB Vibration Joystick (BM),platform:Windows,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"79001b18000000000000504944564944,Venom Arcade Joystick,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,",
"10280000000000000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,x:b4,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Mac OS X,",
"79000000000000000600000000000000,G-Shark GP-702,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b6,righttrigger:b7,platform:Mac OS X,",
"AD1B00000000000001F9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,",
"0d0f0000000000004d00000000000000,HORI Gem Pad 3,platform:Mac OS X,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
"0d0f0000000000006600000000000000,HORIPAD FPS PLUS 4,platform:Mac OS X,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:a4,",
"83050000000000006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,x:b3,y:b2,back:b6,start:b7,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Mac OS X,",
"6d0400000000000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
"6d0400000000000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
"6d040000000000001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
"6d0400000000000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
"2509000000000000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,x:b3,y:b2,back:b8,guide:b10,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:b11,dpdown:b13,dpleft:b12,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Mac OS X,",
"79000000000000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,x:b0,y:b12,back:b32,start:b36,leftstick:b40,rightstick:b44,leftshoulder:b16,rightshoulder:b20,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a4,rightx:a8,righty:a12,lefttrigger:b24,righttrigger:b28,platform:Mac OS X,",
"d814000000000000cecf000000000000,MC Cthulhu,platform:Mac OS X,leftx:,lefty:,rightx:,righty:,lefttrigger:b6,a:b1,b:b2,y:b3,x:b0,start:b9,back:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,righttrigger:b7,",
"8f0e0000000000000300000000000000,Piranha xtreme,platform:Mac OS X,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
"4c050000000000006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,",
"4c05000000000000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
"891600000000000000fd000000000000,Razer Onza Tournament,a:b0,b:b1,y:b3,x:b2,start:b8,guide:b10,back:b9,leftstick:b6,rightstick:b7,leftshoulder:b4,rightshoulder:b5,dpup:b11,dpleft:b13,dpdown:b12,dpright:b14,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Mac OS X,",
"79000000000000001100000000000000,Retrolink Classic Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a3,lefty:a4,platform:Mac OS X,",
"81170000000000007e05000000000000,Sega Saturn,x:b0,a:b2,b:b4,y:b6,start:b13,dpleft:b15,dpdown:b16,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,lefttrigger:b10,rightshoulder:b9,righttrigger:a4,righttrigger:b11,leftx:a0,lefty:a2,platform:Mac OS X,",
"b4040000000000000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,x:b3,y:b4,back:b5,guide:b2,start:b8,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Mac OS X,",
"351200000000000021ab000000000000,SFC30 Joystick,a:b1,b:b0,x:b4,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Mac OS X,",
"4c05000000000000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Mac OS X,",
"4c05000000000000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Mac OS X,",
"4f0400000000000015b3000000000000,Thrustmaster Dual Analog 3.2,platform:Mac OS X,x:b1,a:b0,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"4f0400000000000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,y:b3,x:b1,start:b10,guide:b8,back:b9,leftstick:b11,rightstick:,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Mac OS X,",
"bd1200000000000015d0000000000000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Mac OS X,",
"10080000000000000100000000000000,Twin USB Joystick,a:b4,b:b2,x:b6,y:b0,back:b16,start:b18,leftstick:b20,rightstick:b22,leftshoulder:b12,rightshoulder:b14,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a2,rightx:a6,righty:a4,lefttrigger:b8,righttrigger:b10,platform:Mac OS X,",
"050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,y:b9,x:b10,start:b6,guide:b8,back:b7,dpup:b2,dpleft:b0,dpdown:b3,dpright:b1,leftx:a0,lefty:a1,lefttrigger:b12,righttrigger:,leftshoulder:b11,platform:Mac OS X,",
"050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,x:b18,y:b17,back:b7,guide:b8,start:b6,leftstick:b23,rightstick:b24,leftshoulder:b19,rightshoulder:b20,dpup:b11,dpdown:b12,dpleft:b13,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b21,righttrigger:b22,platform:Mac OS X,",
"5e040000000000008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
"5e04000000000000dd02000000000000,Xbox One Wired Controller,platform:Mac OS X,x:b2,a:b0,b:b1,y:b3,back:b9,guide:b10,start:b8,dpleft:b13,dpdown:b12,dpright:b14,dpup:b11,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b6,rightstick:b7,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"5e04000000000000ea02000000000000,Xbox Wireless Controller,platform:Mac OS X,x:b2,a:b0,b:b1,y:b3,back:b9,guide:b10,start:b8,dpleft:b13,dpdown:b12,dpright:b14,dpup:b11,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b6,rightstick:b7,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"5e04000000000000e002000000000000,Xbox Wireless Controller,platform:Mac OS X,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b10,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"05000000102800000900000000010000,8Bitdo SFC30 GamePad,platform:Linux,x:b4,a:b1,b:b0,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,",
"05000000a00500003232000001000000,8Bitdo Zero GamePad,platform:Linux,a:b0,b:b1,x:b3,y:b4,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,",
"030000006f0e00003901000020060000,Afterglow Wired Controller for Xbox One,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,platform:Linux,",
"03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick ,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"030000006f0e00003001000001010000,EA Sports PS3 Controller,platform:Linux,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
"03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,y:b3,x:b0,start:b9,guide:,back:,leftstick:,rightstick:,leftshoulder:,dpleft:b15,dpdown:b14,dpright:b13,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,rightshoulder:b7,dpup:b12,platform:Linux,",
"03000000260900008888000000010000,GameCube {WiseGroup USB box},a:b0,b:b2,y:b3,x:b1,start:b7,leftshoulder:,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,rightstick:,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Linux,",
"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
"030000006f0e00001f01000000010000,Generic X-Box pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"030000006f0e00001304000000010000,Generic X-Box pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:a0,rightstick:a3,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"03000000280400000140000000010000,Gravis GamePad Pro USB ,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftx:a0,lefty:a1,",
"030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick ,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
"030000008f0e00000300000010010000,GreenAsia Inc. USB Joystick ,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
"03000000ff1100004133000010010000,GreenAsia Inc.USB Joystick,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
"06000000adde0000efbe000002010000,Hidromancer Game Controller,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,platform:Linux,a:b1,b:b2,y:b3,x:b0,start:b12,guide:b9,back:b8,leftshoulder:b4,rightshoulder:b5,lefttrigger:b6,righttrigger:b7,leftx:a0,lefty:a1,",
"03000000c9110000f055000011010000,HJC Game GAMEPAD,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b11,rightshoulder:b5,rightx:a2,start:b9,righty:a3,dpleft:h0.8,lefttrigger:b6,x:b2,dpup:h0.1,back:b8,leftstick:b10,leftshoulder:b4,y:b3,a:b0,dpright:h0.2,righttrigger:b7,b:b1,platform:Linux,",
"030000000d0f00000d00000000010000,hori,platform:Linux,a:b0,b:b6,y:b2,x:b1,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,start:b9,guide:b10,back:b8,leftshoulder:b3,rightshoulder:b7,leftx:b4,lefty:b5,",
"030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7",
"030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,",
"03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,",
"03000000830500006020000010010000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,x:b3,y:b2,back:b6,start:b7,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Linux,",
"03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),platform:Linux,a:b3,b:b4,y:b1,x:b0,start:b7,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,",
"030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,platform:Linux,x:b0,a:b2,b:b3,y:b1,back:b10,guide:b12,start:b11,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,platform:Linux,x:b0,a:b2,b:b3,y:b1,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,",
"03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
"030000006f0e00000103000000020000,Logic3 Controller,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"030000006d04000016c2000011010000,Logitech F310 Gamepad (DInput),x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Linux,",
"030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
"030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000006d04000016c2000010010000,Logitech Logitech Dual Action,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"030000006d04000018c2000010010000,Logitech Logitech RumblePad 2 USB,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,y:b4,x:b3,start:b8,guide:b5,back:b2,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b9,righttrigger:b10,platform:Linux,",
"05000000380700006652000025010000,Mad Catz C.T.R.L.R ,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,lefttrigger:a2,righttrigger:a5,",
"03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,",
"03000000780000000600000010010000,Microntek USB Joystick,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftx:a0,lefty:a1,",
"030000005e0400008e02000004010000,Microsoft X-Box 360 pad,platform:Linux,a:b0,b:b1,x:b2,y:b3,back:b6,start:b7,guide:b8,leftshoulder:b4,rightshoulder:b5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,",
"030000005e0400008e02000062230000,Microsoft X-Box 360 pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"030000005e040000d102000001010000,Microsoft X-Box One pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"030000005e040000dd02000003020000,Microsoft X-Box One pad v2,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,platform:Linux,",
"030000005e0400008502000000010000,Microsoft X-Box pad (Japan),platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b6,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:a2,rightshoulder:b2,righttrigger:a5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b6,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:a2,rightshoulder:b2,righttrigger:a5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"05000000d6200000ad0d000001000000,Moga Pro,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b6,leftstick:b7,rightstick:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,",
"030000001008000001e5000010010000,NEXT Classic USB Game Controller,a:b0,b:b1,back:b8,start:b9,rightx:a2,righty:a3,leftx:a0,lefty:a1,platform:Linux,",
"050000007e0500003003000001000000,Nintendo Wii U Pro Controller,platform:Linux,a:b0,b:b1,x:b3,y:b2,back:b8,start:b9,guide:b10,leftshoulder:b4,rightshoulder:b5,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,dpup:b13,dpleft:b15,dpdown:b14,dpright:b16,",
"05000000010000000100000003000000,Nintendo Wiimote,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b9,guide:b10,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
"03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Linux,",
"05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,platform:Linux,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,",
"05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,platform:Linux,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,",
"03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Linux,",
"030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
"060000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,y:b12,x:b15,start:b3,guide:b16,back:b0,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpleft:b7,dpdown:b6,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9,platform:Linux,",
"050000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,y:b12,x:b15,start:b3,guide:b16,back:b0,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpleft:b7,dpdown:b6,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9,platform:Linux,",
"05000000504c415953544154494f4e00,PS3 Controller (Bluetooth),a:b14,b:b13,y:b12,x:b15,start:b3,guide:b16,back:b0,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpleft:b7,dpdown:b6,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9,platform:Linux,",
"030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,platform:Linux,x:b1,a:b0,b:b4,y:b5,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,",
"030000008916000001fd000024010000,Razer Onza Classic Edition,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:b11,dpdown:b14,dpright:b12,dpup:b13,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"030000008916000000fd000024010000,Razer Onza Tournament,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:b13,dpleft:b11,dpdown:b14,dpright:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,",
"03000000790000001100000010010000,RetroLink Saturn Classic Controller,platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b5,guide:b2,start:b8,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,",
"0300000000f000000300000000010000,RetroUSB.com RetroPad,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Linux,",
"0300000000f00000f100000000010000,RetroUSB.com Super RetroPort,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Linux,",
"030000006f0e00001e01000011010000,Rock Candy Gamepad for PS3,platform:Linux,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,guide:b12,leftshoulder:b4,rightshoulder:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,",
"030000006f0e00004601000001010000,Rock Candy Wired Controller for Xbox One,platform:Linux,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,guide:b8,leftstick:b9,rightstick:b10,lefttrigger:a2,righttrigger:a5,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a4,lefttrigger:b6,righttrigger:b7,platform:Linux,",
"03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,y:b3,x:b0,start:b12,guide:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,platform:Linux,",
"03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,y:b1,x:b0,leftstick:b8,rightstick:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b6,righttrigger:b7,platform:Linux,",
"03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,platform:Linux,a:b12,b:b10,x:b13,y:b11,back:b4,start:b5,leftstick:b14,rightstick:b15,leftshoulder:b9,rightshoulder:b8,dpup:b0,dpdown:b2,dpleft:b3,dpright:b1,leftx:a1,lefty:a0,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
"030000004c050000c405000011810000,Sony DualShock 4,a:b0,b:b1,y:b2,x:b3,start:b9,guide:b10,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,",
"030000004c050000c405000011010000,Sony DualShock 4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Linux,",
"050000004c050000cc09000000810000,Sony DualShock 4 (CUH-ZCT2U) (Bluetooth),platform:Linux,a:b0,b:b1,y:b2,x:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,guide:b10,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,lefttrigger:a2,rightx:a3,righty:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"030000004c050000cc09000011810000,Sony DualShock 4 (CUH-ZCT2U) (USB),platform:Linux,a:b0,b:b1,y:b2,x:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,guide:b10,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,lefttrigger:a2,rightx:a3,righty:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"050000004c050000c405000000010000,Sony DualShock 4 BT,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
"030000004c050000cc09000011010000,Sony DualShock 4 V2,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux,",
"050000004c050000cc09000000010000,Sony DualShock 4 V2 BT,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux,",
"030000004c050000a00b000011010000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux,",
"03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,platform:Linux,a:b2,b:b1,y:b0,x:b3,start:b8,back:b9,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b4,righttrigger:b5,",
"030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,",
"03000000666600000488000000010000,Super Joy Box 5 Pro,platform:Linux,a:b2,b:b1,x:b3,y:b0,back:b9,start:b8,leftshoulder:b6,rightshoulder:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b4,righttrigger:b5,dpup:b12,dpleft:b15,dpdown:b14,dpright:b13,",
"030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,y:b3,x:b1,start:b9,guide:,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Linux,",
"030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,platform:Linux,a:b0,b:b2,x:b1,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,",
"030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5,",
"030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,y:b3,x:b1,start:b10,guide:b8,back:b9,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Linux,",
"030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,platform:Linux,a:b1,b:b2,x:b0,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,",
"030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,platform:Linux,a:b1,b:b2,x:b0,y:b3,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
"03000000bd12000015d0000010010000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Linux,",
"03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,platform:Linux,a:b0,b:b1,y:b2,x:b3,start:b9,back:b8,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,lefttrigger:b6,righttrigger:b7,",
"03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,y:b0,x:b3,start:b9,guide:,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,platform:Linux,",
"03000000100800000300000010010000,USB Gamepad,platform:Linux,a:b2,b:b1,x:b3,y:b0,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,",
"03000000de280000ff11000001000000,Valve Streaming Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"05000000ac0500003232000001000000,VR-BOX,platform:Linux,a:b0,b:b1,x:b2,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,",
"030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:b13,dpleft:b11,dpdown:b14,dpright:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,",
"030000005e040000a102000000010000,X360 Wireless Controller,platform:Linux,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
"0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),platform:Linux,a:b0,b:b1,x:b2,y:b3,start:b7,back:b6,guide:b8,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftshoulder:b4,rightshoulder:b5,lefttrigger:a5,righttrigger:a4,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
"050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
"03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,platform:Linux,y:b0,x:b1,b:b3,a:b4,leftshoulder:b2,rightshoulder:b5,back:b6,start:b7,guide:b9,dpleft:b13,dpdown:b12,dpright:b14,dpup:b11,leftx:a0,lefty:a1,",
"78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"78696e70757403000000000000000000,XInput Arcade Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"78696e70757404000000000000000000,XInput Flight Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"78696e70757405000000000000000000,XInput Dance Pad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"78696e70757406000000000000000000,XInput Guitar (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"78696e70757408000000000000000000,XInput Drum Kit (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
NULL
};

73
src/mappings.h.in Normal file
View File

@ -0,0 +1,73 @@
//========================================================================
// GLFW 3.3 - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2006-2016 Camilla Löwy <elmindreda@glfw.org>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
//
//========================================================================
// As mappings.h.in, this file is used by CMake to produce the mappings.h
// header file. If you are adding a GLFW specific gamepad mapping, this is
// where to put it.
//========================================================================
// As mappings.h, this provides all pre-defined gamepad mappings, including
// all available in SDL_GameControllerDB. Do not edit this file. Any gamepad
// mappings not specific to GLFW should be submitted to SDL_GameControllerDB.
// This file can be re-generated from mappings.h.in and the upstream
// gamecontrollerdb.txt with the GenerateMappings.cmake script.
//========================================================================
// All gamepad mappings not labeled GLFW are copied from the
// SDL_GameControllerDB project under the following license:
//
// Simple DirectMedia Layer
// Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the
// use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
const char* _glfwDefaultMappings[] =
{
@GLFW_GAMEPAD_MAPPINGS@
"78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"78696e70757403000000000000000000,XInput Arcade Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"78696e70757404000000000000000000,XInput Flight Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"78696e70757405000000000000000000,XInput Dance Pad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"78696e70757406000000000000000000,XInput Guitar (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"78696e70757408000000000000000000,XInput Drum Kit (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
NULL
};

View File

@ -1,7 +1,7 @@
//======================================================================== //========================================================================
// GLFW 3.3 Mir - www.glfw.org // GLFW 3.3 Mir - www.glfw.org
//------------------------------------------------------------------------ //------------------------------------------------------------------------
// Copyright (c) 2014-2015 Brandon Schaefer <brandon.schaefer@canonical.com> // Copyright (c) 2014-2017 Brandon Schaefer <brandon.schaefer@canonical.com>
// //
// This software is provided 'as-is', without any express or implied // This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages // warranty. In no event will the authors be held liable for any damages
@ -190,18 +190,11 @@ int _glfwPlatformInit(void)
createKeyTables(); createKeyTables();
if (!_glfwInitThreadLocalStoragePOSIX())
return GLFW_FALSE;
if (!_glfwInitJoysticksLinux()) if (!_glfwInitJoysticksLinux())
return GLFW_FALSE; return GLFW_FALSE;
_glfwInitTimerPOSIX(); _glfwInitTimerPOSIX();
// Need the default conf for when we set a NULL cursor
_glfw.mir.defaultConf = mir_cursor_configuration_from_name(mir_default_cursor_name);
_glfw.mir.disabledConf = mir_cursor_configuration_from_name(mir_disabled_cursor_name);
_glfw.mir.eventQueue = calloc(1, sizeof(EventQueue)); _glfw.mir.eventQueue = calloc(1, sizeof(EventQueue));
_glfwInitEventQueueMir(_glfw.mir.eventQueue); _glfwInitEventQueueMir(_glfw.mir.eventQueue);
@ -222,7 +215,6 @@ void _glfwPlatformTerminate(void)
{ {
_glfwTerminateEGL(); _glfwTerminateEGL();
_glfwTerminateJoysticksLinux(); _glfwTerminateJoysticksLinux();
_glfwTerminateThreadLocalStoragePOSIX();
_glfwDeleteEventQueueMir(_glfw.mir.eventQueue); _glfwDeleteEventQueueMir(_glfw.mir.eventQueue);
@ -239,7 +231,7 @@ const char* _glfwPlatformGetVersionString(void)
#else #else
" gettimeofday" " gettimeofday"
#endif #endif
" /dev/js" " evdev"
#if defined(_GLFW_BUILD_DLL) #if defined(_GLFW_BUILD_DLL)
" shared" " shared"
#endif #endif

View File

@ -1,7 +1,7 @@
//======================================================================== //========================================================================
// GLFW 3.3 Mir - www.glfw.org // GLFW 3.3 Mir - www.glfw.org
//------------------------------------------------------------------------ //------------------------------------------------------------------------
// Copyright (c) 2014-2015 Brandon Schaefer <brandon.schaefer@canonical.com> // Copyright (c) 2014-2017 Brandon Schaefer <brandon.schaefer@canonical.com>
// //
// This software is provided 'as-is', without any express or implied // This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages // warranty. In no event will the authors be held liable for any damages
@ -38,33 +38,41 @@
void _glfwPollMonitorsMir(void) void _glfwPollMonitorsMir(void)
{ {
int i; int i;
MirDisplayConfiguration* displayConfig = MirDisplayConfig* displayConfig =
mir_connection_create_display_config(_glfw.mir.connection); mir_connection_create_display_configuration(_glfw.mir.connection);
for (i = 0; i < displayConfig->num_outputs; i++) int numOutputs = mir_display_config_get_num_outputs(displayConfig);
for (i = 0; i < numOutputs; i++)
{ {
const MirDisplayOutput* out = displayConfig->outputs + i; const MirOutput* output = mir_display_config_get_output(displayConfig, i);
MirOutputConnectionState state = mir_output_get_connection_state(output);
bool enabled = mir_output_is_enabled(output);
if (out->used && if (enabled && state == mir_output_connection_state_connected)
out->connected &&
out->num_modes &&
out->current_mode < out->num_modes)
{ {
_GLFWmonitor* monitor = _glfwAllocMonitor("Unknown", int widthMM = mir_output_get_physical_width_mm(output);
out->physical_width_mm, int heightMM = mir_output_get_physical_height_mm(output);
out->physical_height_mm); int x = mir_output_get_position_x(output);
int y = mir_output_get_position_y(output);
int id = mir_output_get_id(output);
size_t currentMode = mir_output_get_current_mode_index(output);
const char* name = mir_output_type_name(mir_output_get_type(output));
monitor->mir.x = out->position_x; _GLFWmonitor* monitor = _glfwAllocMonitor(name,
monitor->mir.y = out->position_y; widthMM,
monitor->mir.outputId = out->output_id; heightMM);
monitor->mir.curMode = out->current_mode; monitor->mir.x = x;
monitor->mir.y = y;
monitor->mir.outputId = id;
monitor->mir.curMode = currentMode;
monitor->modes = _glfwPlatformGetVideoModes(monitor, &monitor->modeCount); monitor->modes = _glfwPlatformGetVideoModes(monitor, &monitor->modeCount);
_glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST); _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST);
} }
} }
mir_display_config_destroy(displayConfig); mir_display_config_release(displayConfig);
} }
@ -80,7 +88,16 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
*ypos = monitor->mir.y; *ypos = monitor->mir.y;
} }
void FillInRGBBitsFromPixelFormat(GLFWvidmode* mode, const MirPixelFormat pf) void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
float* xscale, float* yscale)
{
if (xscale)
*xscale = 1.f;
if (yscale)
*yscale = 1.f;
}
static void FillInRGBBitsFromPixelFormat(GLFWvidmode* mode, const MirPixelFormat pf)
{ {
switch (pf) switch (pf)
{ {
@ -117,30 +134,52 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found)
{ {
int i; int i;
GLFWvidmode* modes = NULL; GLFWvidmode* modes = NULL;
MirDisplayConfiguration* displayConfig = MirDisplayConfig* displayConfig =
mir_connection_create_display_config(_glfw.mir.connection); mir_connection_create_display_configuration(_glfw.mir.connection);
for (i = 0; i < displayConfig->num_outputs; i++) int numOutputs = mir_display_config_get_num_outputs(displayConfig);
for (i = 0; i < numOutputs; i++)
{ {
const MirDisplayOutput* out = displayConfig->outputs + i; const MirOutput* output = mir_display_config_get_output(displayConfig, i);
if (out->output_id != monitor->mir.outputId) int id = mir_output_get_id(output);
if (id != monitor->mir.outputId)
continue; continue;
modes = calloc(out->num_modes, sizeof(GLFWvidmode)); MirOutputConnectionState state = mir_output_get_connection_state(output);
bool enabled = mir_output_is_enabled(output);
for (*found = 0; *found < out->num_modes; (*found)++) // We must have been disconnected
if (!enabled || state != mir_output_connection_state_connected)
{ {
modes[*found].width = out->modes[*found].horizontal_resolution; _glfwInputError(GLFW_PLATFORM_ERROR,
modes[*found].height = out->modes[*found].vertical_resolution; "Mir: Monitor no longer connected");
modes[*found].refreshRate = out->modes[*found].refresh_rate; return NULL;
}
FillInRGBBitsFromPixelFormat(&modes[*found], out->output_formats[*found]); int numModes = mir_output_get_num_modes(output);
modes = calloc(numModes, sizeof(GLFWvidmode));
for (*found = 0; *found < numModes; (*found)++)
{
const MirOutputMode* mode = mir_output_get_mode(output, *found);
int width = mir_output_mode_get_width(mode);
int height = mir_output_mode_get_height(mode);
double refreshRate = mir_output_mode_get_refresh_rate(mode);
MirPixelFormat currentFormat = mir_output_get_current_pixel_format(output);
modes[*found].width = width;
modes[*found].height = height;
modes[*found].refreshRate = refreshRate;
FillInRGBBitsFromPixelFormat(&modes[*found], currentFormat);
} }
break; break;
} }
mir_display_config_destroy(displayConfig); mir_display_config_release(displayConfig);
return modes; return modes;
} }
@ -173,4 +212,3 @@ GLFWAPI int glfwGetMirMonitor(GLFWmonitor* handle)
_GLFW_REQUIRE_INIT_OR_RETURN(0); _GLFW_REQUIRE_INIT_OR_RETURN(0);
return monitor->mir.outputId; return monitor->mir.outputId;
} }

View File

@ -1,7 +1,7 @@
//======================================================================== //========================================================================
// GLFW 3.3 Mir - www.glfw.org // GLFW 3.3 Mir - www.glfw.org
//------------------------------------------------------------------------ //------------------------------------------------------------------------
// Copyright (c) 2014-2015 Brandon Schaefer <brandon.schaefer@canonical.com> // Copyright (c) 2014-2017 Brandon Schaefer <brandon.schaefer@canonical.com>
// //
// This software is provided 'as-is', without any express or implied // This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages // warranty. In no event will the authors be held liable for any damages
@ -24,30 +24,27 @@
// //
//======================================================================== //========================================================================
#ifndef _glfw3_mir_platform_h_
#define _glfw3_mir_platform_h_
#include <sys/queue.h> #include <sys/queue.h>
#include <pthread.h> #include <pthread.h>
#include <dlfcn.h> #include <dlfcn.h>
#include <mir_toolkit/mir_client_library.h> #include <mir_toolkit/mir_client_library.h>
typedef VkFlags VkMirSurfaceCreateFlagsKHR; typedef VkFlags VkMirWindowCreateFlagsKHR;
typedef struct VkMirSurfaceCreateInfoKHR typedef struct VkMirWindowCreateInfoKHR
{ {
VkStructureType sType; VkStructureType sType;
const void* pNext; const void* pNext;
VkMirSurfaceCreateFlagsKHR flags; VkMirWindowCreateFlagsKHR flags;
MirConnection* connection; MirConnection* connection;
MirSurface* mirSurface; MirWindow* mirWindow;
} VkMirSurfaceCreateInfoKHR; } VkMirWindowCreateInfoKHR;
typedef VkResult (APIENTRY *PFN_vkCreateMirSurfaceKHR)(VkInstance,const VkMirSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); typedef VkResult (APIENTRY *PFN_vkCreateMirWindowKHR)(VkInstance,const VkMirWindowCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*);
typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceMirPresentationSupportKHR)(VkPhysicalDevice,uint32_t,MirConnection*); typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceMirPresentationSupportKHR)(VkPhysicalDevice,uint32_t,MirConnection*);
#include "posix_tls.h" #include "posix_thread.h"
#include "posix_time.h" #include "posix_time.h"
#include "linux_joystick.h" #include "linux_joystick.h"
#include "xkb_unicode.h" #include "xkb_unicode.h"
@ -58,7 +55,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceMirPresentationSupportKHR)(Vk
#define _glfw_dlclose(handle) dlclose(handle) #define _glfw_dlclose(handle) dlclose(handle)
#define _glfw_dlsym(handle, name) dlsym(handle, name) #define _glfw_dlsym(handle, name) dlsym(handle, name)
#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->mir.window) #define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->mir.nativeWindow)
#define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.mir.display) #define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.mir.display)
#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowMir mir #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowMir mir
@ -81,10 +78,10 @@ typedef struct EventQueue
// //
typedef struct _GLFWwindowMir typedef struct _GLFWwindowMir
{ {
MirSurface* surface; MirWindow* window;
int width; int width;
int height; int height;
MirEGLNativeWindowType window; MirEGLNativeWindowType nativeWindow;
_GLFWcursor* currentCursor; _GLFWcursor* currentCursor;
} _GLFWwindowMir; } _GLFWwindowMir;
@ -106,8 +103,6 @@ typedef struct _GLFWlibraryMir
{ {
MirConnection* connection; MirConnection* connection;
MirEGLNativeDisplayType display; MirEGLNativeDisplayType display;
MirCursorConfiguration* defaultConf;
MirCursorConfiguration* disabledConf;
EventQueue* eventQueue; EventQueue* eventQueue;
short int keycodes[256]; short int keycodes[256];
@ -128,10 +123,11 @@ typedef struct _GLFWcursorMir
{ {
MirCursorConfiguration* conf; MirCursorConfiguration* conf;
MirBufferStream* customCursor; MirBufferStream* customCursor;
char const* cursorName; // only needed for system cursors
} _GLFWcursorMir; } _GLFWcursorMir;
extern void _glfwPollMonitorsMir(void);
extern void _glfwInitEventQueueMir(EventQueue* queue); extern void _glfwInitEventQueueMir(EventQueue* queue);
extern void _glfwDeleteEventQueueMir(EventQueue* queue); extern void _glfwDeleteEventQueueMir(EventQueue* queue);
#endif // _glfw3_mir_platform_h_

View File

@ -1,7 +1,7 @@
//======================================================================== //========================================================================
// GLFW 3.3 Mir - www.glfw.org // GLFW 3.3 Mir - www.glfw.org
//------------------------------------------------------------------------ //------------------------------------------------------------------------
// Copyright (c) 2014-2015 Brandon Schaefer <brandon.schaefer@canonical.com> // Copyright (c) 2014-2017 Brandon Schaefer <brandon.schaefer@canonical.com>
// //
// This software is provided 'as-is', without any express or implied // This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages // warranty. In no event will the authors be held liable for any damages
@ -89,9 +89,6 @@ static EventNode* dequeueEvent(EventQueue* queue)
return node; return node;
} }
/* FIXME Soon to be changed upstream mir! So we can use an egl config to figure out
the best pixel format!
*/
static MirPixelFormat findValidPixelFormat(void) static MirPixelFormat findValidPixelFormat(void)
{ {
unsigned int i, validFormats, mirPixelFormats = 32; unsigned int i, validFormats, mirPixelFormats = 32;
@ -120,12 +117,16 @@ static int mirModToGLFWMod(uint32_t mods)
if (mods & mir_input_event_modifier_alt) if (mods & mir_input_event_modifier_alt)
publicMods |= GLFW_MOD_ALT; publicMods |= GLFW_MOD_ALT;
else if (mods & mir_input_event_modifier_shift) if (mods & mir_input_event_modifier_shift)
publicMods |= GLFW_MOD_SHIFT; publicMods |= GLFW_MOD_SHIFT;
else if (mods & mir_input_event_modifier_ctrl) if (mods & mir_input_event_modifier_ctrl)
publicMods |= GLFW_MOD_CONTROL; publicMods |= GLFW_MOD_CONTROL;
else if (mods & mir_input_event_modifier_meta) if (mods & mir_input_event_modifier_meta)
publicMods |= GLFW_MOD_SUPER; publicMods |= GLFW_MOD_SUPER;
if (mods & mir_input_event_modifier_caps_lock)
publicMods |= GLFW_MOD_CAPS_LOCK;
if (mods & mir_input_event_modifier_num_lock)
publicMods |= GLFW_MOD_NUM_LOCK;
return publicMods; return publicMods;
} }
@ -283,14 +284,14 @@ static void handleEvent(const MirEvent* event, _GLFWwindow* window)
} }
} }
static void addNewEvent(MirSurface* surface, const MirEvent* event, void* context) static void addNewEvent(MirWindow* window, const MirEvent* event, void* context)
{ {
enqueueEvent(event, context); enqueueEvent(event, context);
} }
static GLFWbool createSurface(_GLFWwindow* window) static GLFWbool createWindow(_GLFWwindow* window)
{ {
MirSurfaceSpec* spec; MirWindowSpec* spec;
MirBufferUsage buffer_usage = mir_buffer_usage_hardware; MirBufferUsage buffer_usage = mir_buffer_usage_hardware;
MirPixelFormat pixel_format = findValidPixelFormat(); MirPixelFormat pixel_format = findValidPixelFormat();
@ -301,40 +302,39 @@ static GLFWbool createSurface(_GLFWwindow* window)
return GLFW_FALSE; return GLFW_FALSE;
} }
spec = mir_connection_create_spec_for_normal_surface(_glfw.mir.connection, spec = mir_create_normal_window_spec(_glfw.mir.connection,
window->mir.width, window->mir.width,
window->mir.height, window->mir.height);
pixel_format);
mir_surface_spec_set_buffer_usage(spec, buffer_usage); mir_window_spec_set_pixel_format(spec, pixel_format);
mir_surface_spec_set_name(spec, "MirSurface"); mir_window_spec_set_buffer_usage(spec, buffer_usage);
window->mir.surface = mir_surface_create_sync(spec); window->mir.window = mir_create_window_sync(spec);
mir_surface_spec_release(spec); mir_window_spec_release(spec);
if (!mir_surface_is_valid(window->mir.surface)) if (!mir_window_is_valid(window->mir.window))
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, _glfwInputError(GLFW_PLATFORM_ERROR,
"Mir: Unable to create surface: %s", "Mir: Unable to create window: %s",
mir_surface_get_error_message(window->mir.surface)); mir_window_get_error_message(window->mir.window));
return GLFW_FALSE; return GLFW_FALSE;
} }
mir_surface_set_event_handler(window->mir.surface, addNewEvent, window); mir_window_set_event_handler(window->mir.window, addNewEvent, window);
return GLFW_TRUE; return GLFW_TRUE;
} }
static void setSurfaceConfinement(_GLFWwindow* window, MirPointerConfinementState state) static void setWindowConfinement(_GLFWwindow* window, MirPointerConfinementState state)
{ {
MirSurfaceSpec* spec; MirWindowSpec* spec;
spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); spec = mir_create_window_spec(_glfw.mir.connection);
mir_surface_spec_set_pointer_confinement(spec, state); mir_window_spec_set_pointer_confinement(spec, state);
mir_surface_apply_spec(window->mir.surface, spec); mir_window_apply_spec(window->mir.window, spec);
mir_surface_spec_release(spec); mir_window_spec_release(spec);
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -381,12 +381,12 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
GLFWvidmode mode; GLFWvidmode mode;
_glfwPlatformGetVideoMode(window->monitor, &mode); _glfwPlatformGetVideoMode(window->monitor, &mode);
mir_surface_set_state(window->mir.surface, mir_surface_state_fullscreen); mir_window_set_state(window->mir.window, mir_window_state_fullscreen);
if (wndconfig->width > mode.width || wndconfig->height > mode.height) if (wndconfig->width > mode.width || wndconfig->height > mode.height)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, _glfwInputError(GLFW_PLATFORM_ERROR,
"Mir: Requested surface size too large: %ix%i", "Mir: Requested window size too large: %ix%i",
wndconfig->width, wndconfig->height); wndconfig->width, wndconfig->height);
return GLFW_FALSE; return GLFW_FALSE;
@ -397,11 +397,11 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
window->mir.height = wndconfig->height; window->mir.height = wndconfig->height;
window->mir.currentCursor = NULL; window->mir.currentCursor = NULL;
if (!createSurface(window)) if (!createWindow(window))
return GLFW_FALSE; return GLFW_FALSE;
window->mir.window = mir_buffer_stream_get_egl_native_window( window->mir.nativeWindow = mir_buffer_stream_get_egl_native_window(
mir_surface_get_buffer_stream(window->mir.surface)); mir_window_get_buffer_stream(window->mir.window));
if (ctxconfig->client != GLFW_NO_API) if (ctxconfig->client != GLFW_NO_API)
{ {
@ -430,10 +430,10 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
if (_glfw.mir.disabledCursorWindow == window) if (_glfw.mir.disabledCursorWindow == window)
_glfw.mir.disabledCursorWindow = NULL; _glfw.mir.disabledCursorWindow = NULL;
if (mir_surface_is_valid(window->mir.surface)) if (mir_window_is_valid(window->mir.window))
{ {
mir_surface_release_sync(window->mir.surface); mir_window_release_sync(window->mir.window);
window->mir.surface = NULL; window->mir.window= NULL;
} }
if (window->context.destroy) if (window->context.destroy)
@ -442,13 +442,12 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
{ {
MirSurfaceSpec* spec; MirWindowSpec* spec;
spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); spec = mir_create_window_spec(_glfw.mir.connection);
mir_surface_spec_set_name(spec, title); mir_window_spec_set_name(spec, title);
mir_window_apply_spec(window->mir.window, spec);
mir_surface_apply_spec(window->mir.surface, spec); mir_window_spec_release(spec);
mir_surface_spec_release(spec);
} }
void _glfwPlatformSetWindowIcon(_GLFWwindow* window, void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
@ -460,22 +459,30 @@ void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
{ {
MirSurfaceSpec* spec; MirWindowSpec* spec;
spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); spec = mir_create_window_spec(_glfw.mir.connection);
mir_surface_spec_set_width (spec, width); mir_window_spec_set_width (spec, width);
mir_surface_spec_set_height(spec, height); mir_window_spec_set_height(spec, height);
mir_surface_apply_spec(window->mir.surface, spec); mir_window_apply_spec(window->mir.window, spec);
mir_surface_spec_release(spec); mir_window_spec_release(spec);
} }
void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
int minwidth, int minheight, int minwidth, int minheight,
int maxwidth, int maxheight) int maxwidth, int maxheight)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, MirWindowSpec* spec;
"Mir: Unsupported function %s", __PRETTY_FUNCTION__);
spec = mir_create_window_spec(_glfw.mir.connection);
mir_window_spec_set_max_width (spec, maxwidth);
mir_window_spec_set_max_height(spec, maxheight);
mir_window_spec_set_min_width (spec, minwidth);
mir_window_spec_set_min_height(spec, minheight);
mir_window_apply_spec(window->mir.window, spec);
mir_window_spec_release(spec);
} }
void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom)
@ -512,59 +519,74 @@ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
*height = window->mir.height; *height = window->mir.height;
} }
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
float* xscale, float* yscale)
{
if (xscale)
*xscale = 1.f;
if (yscale)
*yscale = 1.f;
}
void _glfwPlatformIconifyWindow(_GLFWwindow* window) void _glfwPlatformIconifyWindow(_GLFWwindow* window)
{ {
MirSurfaceSpec* spec; MirWindowSpec* spec;
spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); spec = mir_create_window_spec(_glfw.mir.connection);
mir_surface_spec_set_state(spec, mir_surface_state_minimized); mir_window_spec_set_state(spec, mir_window_state_minimized);
mir_surface_apply_spec(window->mir.surface, spec); mir_window_apply_spec(window->mir.window, spec);
mir_surface_spec_release(spec); mir_window_spec_release(spec);
} }
void _glfwPlatformRestoreWindow(_GLFWwindow* window) void _glfwPlatformRestoreWindow(_GLFWwindow* window)
{ {
MirSurfaceSpec* spec; MirWindowSpec* spec;
spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); spec = mir_create_window_spec(_glfw.mir.connection);
mir_surface_spec_set_state(spec, mir_surface_state_restored); mir_window_spec_set_state(spec, mir_window_state_restored);
mir_surface_apply_spec(window->mir.surface, spec); mir_window_apply_spec(window->mir.window, spec);
mir_surface_spec_release(spec); mir_window_spec_release(spec);
} }
void _glfwPlatformMaximizeWindow(_GLFWwindow* window) void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
{ {
MirSurfaceSpec* spec; MirWindowSpec* spec;
spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); spec = mir_create_window_spec(_glfw.mir.connection);
mir_surface_spec_set_state(spec, mir_surface_state_maximized); mir_window_spec_set_state(spec, mir_window_state_maximized);
mir_surface_apply_spec(window->mir.surface, spec); mir_window_apply_spec(window->mir.window, spec);
mir_surface_spec_release(spec); mir_window_spec_release(spec);
} }
void _glfwPlatformHideWindow(_GLFWwindow* window) void _glfwPlatformHideWindow(_GLFWwindow* window)
{ {
MirSurfaceSpec* spec; MirWindowSpec* spec;
spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); spec = mir_create_window_spec(_glfw.mir.connection);
mir_surface_spec_set_state(spec, mir_surface_state_hidden); mir_window_spec_set_state(spec, mir_window_state_hidden);
mir_surface_apply_spec(window->mir.surface, spec); mir_window_apply_spec(window->mir.window, spec);
mir_surface_spec_release(spec); mir_window_spec_release(spec);
} }
void _glfwPlatformShowWindow(_GLFWwindow* window) void _glfwPlatformShowWindow(_GLFWwindow* window)
{ {
MirSurfaceSpec* spec; MirWindowSpec* spec;
spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); spec = mir_create_window_spec(_glfw.mir.connection);
mir_surface_spec_set_state(spec, mir_surface_state_restored); mir_window_spec_set_state(spec, mir_window_state_restored);
mir_surface_apply_spec(window->mir.surface, spec); mir_window_apply_spec(window->mir.window, spec);
mir_surface_spec_release(spec); mir_window_spec_release(spec);
}
void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Mir: Unsupported function %s", __PRETTY_FUNCTION__);
} }
void _glfwPlatformFocusWindow(_GLFWwindow* window) void _glfwPlatformFocusWindow(_GLFWwindow* window)
@ -585,7 +607,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
int _glfwPlatformWindowFocused(_GLFWwindow* window) int _glfwPlatformWindowFocused(_GLFWwindow* window)
{ {
return mir_surface_get_focus(window->mir.surface) == mir_surface_focused; return mir_window_get_focus_state(window->mir.window) == mir_window_focus_state_focused;
} }
int _glfwPlatformWindowIconified(_GLFWwindow* window) int _glfwPlatformWindowIconified(_GLFWwindow* window)
@ -597,12 +619,19 @@ int _glfwPlatformWindowIconified(_GLFWwindow* window)
int _glfwPlatformWindowVisible(_GLFWwindow* window) int _glfwPlatformWindowVisible(_GLFWwindow* window)
{ {
return mir_surface_get_visibility(window->mir.surface) == mir_surface_visibility_exposed; return mir_window_get_visibility(window->mir.window) == mir_window_visibility_exposed;
} }
int _glfwPlatformWindowMaximized(_GLFWwindow* window) int _glfwPlatformWindowMaximized(_GLFWwindow* window)
{ {
return mir_surface_get_state(window->mir.surface) == mir_surface_state_maximized; return mir_window_get_state(window->mir.window) == mir_window_state_maximized;
}
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Mir: Unsupported function %s", __PRETTY_FUNCTION__);
return GLFW_FALSE;
} }
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
@ -623,6 +652,15 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
"Mir: Unsupported function %s", __PRETTY_FUNCTION__); "Mir: Unsupported function %s", __PRETTY_FUNCTION__);
} }
float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
{
return 1.f;
}
void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
{
}
void _glfwPlatformPollEvents(void) void _glfwPlatformPollEvents(void)
{ {
EventNode* node = NULL; EventNode* node = NULL;
@ -638,7 +676,7 @@ void _glfwPlatformWaitEvents(void)
{ {
pthread_mutex_lock(&_glfw.mir.eventMutex); pthread_mutex_lock(&_glfw.mir.eventMutex);
if (emptyEventQueue(_glfw.mir.eventQueue)) while (emptyEventQueue(_glfw.mir.eventQueue))
pthread_cond_wait(&_glfw.mir.eventCond, &_glfw.mir.eventMutex); pthread_cond_wait(&_glfw.mir.eventCond, &_glfw.mir.eventMutex);
pthread_mutex_unlock(&_glfw.mir.eventMutex); pthread_mutex_unlock(&_glfw.mir.eventMutex);
@ -681,54 +719,40 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
int xhot, int yhot) int xhot, int yhot)
{ {
MirBufferStream* stream; MirBufferStream* stream;
MirPixelFormat pixel_format = findValidPixelFormat();
int i_w = image->width; int i_w = image->width;
int i_h = image->height; int i_h = image->height;
if (pixel_format == mir_pixel_format_invalid)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Mir: Unable to find a correct pixel format");
return GLFW_FALSE;
}
stream = mir_connection_create_buffer_stream_sync(_glfw.mir.connection, stream = mir_connection_create_buffer_stream_sync(_glfw.mir.connection,
i_w, i_h, i_w, i_h,
pixel_format, mir_pixel_format_argb_8888,
mir_buffer_usage_software); mir_buffer_usage_software);
cursor->mir.conf = mir_cursor_configuration_from_buffer_stream(stream, xhot, yhot); cursor->mir.conf = mir_cursor_configuration_from_buffer_stream(stream, xhot, yhot);
char* dest;
unsigned char *pixels;
int i, r_stride, bytes_per_pixel, bytes_per_row;
MirGraphicsRegion region; MirGraphicsRegion region;
mir_buffer_stream_get_graphics_region(stream, &region); mir_buffer_stream_get_graphics_region(stream, &region);
// FIXME Figure this out based on the current_pf unsigned char* pixels = image->pixels;
bytes_per_pixel = 4; char* dest = region.vaddr;
bytes_per_row = bytes_per_pixel * i_w; int i;
dest = region.vaddr; for (i = 0; i < i_w * i_h; i++, pixels += 4)
pixels = image->pixels;
r_stride = region.stride;
for (i = 0; i < i_h; i++)
{ {
memcpy(dest, pixels, bytes_per_row); unsigned int alpha = pixels[3];
dest += r_stride; *dest++ = (char)(pixels[2] * alpha / 255);
pixels += r_stride; *dest++ = (char)(pixels[1] * alpha / 255);
*dest++ = (char)(pixels[0] * alpha / 255);
*dest++ = (char)alpha;
} }
mir_buffer_stream_swap_buffers_sync(stream);
cursor->mir.customCursor = stream; cursor->mir.customCursor = stream;
return GLFW_TRUE; return GLFW_TRUE;
} }
const char* getSystemCursorName(int shape) static const char* getSystemCursorName(int shape)
{ {
switch (shape) switch (shape)
{ {
@ -751,17 +775,11 @@ const char* getSystemCursorName(int shape)
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
{ {
const char* cursor_name = getSystemCursorName(shape); cursor->mir.conf = NULL;
if (cursor_name)
{
cursor->mir.conf = mir_cursor_configuration_from_name(cursor_name);
cursor->mir.customCursor = NULL; cursor->mir.customCursor = NULL;
cursor->mir.cursorName = getSystemCursorName(shape);
return GLFW_TRUE; return cursor->mir.cursorName != NULL;
}
return GLFW_FALSE;
} }
void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
@ -772,21 +790,32 @@ void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
mir_buffer_stream_release_sync(cursor->mir.customCursor); mir_buffer_stream_release_sync(cursor->mir.customCursor);
} }
static void setCursorNameForWindow(MirWindow* window, char const* name)
{
MirWindowSpec* spec = mir_create_window_spec(_glfw.mir.connection);
mir_window_spec_set_cursor_name(spec, name);
mir_window_apply_spec(window, spec);
mir_window_spec_release(spec);
}
void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
{ {
if (cursor && cursor->mir.conf) if (cursor)
{ {
window->mir.currentCursor = cursor; window->mir.currentCursor = cursor;
mir_wait_for(mir_surface_configure_cursor(window->mir.surface, cursor->mir.conf)); if (cursor->mir.cursorName)
if (cursor->mir.customCursor)
{ {
mir_buffer_stream_swap_buffers_sync(cursor->mir.customCursor); setCursorNameForWindow(window->mir.window, cursor->mir.cursorName);
}
else if (cursor->mir.conf)
{
mir_window_configure_cursor(window->mir.window, cursor->mir.conf);
} }
} }
else else
{ {
mir_wait_for(mir_surface_configure_cursor(window->mir.surface, _glfw.mir.defaultConf)); setCursorNameForWindow(window->mir.window, mir_default_cursor_name);
} }
} }
@ -807,8 +836,8 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
if (mode == GLFW_CURSOR_DISABLED) if (mode == GLFW_CURSOR_DISABLED)
{ {
_glfw.mir.disabledCursorWindow = window; _glfw.mir.disabledCursorWindow = window;
setSurfaceConfinement(window, mir_pointer_confined_to_surface); setWindowConfinement(window, mir_pointer_confined_to_window);
mir_wait_for(mir_surface_configure_cursor(window->mir.surface, _glfw.mir.disabledConf)); setCursorNameForWindow(window->mir.window, mir_disabled_cursor_name);
} }
else else
{ {
@ -816,7 +845,7 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
if (_glfw.mir.disabledCursorWindow == window) if (_glfw.mir.disabledCursorWindow == window)
{ {
_glfw.mir.disabledCursorWindow = NULL; _glfw.mir.disabledCursorWindow = NULL;
setSurfaceConfinement(window, mir_pointer_unconfined); setWindowConfinement(window, mir_pointer_unconfined);
} }
if (window->cursorMode == GLFW_CURSOR_NORMAL) if (window->cursorMode == GLFW_CURSOR_NORMAL)
@ -825,12 +854,12 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
} }
else if (window->cursorMode == GLFW_CURSOR_HIDDEN) else if (window->cursorMode == GLFW_CURSOR_HIDDEN)
{ {
mir_wait_for(mir_surface_configure_cursor(window->mir.surface, _glfw.mir.disabledConf)); setCursorNameForWindow(window->mir.window, mir_disabled_cursor_name);
} }
} }
} }
const char* _glfwPlatformGetKeyName(int key, int scancode) const char* _glfwPlatformGetScancodeName(int scancode)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, _glfwInputError(GLFW_PLATFORM_ERROR,
"Mir: Unsupported function %s", __PRETTY_FUNCTION__); "Mir: Unsupported function %s", __PRETTY_FUNCTION__);
@ -842,13 +871,13 @@ int _glfwPlatformGetKeyScancode(int key)
return _glfw.mir.scancodes[key]; return _glfw.mir.scancodes[key];
} }
void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) void _glfwPlatformSetClipboardString(const char* string)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, _glfwInputError(GLFW_PLATFORM_ERROR,
"Mir: Unsupported function %s", __PRETTY_FUNCTION__); "Mir: Unsupported function %s", __PRETTY_FUNCTION__);
} }
const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) const char* _glfwPlatformGetClipboardString(void)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, _glfwInputError(GLFW_PLATFORM_ERROR,
"Mir: Unsupported function %s", __PRETTY_FUNCTION__); "Mir: Unsupported function %s", __PRETTY_FUNCTION__);
@ -869,7 +898,8 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
VkPhysicalDevice device, VkPhysicalDevice device,
uint32_t queuefamily) uint32_t queuefamily)
{ {
PFN_vkGetPhysicalDeviceMirPresentationSupportKHR vkGetPhysicalDeviceMirPresentationSupportKHR = PFN_vkGetPhysicalDeviceMirPresentationSupportKHR
vkGetPhysicalDeviceMirPresentationSupportKHR =
(PFN_vkGetPhysicalDeviceMirPresentationSupportKHR) (PFN_vkGetPhysicalDeviceMirPresentationSupportKHR)
vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceMirPresentationSupportKHR"); vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceMirPresentationSupportKHR");
if (!vkGetPhysicalDeviceMirPresentationSupportKHR) if (!vkGetPhysicalDeviceMirPresentationSupportKHR)
@ -890,12 +920,12 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
VkSurfaceKHR* surface) VkSurfaceKHR* surface)
{ {
VkResult err; VkResult err;
VkMirSurfaceCreateInfoKHR sci; VkMirWindowCreateInfoKHR sci;
PFN_vkCreateMirSurfaceKHR vkCreateMirSurfaceKHR; PFN_vkCreateMirWindowKHR vkCreateMirWindowKHR;
vkCreateMirSurfaceKHR = (PFN_vkCreateMirSurfaceKHR) vkCreateMirWindowKHR = (PFN_vkCreateMirWindowKHR)
vkGetInstanceProcAddr(instance, "vkCreateMirSurfaceKHR"); vkGetInstanceProcAddr(instance, "vkCreateMirWindowKHR");
if (!vkCreateMirSurfaceKHR) if (!vkCreateMirWindowKHR)
{ {
_glfwInputError(GLFW_API_UNAVAILABLE, _glfwInputError(GLFW_API_UNAVAILABLE,
"Mir: Vulkan instance missing VK_KHR_mir_surface extension"); "Mir: Vulkan instance missing VK_KHR_mir_surface extension");
@ -905,9 +935,9 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
memset(&sci, 0, sizeof(sci)); memset(&sci, 0, sizeof(sci));
sci.sType = VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR; sci.sType = VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR;
sci.connection = _glfw.mir.connection; sci.connection = _glfw.mir.connection;
sci.mirSurface = window->mir.surface; sci.mirWindow = window->mir.window;
err = vkCreateMirSurfaceKHR(instance, &sci, allocator, surface); err = vkCreateMirWindowKHR(instance, &sci, allocator, surface);
if (err) if (err)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, _glfwInputError(GLFW_PLATFORM_ERROR,
@ -929,10 +959,10 @@ GLFWAPI MirConnection* glfwGetMirDisplay(void)
return _glfw.mir.connection; return _glfw.mir.connection;
} }
GLFWAPI MirSurface* glfwGetMirWindow(GLFWwindow* handle) GLFWAPI MirWindow* glfwGetMirWindow(GLFWwindow* handle)
{ {
_GLFWwindow* window = (_GLFWwindow*) handle; _GLFWwindow* window = (_GLFWwindow*) handle;
_GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
return window->mir.surface; return window->mir.window;
} }

View File

@ -107,6 +107,19 @@ void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement)
else if (action == GLFW_DISCONNECTED) else if (action == GLFW_DISCONNECTED)
{ {
int i; int i;
_GLFWwindow* window;
for (window = _glfw.windowListHead; window; window = window->next)
{
if (window->monitor == monitor)
{
int width, height, xoff, yoff;
_glfwPlatformGetWindowSize(window, &width, &height);
_glfwPlatformSetWindowMonitor(window, NULL, 0, 0, width, height, 0);
_glfwPlatformGetWindowFrameSize(window, &xoff, &yoff, NULL, NULL);
_glfwPlatformSetWindowPos(window, xoff, yoff);
}
}
for (i = 0; i < _glfw.monitorCount; i++) for (i = 0; i < _glfw.monitorCount; i++)
{ {
@ -314,6 +327,21 @@ GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* handle, int* widthMM, int*
*heightMM = monitor->heightMM; *heightMM = monitor->heightMM;
} }
GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* handle,
float* xscale, float* yscale)
{
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
assert(monitor != NULL);
if (xscale)
*xscale = 0.f;
if (yscale)
*yscale = 0.f;
_GLFW_REQUIRE_INIT();
_glfwPlatformGetMonitorContentScale(monitor, xscale, yscale);
}
GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* handle) GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* handle)
{ {
_GLFWmonitor* monitor = (_GLFWmonitor*) handle; _GLFWmonitor* monitor = (_GLFWmonitor*) handle;

View File

@ -24,9 +24,6 @@
// //
//======================================================================== //========================================================================
#ifndef _glfw3_nsgl_context_h_
#define _glfw3_nsgl_context_h_
#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextNSGL nsgl #define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextNSGL nsgl
#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryNSGL nsgl #define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryNSGL nsgl
@ -57,4 +54,3 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
const _GLFWfbconfig* fbconfig); const _GLFWfbconfig* fbconfig);
void _glfwDestroyContextNSGL(_GLFWwindow* window); void _glfwDestroyContextNSGL(_GLFWwindow* window);
#endif // _glfw3_nsgl_context_h_

View File

@ -34,7 +34,7 @@ static void makeContextCurrentNSGL(_GLFWwindow* window)
else else
[NSOpenGLContext clearCurrentContext]; [NSOpenGLContext clearCurrentContext];
_glfwPlatformSetCurrentContext(window); _glfwPlatformSetTls(&_glfw.contextSlot, window);
} }
static void swapBuffersNSGL(_GLFWwindow* window) static void swapBuffersNSGL(_GLFWwindow* window)
@ -45,7 +45,7 @@ static void swapBuffersNSGL(_GLFWwindow* window)
static void swapIntervalNSGL(int interval) static void swapIntervalNSGL(int interval)
{ {
_GLFWwindow* window = _glfwPlatformGetCurrentContext(); _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot);
GLint sync = interval; GLint sync = interval;
[window->context.nsgl.object setValues:&sync [window->context.nsgl.object setValues:&sync
@ -119,8 +119,6 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig, const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig) const _GLFWfbconfig* fbconfig)
{ {
unsigned int attributeCount = 0;
if (ctxconfig->client == GLFW_OPENGL_ES_API) if (ctxconfig->client == GLFW_OPENGL_ES_API)
{ {
_glfwInputError(GLFW_API_UNAVAILABLE, _glfwInputError(GLFW_API_UNAVAILABLE,
@ -145,49 +143,59 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
} }
} }
// Context robustness modes (GL_KHR_robustness) are not yet supported on // Context robustness modes (GL_KHR_robustness) are not yet supported by
// macOS but are not a hard constraint, so ignore and continue // macOS but are not a hard constraint, so ignore and continue
// Context release behaviors (GL_KHR_context_flush_control) are not yet // Context release behaviors (GL_KHR_context_flush_control) are not yet
// supported on macOS but are not a hard constraint, so ignore and continue // supported by macOS but are not a hard constraint, so ignore and continue
#define ADD_ATTR(x) { attributes[attributeCount++] = x; } // Debug contexts (GL_KHR_debug) are not yet supported by macOS but are not
#define ADD_ATTR2(x, y) { ADD_ATTR(x); ADD_ATTR(y); } // a hard constraint, so ignore and continue
// Arbitrary array size here // No-error contexts (GL_KHR_no_error) are not yet supported by macOS but
NSOpenGLPixelFormatAttribute attributes[40]; // are not a hard constraint, so ignore and continue
ADD_ATTR(NSOpenGLPFAAccelerated); #define addAttrib(a) \
ADD_ATTR(NSOpenGLPFAClosestPolicy); { \
assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \
attribs[index++] = a; \
}
#define setAttrib(a, v) { addAttrib(a); addAttrib(v); }
NSOpenGLPixelFormatAttribute attribs[40];
int index = 0;
addAttrib(NSOpenGLPFAAccelerated);
addAttrib(NSOpenGLPFAClosestPolicy);
if (ctxconfig->nsgl.offline) if (ctxconfig->nsgl.offline)
{ {
ADD_ATTR(NSOpenGLPFAAllowOfflineRenderers); addAttrib(NSOpenGLPFAAllowOfflineRenderers);
// NOTE: This replaces the NSSupportsAutomaticGraphicsSwitching key in // NOTE: This replaces the NSSupportsAutomaticGraphicsSwitching key in
// Info.plist for unbundled applications // Info.plist for unbundled applications
// HACK: This assumes that NSOpenGLPixelFormat will remain // HACK: This assumes that NSOpenGLPixelFormat will remain
// a straightforward wrapper of its CGL counterpart // a straightforward wrapper of its CGL counterpart
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 100800 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 100800
ADD_ATTR(kCGLPFASupportsAutomaticGraphicsSwitching); addAttrib(kCGLPFASupportsAutomaticGraphicsSwitching);
#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ #endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/
} }
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
if (ctxconfig->major >= 4) if (ctxconfig->major >= 4)
{ {
ADD_ATTR2(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core); setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core);
} }
else else
#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ #endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/
if (ctxconfig->major >= 3) if (ctxconfig->major >= 3)
{ {
ADD_ATTR2(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core);
} }
if (ctxconfig->major <= 2) if (ctxconfig->major <= 2)
{ {
if (fbconfig->auxBuffers != GLFW_DONT_CARE) if (fbconfig->auxBuffers != GLFW_DONT_CARE)
ADD_ATTR2(NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers); setAttrib(NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers);
if (fbconfig->accumRedBits != GLFW_DONT_CARE && if (fbconfig->accumRedBits != GLFW_DONT_CARE &&
fbconfig->accumGreenBits != GLFW_DONT_CARE && fbconfig->accumGreenBits != GLFW_DONT_CARE &&
@ -199,7 +207,7 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
fbconfig->accumBlueBits + fbconfig->accumBlueBits +
fbconfig->accumAlphaBits; fbconfig->accumAlphaBits;
ADD_ATTR2(NSOpenGLPFAAccumSize, accumBits); setAttrib(NSOpenGLPFAAccumSize, accumBits);
} }
} }
@ -217,17 +225,17 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
else if (colorBits < 15) else if (colorBits < 15)
colorBits = 15; colorBits = 15;
ADD_ATTR2(NSOpenGLPFAColorSize, colorBits); setAttrib(NSOpenGLPFAColorSize, colorBits);
} }
if (fbconfig->alphaBits != GLFW_DONT_CARE) if (fbconfig->alphaBits != GLFW_DONT_CARE)
ADD_ATTR2(NSOpenGLPFAAlphaSize, fbconfig->alphaBits); setAttrib(NSOpenGLPFAAlphaSize, fbconfig->alphaBits);
if (fbconfig->depthBits != GLFW_DONT_CARE) if (fbconfig->depthBits != GLFW_DONT_CARE)
ADD_ATTR2(NSOpenGLPFADepthSize, fbconfig->depthBits); setAttrib(NSOpenGLPFADepthSize, fbconfig->depthBits);
if (fbconfig->stencilBits != GLFW_DONT_CARE) if (fbconfig->stencilBits != GLFW_DONT_CARE)
ADD_ATTR2(NSOpenGLPFAStencilSize, fbconfig->stencilBits); setAttrib(NSOpenGLPFAStencilSize, fbconfig->stencilBits);
if (fbconfig->stereo) if (fbconfig->stereo)
{ {
@ -236,36 +244,36 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
"NSGL: Stereo rendering is deprecated"); "NSGL: Stereo rendering is deprecated");
return GLFW_FALSE; return GLFW_FALSE;
#else #else
ADD_ATTR(NSOpenGLPFAStereo); addAttrib(NSOpenGLPFAStereo);
#endif #endif
} }
if (fbconfig->doublebuffer) if (fbconfig->doublebuffer)
ADD_ATTR(NSOpenGLPFADoubleBuffer); addAttrib(NSOpenGLPFADoubleBuffer);
if (fbconfig->samples != GLFW_DONT_CARE) if (fbconfig->samples != GLFW_DONT_CARE)
{ {
if (fbconfig->samples == 0) if (fbconfig->samples == 0)
{ {
ADD_ATTR2(NSOpenGLPFASampleBuffers, 0); setAttrib(NSOpenGLPFASampleBuffers, 0);
} }
else else
{ {
ADD_ATTR2(NSOpenGLPFASampleBuffers, 1); setAttrib(NSOpenGLPFASampleBuffers, 1);
ADD_ATTR2(NSOpenGLPFASamples, fbconfig->samples); setAttrib(NSOpenGLPFASamples, fbconfig->samples);
} }
} }
// NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB // NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB
// framebuffer, so there's no need (and no way) to request it // framebuffer, so there's no need (and no way) to request it
ADD_ATTR(0); addAttrib(0);
#undef ADD_ATTR #undef addAttrib
#undef ADD_ATTR2 #undef setAttrib
window->context.nsgl.pixelFormat = window->context.nsgl.pixelFormat =
[[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
if (window->context.nsgl.pixelFormat == nil) if (window->context.nsgl.pixelFormat == nil)
{ {
_glfwInputError(GLFW_FORMAT_UNAVAILABLE, _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
@ -288,6 +296,12 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
return GLFW_FALSE; return GLFW_FALSE;
} }
if (fbconfig->transparent)
{
GLint opaque = 0;
[window->context.nsgl.object setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity];
}
[window->context.nsgl.object setView:window->ns.view]; [window->context.nsgl.object setView:window->ns.view];
window->context.makeCurrent = makeContextCurrentNSGL; window->context.makeCurrent = makeContextCurrentNSGL;

View File

@ -34,9 +34,6 @@
int _glfwPlatformInit(void) int _glfwPlatformInit(void)
{ {
if (!_glfwInitThreadLocalStoragePOSIX())
return GLFW_FALSE;
_glfwInitTimerPOSIX(); _glfwInitTimerPOSIX();
return GLFW_TRUE; return GLFW_TRUE;
} }
@ -44,7 +41,6 @@ int _glfwPlatformInit(void)
void _glfwPlatformTerminate(void) void _glfwPlatformTerminate(void)
{ {
_glfwTerminateOSMesa(); _glfwTerminateOSMesa();
_glfwTerminateThreadLocalStoragePOSIX();
} }
const char* _glfwPlatformGetVersionString(void) const char* _glfwPlatformGetVersionString(void)

View File

@ -31,8 +31,12 @@
////// GLFW platform API ////// ////// GLFW platform API //////
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
int _glfwPlatformPollJoystick(int jid, int mode) int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode)
{ {
return GLFW_FALSE; return GLFW_FALSE;
} }
void _glfwPlatformUpdateGamepadGUID(char* guid)
{
}

View File

@ -24,11 +24,8 @@
// //
//======================================================================== //========================================================================
#ifndef _glfw3_null_joystick_h_
#define _glfw3_null_joystick_h_
#define _GLFW_PLATFORM_JOYSTICK_STATE int nulljs #define _GLFW_PLATFORM_JOYSTICK_STATE int nulljs
#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE int nulljs #define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE int nulljs
#define _GLFW_PLATFORM_MAPPING_NAME ""
#endif // _glfw3_null_joystick_h_

View File

@ -36,6 +36,15 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
{ {
} }
void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
float* xscale, float* yscale)
{
if (xscale)
*xscale = 1.f;
if (yscale)
*yscale = 1.f;
}
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found)
{ {
return NULL; return NULL;

View File

@ -25,9 +25,6 @@
// //
//======================================================================== //========================================================================
#ifndef _glfw3_null_platform_h_
#define _glfw3_null_platform_h_
#include <dlfcn.h> #include <dlfcn.h>
#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNull null #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNull null
@ -42,7 +39,7 @@
#include "osmesa_context.h" #include "osmesa_context.h"
#include "posix_time.h" #include "posix_time.h"
#include "posix_tls.h" #include "posix_thread.h"
#include "null_joystick.h" #include "null_joystick.h"
#if defined(_GLFW_WIN32) #if defined(_GLFW_WIN32)
@ -63,5 +60,3 @@ typedef struct _GLFWwindowNull
int height; int height;
} _GLFWwindowNull; } _GLFWwindowNull;
#endif // _glfw3_null_platform_h_

View File

@ -139,6 +139,15 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
{ {
} }
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
float* xscale, float* yscale)
{
if (xscale)
*xscale = 1.f;
if (yscale)
*yscale = 1.f;
}
void _glfwPlatformIconifyWindow(_GLFWwindow* window) void _glfwPlatformIconifyWindow(_GLFWwindow* window)
{ {
} }
@ -156,6 +165,11 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window)
return GLFW_FALSE; return GLFW_FALSE;
} }
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
{
return GLFW_FALSE;
}
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
{ {
} }
@ -168,10 +182,24 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
{ {
} }
float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
{
return 1.f;
}
void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
{
}
void _glfwPlatformShowWindow(_GLFWwindow* window) void _glfwPlatformShowWindow(_GLFWwindow* window)
{ {
} }
void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
{
}
void _glfwPlatformUnhideWindow(_GLFWwindow* window) void _glfwPlatformUnhideWindow(_GLFWwindow* window)
{ {
} }
@ -247,16 +275,16 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
{ {
} }
void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) void _glfwPlatformSetClipboardString(const char* string)
{ {
} }
const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) const char* _glfwPlatformGetClipboardString(void)
{ {
return NULL; return NULL;
} }
const char* _glfwPlatformGetKeyName(int key, int scancode) const char* _glfwPlatformGetScancodeName(int scancode)
{ {
return ""; return "";
} }

View File

@ -63,7 +63,7 @@ static void makeContextCurrentOSMesa(_GLFWwindow* window)
} }
} }
_glfwPlatformSetCurrentContext(window); _glfwPlatformSetTls(&_glfw.contextSlot, window);
} }
static GLFWglproc getProcAddressOSMesa(const char* procname) static GLFWglproc getProcAddressOSMesa(const char* procname)
@ -113,7 +113,9 @@ GLFWbool _glfwInitOSMesa(void)
int i; int i;
const char* sonames[] = const char* sonames[] =
{ {
#if defined(_WIN32) #if defined(_GLFW_OSMESA_LIBRARY)
_GLFW_OSMESA_LIBRARY,
#elif defined(_WIN32)
"libOSMesa.dll", "libOSMesa.dll",
"OSMesa.dll", "OSMesa.dll",
#elif defined(__APPLE__) #elif defined(__APPLE__)
@ -184,11 +186,11 @@ void _glfwTerminateOSMesa(void)
} }
} }
#define setAttrib(attribName, attribValue) \ #define setAttrib(a, v) \
{ \ { \
attribs[index++] = attribName; \ assert((size_t) (index + 1) < sizeof(attribs) / sizeof(attribs[0])); \
attribs[index++] = attribValue; \ attribs[index++] = a; \
assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ attribs[index++] = v; \
} }
GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window,

View File

@ -25,9 +25,6 @@
// //
//======================================================================== //========================================================================
#ifndef _glfw3_osmesa_context_h_
#define _glfw3_osmesa_context_h_
#define OSMESA_RGBA 0x1908 #define OSMESA_RGBA 0x1908
#define OSMESA_FORMAT 0x22 #define OSMESA_FORMAT 0x22
#define OSMESA_DEPTH_BITS 0x30 #define OSMESA_DEPTH_BITS 0x30
@ -40,7 +37,7 @@
#define OSMESA_CONTEXT_MINOR_VERSION 0x37 #define OSMESA_CONTEXT_MINOR_VERSION 0x37
typedef void* OSMesaContext; typedef void* OSMesaContext;
typedef void (*OSMESAproc)(); typedef void (*OSMESAproc)(void);
typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextExt)(GLenum,GLint,GLint,GLint,OSMesaContext); typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextExt)(GLenum,GLint,GLint,GLint,OSMesaContext);
typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextAttribs)(const int*,OSMesaContext); typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextAttribs)(const int*,OSMesaContext);
@ -95,4 +92,3 @@ GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig, const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig); const _GLFWfbconfig* fbconfig);
#endif // _glfw3_osmesa_context_h_

View File

@ -27,42 +27,77 @@
#include "internal.h" #include "internal.h"
#include <assert.h>
////////////////////////////////////////////////////////////////////////// #include <string.h>
////// GLFW internal API //////
//////////////////////////////////////////////////////////////////////////
GLFWbool _glfwInitThreadLocalStoragePOSIX(void)
{
if (pthread_key_create(&_glfw.posix_tls.context, NULL) != 0)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"POSIX: Failed to create context TLS");
return GLFW_FALSE;
}
_glfw.posix_tls.allocated = GLFW_TRUE;
return GLFW_TRUE;
}
void _glfwTerminateThreadLocalStoragePOSIX(void)
{
if (_glfw.posix_tls.allocated)
pthread_key_delete(_glfw.posix_tls.context);
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
////// GLFW platform API ////// ////// GLFW platform API //////
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
void _glfwPlatformSetCurrentContext(_GLFWwindow* context) GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls)
{ {
pthread_setspecific(_glfw.posix_tls.context, context); assert(tls->posix.allocated == GLFW_FALSE);
if (pthread_key_create(&tls->posix.key, NULL) != 0)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"POSIX: Failed to create context TLS");
return GLFW_FALSE;
}
tls->posix.allocated = GLFW_TRUE;
return GLFW_TRUE;
} }
_GLFWwindow* _glfwPlatformGetCurrentContext(void) void _glfwPlatformDestroyTls(_GLFWtls* tls)
{ {
return pthread_getspecific(_glfw.posix_tls.context); if (tls->posix.allocated)
pthread_key_delete(tls->posix.key);
memset(tls, 0, sizeof(_GLFWtls));
}
void* _glfwPlatformGetTls(_GLFWtls* tls)
{
assert(tls->posix.allocated == GLFW_TRUE);
return pthread_getspecific(tls->posix.key);
}
void _glfwPlatformSetTls(_GLFWtls* tls, void* value)
{
assert(tls->posix.allocated == GLFW_TRUE);
pthread_setspecific(tls->posix.key, value);
}
GLFWbool _glfwPlatformCreateMutex(_GLFWmutex* mutex)
{
assert(mutex->posix.allocated == GLFW_FALSE);
if (pthread_mutex_init(&mutex->posix.handle, NULL) != 0)
{
_glfwInputError(GLFW_PLATFORM_ERROR, "POSIX: Failed to create mutex");
return GLFW_FALSE;
}
return mutex->posix.allocated = GLFW_TRUE;
}
void _glfwPlatformDestroyMutex(_GLFWmutex* mutex)
{
if (mutex->posix.allocated)
pthread_mutex_destroy(&mutex->posix.handle);
memset(mutex, 0, sizeof(_GLFWmutex));
}
void _glfwPlatformLockMutex(_GLFWmutex* mutex)
{
assert(mutex->posix.allocated == GLFW_TRUE);
pthread_mutex_lock(&mutex->posix.handle);
}
void _glfwPlatformUnlockMutex(_GLFWmutex* mutex)
{
assert(mutex->posix.allocated == GLFW_TRUE);
pthread_mutex_unlock(&mutex->posix.handle);
} }

View File

@ -25,25 +25,27 @@
// //
//======================================================================== //========================================================================
#ifndef _glfw3_posix_tls_h_
#define _glfw3_posix_tls_h_
#include <pthread.h> #include <pthread.h>
#define _GLFW_PLATFORM_LIBRARY_TLS_STATE _GLFWtlsPOSIX posix_tls #define _GLFW_PLATFORM_TLS_STATE _GLFWtlsPOSIX posix
#define _GLFW_PLATFORM_MUTEX_STATE _GLFWmutexPOSIX posix
// POSIX-specific global TLS data // POSIX-specific thread local storage data
// //
typedef struct _GLFWtlsPOSIX typedef struct _GLFWtlsPOSIX
{ {
GLFWbool allocated; GLFWbool allocated;
pthread_key_t context; pthread_key_t key;
} _GLFWtlsPOSIX; } _GLFWtlsPOSIX;
// POSIX-specific mutex data
//
typedef struct _GLFWmutexPOSIX
{
GLFWbool allocated;
pthread_mutex_t handle;
GLFWbool _glfwInitThreadLocalStoragePOSIX(void); } _GLFWmutexPOSIX;
void _glfwTerminateThreadLocalStoragePOSIX(void);
#endif // _glfw3_posix_tls_h_

View File

@ -44,14 +44,14 @@ void _glfwInitTimerPOSIX(void)
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
{ {
_glfw.posix_time.monotonic = GLFW_TRUE; _glfw.timer.posix.monotonic = GLFW_TRUE;
_glfw.posix_time.frequency = 1000000000; _glfw.timer.posix.frequency = 1000000000;
} }
else else
#endif #endif
{ {
_glfw.posix_time.monotonic = GLFW_FALSE; _glfw.timer.posix.monotonic = GLFW_FALSE;
_glfw.posix_time.frequency = 1000000; _glfw.timer.posix.frequency = 1000000;
} }
} }
@ -63,7 +63,7 @@ void _glfwInitTimerPOSIX(void)
uint64_t _glfwPlatformGetTimerValue(void) uint64_t _glfwPlatformGetTimerValue(void)
{ {
#if defined(CLOCK_MONOTONIC) #if defined(CLOCK_MONOTONIC)
if (_glfw.posix_time.monotonic) if (_glfw.timer.posix.monotonic)
{ {
struct timespec ts; struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts); clock_gettime(CLOCK_MONOTONIC, &ts);
@ -80,6 +80,6 @@ uint64_t _glfwPlatformGetTimerValue(void)
uint64_t _glfwPlatformGetTimerFrequency(void) uint64_t _glfwPlatformGetTimerFrequency(void)
{ {
return _glfw.posix_time.frequency; return _glfw.timer.posix.frequency;
} }

View File

@ -25,24 +25,20 @@
// //
//======================================================================== //========================================================================
#ifndef _glfw3_posix_time_h_ #define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerPOSIX posix
#define _glfw3_posix_time_h_
#define _GLFW_PLATFORM_LIBRARY_TIME_STATE _GLFWtimePOSIX posix_time
#include <stdint.h> #include <stdint.h>
// POSIX-specific global timer data // POSIX-specific global timer data
// //
typedef struct _GLFWtimePOSIX typedef struct _GLFWtimerPOSIX
{ {
GLFWbool monotonic; GLFWbool monotonic;
uint64_t frequency; uint64_t frequency;
} _GLFWtimePOSIX; } _GLFWtimerPOSIX;
void _glfwInitTimerPOSIX(void); void _glfwInitTimerPOSIX(void);
#endif // _glfw3_posix_time_h_

View File

@ -49,11 +49,12 @@ GLFWbool _glfwInitVulkan(int mode)
return GLFW_TRUE; return GLFW_TRUE;
#if !defined(_GLFW_VULKAN_STATIC) #if !defined(_GLFW_VULKAN_STATIC)
#if defined(_GLFW_WIN32) #if defined(_GLFW_VULKAN_LIBRARY)
_glfw.vk.handle = _glfw_dlopen(_GLFW_VULKAN_LIBRARY);
#elif defined(_GLFW_WIN32)
_glfw.vk.handle = _glfw_dlopen("vulkan-1.dll"); _glfw.vk.handle = _glfw_dlopen("vulkan-1.dll");
#elif defined(_GLFW_COCOA) #elif defined(_GLFW_COCOA)
// NULL maps to RTLD_DEFAULT, which searches all loaded binaries _glfw.vk.handle = _glfw_dlopen("libMoltenVK.dylib");
_glfw.vk.handle = _glfw_dlopen(NULL);
#else #else
_glfw.vk.handle = _glfw_dlopen("libvulkan.so.1"); _glfw.vk.handle = _glfw_dlopen("libvulkan.so.1");
#endif #endif
@ -69,16 +70,8 @@ GLFWbool _glfwInitVulkan(int mode)
_glfw_dlsym(_glfw.vk.handle, "vkGetInstanceProcAddr"); _glfw_dlsym(_glfw.vk.handle, "vkGetInstanceProcAddr");
if (!_glfw.vk.GetInstanceProcAddr) if (!_glfw.vk.GetInstanceProcAddr)
{ {
#if defined(_GLFW_COCOA)
if (mode == _GLFW_REQUIRE_LOADER)
{
_glfwInputError(GLFW_API_UNAVAILABLE,
"Vulkan: vkGetInstanceProcAddr not found in process");
}
#else
_glfwInputError(GLFW_API_UNAVAILABLE, _glfwInputError(GLFW_API_UNAVAILABLE,
"Vulkan: Loader does not export vkGetInstanceProcAddr"); "Vulkan: Loader does not export vkGetInstanceProcAddr");
#endif
_glfwTerminateVulkan(); _glfwTerminateVulkan();
return GLFW_FALSE; return GLFW_FALSE;

View File

@ -54,7 +54,9 @@ static int getPixelFormatAttrib(_GLFWwindow* window, int pixelFormat, int attrib
// Return a list of available and usable framebuffer configs // Return a list of available and usable framebuffer configs
// //
static int choosePixelFormat(_GLFWwindow* window, const _GLFWfbconfig* desired) static int choosePixelFormat(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig)
{ {
_GLFWfbconfig* usableConfigs; _GLFWfbconfig* usableConfigs;
const _GLFWfbconfig* closest; const _GLFWfbconfig* closest;
@ -127,6 +129,8 @@ static int choosePixelFormat(_GLFWwindow* window, const _GLFWfbconfig* desired)
if (_glfw.wgl.ARB_multisample) if (_glfw.wgl.ARB_multisample)
u->samples = getPixelFormatAttrib(window, n, WGL_SAMPLES_ARB); u->samples = getPixelFormatAttrib(window, n, WGL_SAMPLES_ARB);
if (ctxconfig->client == GLFW_OPENGL_API)
{
if (_glfw.wgl.ARB_framebuffer_sRGB || if (_glfw.wgl.ARB_framebuffer_sRGB ||
_glfw.wgl.EXT_framebuffer_sRGB) _glfw.wgl.EXT_framebuffer_sRGB)
{ {
@ -136,10 +140,22 @@ static int choosePixelFormat(_GLFWwindow* window, const _GLFWfbconfig* desired)
} }
else else
{ {
PIXELFORMATDESCRIPTOR pfd; if (_glfw.wgl.EXT_colorspace)
{
if (getPixelFormatAttrib(window, n, WGL_COLORSPACE_EXT) ==
WGL_COLORSPACE_SRGB_EXT)
{
u->sRGB = GLFW_TRUE;
}
}
}
}
else
{
// Get pixel format attributes through legacy PFDs // Get pixel format attributes through legacy PFDs
PIXELFORMATDESCRIPTOR pfd;
if (!DescribePixelFormat(window->context.wgl.dc, if (!DescribePixelFormat(window->context.wgl.dc,
n, n,
sizeof(PIXELFORMATDESCRIPTOR), sizeof(PIXELFORMATDESCRIPTOR),
@ -197,7 +213,7 @@ static int choosePixelFormat(_GLFWwindow* window, const _GLFWfbconfig* desired)
return 0; return 0;
} }
closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); closest = _glfwChooseFBConfig(fbconfig, usableConfigs, usableCount);
if (!closest) if (!closest)
{ {
_glfwInputError(GLFW_FORMAT_UNAVAILABLE, _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
@ -213,32 +229,17 @@ static int choosePixelFormat(_GLFWwindow* window, const _GLFWfbconfig* desired)
return pixelFormat; return pixelFormat;
} }
// Returns whether desktop compositing is enabled
//
static GLFWbool isCompositionEnabled(void)
{
BOOL enabled;
if (!_glfw_DwmIsCompositionEnabled)
return FALSE;
if (_glfw_DwmIsCompositionEnabled(&enabled) != S_OK)
return FALSE;
return enabled;
}
static void makeContextCurrentWGL(_GLFWwindow* window) static void makeContextCurrentWGL(_GLFWwindow* window)
{ {
if (window) if (window)
{ {
if (wglMakeCurrent(window->context.wgl.dc, window->context.wgl.handle)) if (wglMakeCurrent(window->context.wgl.dc, window->context.wgl.handle))
_glfwPlatformSetCurrentContext(window); _glfwPlatformSetTls(&_glfw.contextSlot, window);
else else
{ {
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR, _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"WGL: Failed to make context current"); "WGL: Failed to make context current");
_glfwPlatformSetCurrentContext(NULL); _glfwPlatformSetTls(&_glfw.contextSlot, NULL);
} }
} }
else else
@ -249,18 +250,18 @@ static void makeContextCurrentWGL(_GLFWwindow* window)
"WGL: Failed to clear current context"); "WGL: Failed to clear current context");
} }
_glfwPlatformSetCurrentContext(NULL); _glfwPlatformSetTls(&_glfw.contextSlot, NULL);
} }
} }
static void swapBuffersWGL(_GLFWwindow* window) static void swapBuffersWGL(_GLFWwindow* window)
{ {
// HACK: Use DwmFlush when desktop composition is enabled // HACK: Use DwmFlush when desktop composition is enabled
if (isCompositionEnabled() && !window->monitor) if (_glfwIsCompositionEnabledWin32() && !window->monitor)
{ {
int count = abs(window->context.wgl.interval); int count = abs(window->context.wgl.interval);
while (count--) while (count--)
_glfw_DwmFlush(); DwmFlush();
} }
SwapBuffers(window->context.wgl.dc); SwapBuffers(window->context.wgl.dc);
@ -268,13 +269,13 @@ static void swapBuffersWGL(_GLFWwindow* window)
static void swapIntervalWGL(int interval) static void swapIntervalWGL(int interval)
{ {
_GLFWwindow* window = _glfwPlatformGetCurrentContext(); _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot);
window->context.wgl.interval = interval; window->context.wgl.interval = interval;
// HACK: Disable WGL swap interval when desktop composition is enabled to // HACK: Disable WGL swap interval when desktop composition is enabled to
// avoid interfering with DWM vsync // avoid interfering with DWM vsync
if (isCompositionEnabled() && !window->monitor) if (_glfwIsCompositionEnabledWin32() && !window->monitor)
interval = 0; interval = 0;
if (_glfw.wgl.EXT_swap_control) if (_glfw.wgl.EXT_swap_control)
@ -402,8 +403,12 @@ static void loadWGLExtensions(void)
extensionSupportedWGL("WGL_EXT_create_context_es2_profile"); extensionSupportedWGL("WGL_EXT_create_context_es2_profile");
_glfw.wgl.ARB_create_context_robustness = _glfw.wgl.ARB_create_context_robustness =
extensionSupportedWGL("WGL_ARB_create_context_robustness"); extensionSupportedWGL("WGL_ARB_create_context_robustness");
_glfw.wgl.ARB_create_context_no_error =
extensionSupportedWGL("WGL_ARB_create_context_no_error");
_glfw.wgl.EXT_swap_control = _glfw.wgl.EXT_swap_control =
extensionSupportedWGL("WGL_EXT_swap_control"); extensionSupportedWGL("WGL_EXT_swap_control");
_glfw.wgl.EXT_colorspace =
extensionSupportedWGL("WGL_EXT_colorspace");
_glfw.wgl.ARB_pixel_format = _glfw.wgl.ARB_pixel_format =
extensionSupportedWGL("WGL_ARB_pixel_format"); extensionSupportedWGL("WGL_ARB_pixel_format");
_glfw.wgl.ARB_context_flush_control = _glfw.wgl.ARB_context_flush_control =
@ -457,11 +462,11 @@ void _glfwTerminateWGL(void)
FreeLibrary(_glfw.wgl.instance); FreeLibrary(_glfw.wgl.instance);
} }
#define setWGLattrib(attribName, attribValue) \ #define setAttrib(a, v) \
{ \ { \
attribs[index++] = attribName; \ assert((size_t) (index + 1) < sizeof(attribs) / sizeof(attribs[0])); \
attribs[index++] = attribValue; \ attribs[index++] = a; \
assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ attribs[index++] = v; \
} }
// Create the OpenGL or OpenGL ES context // Create the OpenGL or OpenGL ES context
@ -489,7 +494,7 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window,
return GLFW_FALSE; return GLFW_FALSE;
} }
pixelFormat = choosePixelFormat(window, fbconfig); pixelFormat = choosePixelFormat(window, ctxconfig, fbconfig);
if (!pixelFormat) if (!pixelFormat)
return GLFW_FALSE; return GLFW_FALSE;
@ -561,8 +566,6 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window,
if (ctxconfig->debug) if (ctxconfig->debug)
flags |= WGL_CONTEXT_DEBUG_BIT_ARB; flags |= WGL_CONTEXT_DEBUG_BIT_ARB;
if (ctxconfig->noerror)
flags |= GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR;
if (ctxconfig->robustness) if (ctxconfig->robustness)
{ {
@ -570,12 +573,12 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window,
{ {
if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION)
{ {
setWGLattrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, setAttrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
WGL_NO_RESET_NOTIFICATION_ARB); WGL_NO_RESET_NOTIFICATION_ARB);
} }
else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET)
{ {
setWGLattrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, setAttrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
WGL_LOSE_CONTEXT_ON_RESET_ARB); WGL_LOSE_CONTEXT_ON_RESET_ARB);
} }
@ -589,33 +592,39 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window,
{ {
if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE)
{ {
setWGLattrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, setAttrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB,
WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB);
} }
else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH)
{ {
setWGLattrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, setAttrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB,
WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB);
} }
} }
} }
if (ctxconfig->noerror)
{
if (_glfw.wgl.ARB_create_context_no_error)
setAttrib(WGL_CONTEXT_OPENGL_NO_ERROR_ARB, GLFW_TRUE);
}
// NOTE: Only request an explicitly versioned context when necessary, as // NOTE: Only request an explicitly versioned context when necessary, as
// explicitly requesting version 1.0 does not always return the // explicitly requesting version 1.0 does not always return the
// highest version supported by the driver // highest version supported by the driver
if (ctxconfig->major != 1 || ctxconfig->minor != 0) if (ctxconfig->major != 1 || ctxconfig->minor != 0)
{ {
setWGLattrib(WGL_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); setAttrib(WGL_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major);
setWGLattrib(WGL_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); setAttrib(WGL_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor);
} }
if (flags) if (flags)
setWGLattrib(WGL_CONTEXT_FLAGS_ARB, flags); setAttrib(WGL_CONTEXT_FLAGS_ARB, flags);
if (mask) if (mask)
setWGLattrib(WGL_CONTEXT_PROFILE_MASK_ARB, mask); setAttrib(WGL_CONTEXT_PROFILE_MASK_ARB, mask);
setWGLattrib(0, 0); setAttrib(0, 0);
window->context.wgl.handle = window->context.wgl.handle =
_glfw.wgl.CreateContextAttribsARB(window->context.wgl.dc, _glfw.wgl.CreateContextAttribsARB(window->context.wgl.dc,
@ -646,6 +655,11 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window,
_glfwInputError(GLFW_VERSION_UNAVAILABLE, _glfwInputError(GLFW_VERSION_UNAVAILABLE,
"WGL: Driver does not support the requested OpenGL profile"); "WGL: Driver does not support the requested OpenGL profile");
} }
else if (error == (0xc0070000 | ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB))
{
_glfwInputError(GLFW_INVALID_VALUE,
"WGL: The share context is not compatible with the requested context");
}
else else
{ {
if (ctxconfig->client == GLFW_OPENGL_API) if (ctxconfig->client == GLFW_OPENGL_API)
@ -694,7 +708,7 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window,
return GLFW_TRUE; return GLFW_TRUE;
} }
#undef setWGLattrib #undef setAttrib
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////

View File

@ -25,10 +25,6 @@
// //
//======================================================================== //========================================================================
#ifndef _glfw3_wgl_context_h_
#define _glfw3_wgl_context_h_
#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 #define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
#define WGL_SUPPORT_OPENGL_ARB 0x2010 #define WGL_SUPPORT_OPENGL_ARB 0x2010
#define WGL_DRAW_TO_WINDOW_ARB 0x2001 #define WGL_DRAW_TO_WINDOW_ARB 0x2001
@ -72,9 +68,13 @@
#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 #define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097
#define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 #define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0
#define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 #define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098
#define WGL_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3
#define WGL_COLORSPACE_EXT 0x309d
#define WGL_COLORSPACE_SRGB_EXT 0x3089
#define ERROR_INVALID_VERSION_ARB 0x2095 #define ERROR_INVALID_VERSION_ARB 0x2095
#define ERROR_INVALID_PROFILE_ARB 0x2096 #define ERROR_INVALID_PROFILE_ARB 0x2096
#define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054
typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int); typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int);
typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC,int,int,UINT,const int*,int*); typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC,int,int,UINT,const int*,int*);
@ -135,6 +135,7 @@ typedef struct _GLFWlibraryWGL
PFNWGLGETEXTENSIONSSTRINGARBPROC GetExtensionsStringARB; PFNWGLGETEXTENSIONSSTRINGARBPROC GetExtensionsStringARB;
PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB;
GLFWbool EXT_swap_control; GLFWbool EXT_swap_control;
GLFWbool EXT_colorspace;
GLFWbool ARB_multisample; GLFWbool ARB_multisample;
GLFWbool ARB_framebuffer_sRGB; GLFWbool ARB_framebuffer_sRGB;
GLFWbool EXT_framebuffer_sRGB; GLFWbool EXT_framebuffer_sRGB;
@ -143,6 +144,7 @@ typedef struct _GLFWlibraryWGL
GLFWbool ARB_create_context_profile; GLFWbool ARB_create_context_profile;
GLFWbool EXT_create_context_es2_profile; GLFWbool EXT_create_context_es2_profile;
GLFWbool ARB_create_context_robustness; GLFWbool ARB_create_context_robustness;
GLFWbool ARB_create_context_no_error;
GLFWbool ARB_context_flush_control; GLFWbool ARB_context_flush_control;
} _GLFWlibraryWGL; } _GLFWlibraryWGL;
@ -154,4 +156,3 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig, const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig); const _GLFWfbconfig* fbconfig);
#endif // _glfw3_wgl_context_h_

View File

@ -30,8 +30,10 @@
#include <stdlib.h> #include <stdlib.h>
#include <malloc.h> #include <malloc.h>
#include <initguid.h> static const GUID _glfw_GUID_DEVINTERFACE_HID =
DEFINE_GUID(GUID_DEVINTERFACE_HID,0x4d1e55b2,0xf16f,0x11cf,0x88,0xcb,0x00,0x11,0x11,0x00,0x00,0x30); {0x4d1e55b2,0xf16f,0x11cf,{0x88,0xcb,0x00,0x11,0x11,0x00,0x00,0x30}};
#define GUID_DEVINTERFACE_HID _glfw_GUID_DEVINTERFACE_HID
#if defined(_GLFW_USE_HYBRID_HPG) || defined(_GLFW_USE_OPTIMUS_HPG) #if defined(_GLFW_USE_HYBRID_HPG) || defined(_GLFW_USE_OPTIMUS_HPG)
@ -60,6 +62,17 @@ BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
#endif // _GLFW_BUILD_DLL #endif // _GLFW_BUILD_DLL
// HACK: Define versionhelpers.h functions manually as MinGW lacks the header
BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp)
{
OSVERSIONINFOEXW osvi = { sizeof(osvi), major, minor, 0, 0, {0}, sp };
DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR;
ULONGLONG cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL);
cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL);
cond = VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
return VerifyVersionInfoW(&osvi, mask, cond);
}
// Load necessary libraries (DLLs) // Load necessary libraries (DLLs)
// //
static GLFWbool loadLibraries(void) static GLFWbool loadLibraries(void)
@ -67,7 +80,8 @@ static GLFWbool loadLibraries(void)
_glfw.win32.winmm.instance = LoadLibraryA("winmm.dll"); _glfw.win32.winmm.instance = LoadLibraryA("winmm.dll");
if (!_glfw.win32.winmm.instance) if (!_glfw.win32.winmm.instance)
{ {
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to load winmm.dll"); _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"Win32: Failed to load winmm.dll");
return GLFW_FALSE; return GLFW_FALSE;
} }
@ -77,13 +91,14 @@ static GLFWbool loadLibraries(void)
_glfw.win32.user32.instance = LoadLibraryA("user32.dll"); _glfw.win32.user32.instance = LoadLibraryA("user32.dll");
if (!_glfw.win32.user32.instance) if (!_glfw.win32.user32.instance)
{ {
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to load user32.dll"); _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"Win32: Failed to load user32.dll");
return GLFW_FALSE; return GLFW_FALSE;
} }
_glfw.win32.user32.SetProcessDPIAware = (PFN_SetProcessDPIAware) _glfw.win32.user32.SetProcessDPIAware_ = (PFN_SetProcessDPIAware)
GetProcAddress(_glfw.win32.user32.instance, "SetProcessDPIAware"); GetProcAddress(_glfw.win32.user32.instance, "SetProcessDPIAware");
_glfw.win32.user32.ChangeWindowMessageFilterEx = (PFN_ChangeWindowMessageFilterEx) _glfw.win32.user32.ChangeWindowMessageFilterEx_ = (PFN_ChangeWindowMessageFilterEx)
GetProcAddress(_glfw.win32.user32.instance, "ChangeWindowMessageFilterEx"); GetProcAddress(_glfw.win32.user32.instance, "ChangeWindowMessageFilterEx");
_glfw.win32.dinput8.instance = LoadLibraryA("dinput8.dll"); _glfw.win32.dinput8.instance = LoadLibraryA("dinput8.dll");
@ -123,17 +138,21 @@ static GLFWbool loadLibraries(void)
_glfw.win32.dwmapi.instance = LoadLibraryA("dwmapi.dll"); _glfw.win32.dwmapi.instance = LoadLibraryA("dwmapi.dll");
if (_glfw.win32.dwmapi.instance) if (_glfw.win32.dwmapi.instance)
{ {
_glfw.win32.dwmapi.DwmIsCompositionEnabled = (PFN_DwmIsCompositionEnabled) _glfw.win32.dwmapi.IsCompositionEnabled = (PFN_DwmIsCompositionEnabled)
GetProcAddress(_glfw.win32.dwmapi.instance, "DwmIsCompositionEnabled"); GetProcAddress(_glfw.win32.dwmapi.instance, "DwmIsCompositionEnabled");
_glfw.win32.dwmapi.DwmFlush = (PFN_DwmFlush) _glfw.win32.dwmapi.Flush = (PFN_DwmFlush)
GetProcAddress(_glfw.win32.dwmapi.instance, "DwmFlush"); GetProcAddress(_glfw.win32.dwmapi.instance, "DwmFlush");
_glfw.win32.dwmapi.EnableBlurBehindWindow = (PFN_DwmEnableBlurBehindWindow)
GetProcAddress(_glfw.win32.dwmapi.instance, "DwmEnableBlurBehindWindow");
} }
_glfw.win32.shcore.instance = LoadLibraryA("shcore.dll"); _glfw.win32.shcore.instance = LoadLibraryA("shcore.dll");
if (_glfw.win32.shcore.instance) if (_glfw.win32.shcore.instance)
{ {
_glfw.win32.shcore.SetProcessDpiAwareness = (PFN_SetProcessDpiAwareness) _glfw.win32.shcore.SetProcessDpiAwareness_ = (PFN_SetProcessDpiAwareness)
GetProcAddress(_glfw.win32.shcore.instance, "SetProcessDpiAwareness"); GetProcAddress(_glfw.win32.shcore.instance, "SetProcessDpiAwareness");
_glfw.win32.shcore.GetDpiForMonitor_ = (PFN_GetDpiForMonitor)
GetProcAddress(_glfw.win32.shcore.instance, "GetDpiForMonitor");
} }
return GLFW_TRUE; return GLFW_TRUE;
@ -356,19 +375,19 @@ static HWND createHelperWindow(void)
WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source) WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source)
{ {
WCHAR* target; WCHAR* target;
int length; int count;
length = MultiByteToWideChar(CP_UTF8, 0, source, -1, NULL, 0); count = MultiByteToWideChar(CP_UTF8, 0, source, -1, NULL, 0);
if (!length) if (!count)
{ {
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR, _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"Win32: Failed to convert string from UTF-8"); "Win32: Failed to convert string from UTF-8");
return NULL; return NULL;
} }
target = calloc(length, sizeof(WCHAR)); target = calloc(count, sizeof(WCHAR));
if (!MultiByteToWideChar(CP_UTF8, 0, source, -1, target, length)) if (!MultiByteToWideChar(CP_UTF8, 0, source, -1, target, count))
{ {
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR, _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"Win32: Failed to convert string from UTF-8"); "Win32: Failed to convert string from UTF-8");
@ -384,19 +403,19 @@ WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source)
char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source) char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source)
{ {
char* target; char* target;
int length; int size;
length = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL); size = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL);
if (!length) if (!size)
{ {
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR, _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"Win32: Failed to convert string to UTF-8"); "Win32: Failed to convert string to UTF-8");
return NULL; return NULL;
} }
target = calloc(length, 1); target = calloc(size, 1);
if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, length, NULL, NULL)) if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, size, NULL, NULL))
{ {
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR, _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"Win32: Failed to convert string to UTF-8"); "Win32: Failed to convert string to UTF-8");
@ -411,8 +430,8 @@ char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source)
// //
void _glfwInputErrorWin32(int error, const char* description) void _glfwInputErrorWin32(int error, const char* description)
{ {
WCHAR buffer[1024] = L""; WCHAR buffer[_GLFW_MESSAGE_SIZE] = L"";
char message[2048] = ""; char message[_GLFW_MESSAGE_SIZE] = "";
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_IGNORE_INSERTS |
@ -428,6 +447,60 @@ void _glfwInputErrorWin32(int error, const char* description)
_glfwInputError(error, "%s: %s", description, message); _glfwInputError(error, "%s: %s", description, message);
} }
// Updates key names according to the current keyboard layout
//
void _glfwUpdateKeyNamesWin32(void)
{
int key;
BYTE state[256] = {0};
memset(_glfw.win32.keynames, 0, sizeof(_glfw.win32.keynames));
for (key = GLFW_KEY_SPACE; key <= GLFW_KEY_LAST; key++)
{
UINT vk;
int scancode, length;
WCHAR chars[16];
scancode = _glfw.win32.scancodes[key];
if (scancode == -1)
continue;
if (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_ADD)
{
const UINT vks[] = {
VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3,
VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7,
VK_NUMPAD8, VK_NUMPAD9, VK_DECIMAL, VK_DIVIDE,
VK_MULTIPLY, VK_SUBTRACT, VK_ADD
};
vk = vks[key - GLFW_KEY_KP_0];
}
else
vk = MapVirtualKey(scancode, MAPVK_VSC_TO_VK);
length = ToUnicode(vk, scancode, state,
chars, sizeof(chars) / sizeof(WCHAR),
0);
if (length == -1)
{
length = ToUnicode(vk, scancode, state,
chars, sizeof(chars) / sizeof(WCHAR),
0);
}
if (length < 1)
continue;
WideCharToMultiByte(CP_UTF8, 0, chars, 1,
_glfw.win32.keynames[key],
sizeof(_glfw.win32.keynames[key]),
NULL, NULL);
}
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
////// GLFW platform API ////// ////// GLFW platform API //////
@ -435,9 +508,6 @@ void _glfwInputErrorWin32(int error, const char* description)
int _glfwPlatformInit(void) int _glfwPlatformInit(void)
{ {
if (!_glfwInitThreadLocalStorageWin32())
return GLFW_FALSE;
// To make SetForegroundWindow work as we want, we need to fiddle // To make SetForegroundWindow work as we want, we need to fiddle
// with the FOREGROUNDLOCKTIMEOUT system setting (we do this as early // with the FOREGROUNDLOCKTIMEOUT system setting (we do this as early
// as possible in the hope of still being the foreground process) // as possible in the hope of still being the foreground process)
@ -450,11 +520,12 @@ int _glfwPlatformInit(void)
return GLFW_FALSE; return GLFW_FALSE;
createKeyTables(); createKeyTables();
_glfwUpdateKeyNamesWin32();
if (_glfw_SetProcessDpiAwareness) if (IsWindows8Point1OrGreater())
_glfw_SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
else if (_glfw_SetProcessDPIAware) else if (IsWindowsVistaOrGreater())
_glfw_SetProcessDPIAware(); SetProcessDPIAware();
if (!_glfwRegisterWindowClassWin32()) if (!_glfwRegisterWindowClassWin32())
return GLFW_FALSE; return GLFW_FALSE;
@ -489,7 +560,6 @@ void _glfwPlatformTerminate(void)
_glfwTerminateEGL(); _glfwTerminateEGL();
_glfwTerminateJoysticksWin32(); _glfwTerminateJoysticksWin32();
_glfwTerminateThreadLocalStorageWin32();
freeLibraries(); freeLibraries();
} }

View File

@ -27,10 +27,9 @@
#include "internal.h" #include "internal.h"
#include <stdio.h>
#include <math.h> #include <math.h>
#include <initguid.h>
#define _GLFW_TYPE_AXIS 0 #define _GLFW_TYPE_AXIS 0
#define _GLFW_TYPE_SLIDER 1 #define _GLFW_TYPE_SLIDER 1
#define _GLFW_TYPE_BUTTON 2 #define _GLFW_TYPE_BUTTON 2
@ -49,18 +48,36 @@ typedef struct _GLFWobjenumWin32
int povCount; int povCount;
} _GLFWobjenumWin32; } _GLFWobjenumWin32;
// Define only the necessary GUIDs (it's bad enough that we're exporting these) // Define local copies of the necessary GUIDs
// //
DEFINE_GUID(IID_IDirectInput8W,0xbf798031,0x483a,0x4da2,0xaa,0x99,0x5d,0x64,0xed,0x36,0x97,0x00); static const GUID _glfw_IID_IDirectInput8W =
DEFINE_GUID(GUID_XAxis,0xa36d02e0,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); {0xbf798031,0x483a,0x4da2,{0xaa,0x99,0x5d,0x64,0xed,0x36,0x97,0x00}};
DEFINE_GUID(GUID_YAxis,0xa36d02e1,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); static const GUID _glfw_GUID_XAxis =
DEFINE_GUID(GUID_ZAxis,0xa36d02e2,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); {0xa36d02e0,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); static const GUID _glfw_GUID_YAxis =
DEFINE_GUID(GUID_RyAxis,0xa36d02f5,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); {0xa36d02e1,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); static const GUID _glfw_GUID_ZAxis =
DEFINE_GUID(GUID_Slider,0xa36d02e4,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); {0xa36d02e2,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); static const GUID _glfw_GUID_RxAxis =
DEFINE_GUID(GUID_POV,0xa36d02f2,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); {0xa36d02f4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}};
static const GUID _glfw_GUID_RyAxis =
{0xa36d02f5,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}};
static const GUID _glfw_GUID_RzAxis =
{0xa36d02e3,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}};
static const GUID _glfw_GUID_Slider =
{0xa36d02e4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}};
static const GUID _glfw_GUID_POV =
{0xa36d02f2,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}};
#define IID_IDirectInput8W _glfw_IID_IDirectInput8W
#define GUID_XAxis _glfw_GUID_XAxis
#define GUID_YAxis _glfw_GUID_YAxis
#define GUID_ZAxis _glfw_GUID_ZAxis
#define GUID_RxAxis _glfw_GUID_RxAxis
#define GUID_RyAxis _glfw_GUID_RyAxis
#define GUID_RzAxis _glfw_GUID_RzAxis
#define GUID_Slider _glfw_GUID_Slider
#define GUID_POV _glfw_GUID_POV
// Object data array for our clone of c_dfDIJoystick // Object data array for our clone of c_dfDIJoystick
// Generated with https://github.com/elmindreda/c_dfDIJoystick2 // Generated with https://github.com/elmindreda/c_dfDIJoystick2
@ -146,9 +163,9 @@ static const char* getDeviceDescription(const XINPUT_CAPABILITIES* xic)
case XINPUT_DEVSUBTYPE_GAMEPAD: case XINPUT_DEVSUBTYPE_GAMEPAD:
{ {
if (xic->Flags & XINPUT_CAPS_WIRELESS) if (xic->Flags & XINPUT_CAPS_WIRELESS)
return "Wireless Xbox 360 Controller"; return "Wireless Xbox Controller";
else else
return "Xbox 360 Controller"; return "Xbox Controller";
} }
} }
@ -244,11 +261,11 @@ static void closeJoystick(_GLFWjoystick* js)
} }
_glfwFreeJoystick(js); _glfwFreeJoystick(js);
_glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_DISCONNECTED); _glfwInputJoystick(js, GLFW_DISCONNECTED);
} }
// DirectInput device object enumeration callback // DirectInput device object enumeration callback
// Insights gleaned from SDL2 // Insights gleaned from SDL
// //
static BOOL CALLBACK deviceObjectCallback(const DIDEVICEOBJECTINSTANCEW* doi, static BOOL CALLBACK deviceObjectCallback(const DIDEVICEOBJECTINSTANCEW* doi,
void* user) void* user)
@ -330,15 +347,18 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user)
IDirectInputDevice8* device; IDirectInputDevice8* device;
_GLFWobjenumWin32 data; _GLFWobjenumWin32 data;
_GLFWjoystick* js; _GLFWjoystick* js;
char guid[33];
char name[256]; char name[256];
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{ {
if (!_glfw.joysticks[jid].present) _GLFWjoystick* js = _glfw.joysticks + jid;
continue; if (js->present)
if (memcmp(&_glfw.joysticks[jid].win32.guid, &di->guidInstance, sizeof(GUID)) == 0) {
if (memcmp(&js->win32.guid, &di->guidInstance, sizeof(GUID)) == 0)
return DIENUM_CONTINUE; return DIENUM_CONTINUE;
} }
}
if (supportsXInput(&di->guidProduct)) if (supportsXInput(&di->guidProduct))
return DIENUM_CONTINUE; return DIENUM_CONTINUE;
@ -425,7 +445,24 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user)
return DIENUM_STOP; return DIENUM_STOP;
} }
js = _glfwAllocJoystick(name, // Generate a joystick GUID that matches the SDL 2.0.5+ one
if (memcmp(&di->guidProduct.Data4[2], "PIDVID", 6) == 0)
{
sprintf(guid, "03000000%02x%02x0000%02x%02x000000000000",
(uint8_t) di->guidProduct.Data1,
(uint8_t) (di->guidProduct.Data1 >> 8),
(uint8_t) (di->guidProduct.Data1 >> 16),
(uint8_t) (di->guidProduct.Data1 >> 24));
}
else
{
sprintf(guid, "05000000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00",
name[0], name[1], name[2], name[3],
name[4], name[5], name[6], name[7],
name[8], name[9], name[10]);
}
js = _glfwAllocJoystick(name, guid,
data.axisCount + data.sliderCount, data.axisCount + data.sliderCount,
data.buttonCount, data.buttonCount,
data.povCount); data.povCount);
@ -441,7 +478,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user)
js->win32.objects = data.objects; js->win32.objects = data.objects;
js->win32.objectCount = data.objectCount; js->win32.objectCount = data.objectCount;
_glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_CONNECTED); _glfwInputJoystick(js, GLFW_CONNECTED);
return DIENUM_CONTINUE; return DIENUM_CONTINUE;
} }
@ -494,6 +531,7 @@ void _glfwDetectJoystickConnectionWin32(void)
for (index = 0; index < XUSER_MAX_COUNT; index++) for (index = 0; index < XUSER_MAX_COUNT; index++)
{ {
int jid; int jid;
char guid[33];
XINPUT_CAPABILITIES xic; XINPUT_CAPABILITIES xic;
_GLFWjoystick* js; _GLFWjoystick* js;
@ -513,13 +551,17 @@ void _glfwDetectJoystickConnectionWin32(void)
if (XInputGetCapabilities(index, 0, &xic) != ERROR_SUCCESS) if (XInputGetCapabilities(index, 0, &xic) != ERROR_SUCCESS)
continue; continue;
js = _glfwAllocJoystick(getDeviceDescription(&xic), 6, 10, 1); // Generate a joystick GUID that matches the SDL 2.0.5+ one
sprintf(guid, "78696e707574%02x000000000000000000",
xic.SubType & 0xff);
js = _glfwAllocJoystick(getDeviceDescription(&xic), guid, 6, 10, 1);
if (!js) if (!js)
continue; continue;
js->win32.index = index; js->win32.index = index;
_glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_CONNECTED); _glfwInputJoystick(js, GLFW_CONNECTED);
} }
} }
@ -546,8 +588,9 @@ void _glfwDetectJoystickDisconnectionWin32(void)
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{ {
if (_glfw.joysticks[jid].present) _GLFWjoystick* js = _glfw.joysticks + jid;
_glfwPlatformPollJoystick(jid, _GLFW_POLL_PRESENCE); if (js->present)
_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE);
} }
} }
@ -556,10 +599,8 @@ void _glfwDetectJoystickDisconnectionWin32(void)
////// GLFW platform API ////// ////// GLFW platform API //////
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
int _glfwPlatformPollJoystick(int jid, int mode) int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode)
{ {
_GLFWjoystick* js = _glfw.joysticks + jid;
if (js->win32.device) if (js->win32.device)
{ {
int i, ai = 0, bi = 0, pi = 0; int i, ai = 0, bi = 0, pi = 0;
@ -598,7 +639,7 @@ int _glfwPlatformPollJoystick(int jid, int mode)
case _GLFW_TYPE_SLIDER: case _GLFW_TYPE_SLIDER:
{ {
const float value = (*((LONG*) data) + 0.5f) / 32767.5f; const float value = (*((LONG*) data) + 0.5f) / 32767.5f;
_glfwInputJoystickAxis(jid, ai, value); _glfwInputJoystickAxis(js, ai, value);
ai++; ai++;
break; break;
} }
@ -606,7 +647,7 @@ int _glfwPlatformPollJoystick(int jid, int mode)
case _GLFW_TYPE_BUTTON: case _GLFW_TYPE_BUTTON:
{ {
const char value = (*((BYTE*) data) & 0x80) != 0; const char value = (*((BYTE*) data) & 0x80) != 0;
_glfwInputJoystickButton(jid, bi, value); _glfwInputJoystickButton(js, bi, value);
bi++; bi++;
break; break;
} }
@ -631,7 +672,7 @@ int _glfwPlatformPollJoystick(int jid, int mode)
if (state < 0 || state > 8) if (state < 0 || state > 8)
state = 8; state = 8;
_glfwInputJoystickHat(jid, pi, states[state]); _glfwInputJoystickHat(js, pi, states[state]);
pi++; pi++;
break; break;
} }
@ -643,8 +684,7 @@ int _glfwPlatformPollJoystick(int jid, int mode)
int i, dpad = 0; int i, dpad = 0;
DWORD result; DWORD result;
XINPUT_STATE xis; XINPUT_STATE xis;
float axes[6] = { 0.f, 0.f, 0.f, 0.f, -1.f, -1.f }; const WORD buttons[10] =
const WORD buttons[14] =
{ {
XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_A,
XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_B,
@ -670,37 +710,17 @@ int _glfwPlatformPollJoystick(int jid, int mode)
if (mode == _GLFW_POLL_PRESENCE) if (mode == _GLFW_POLL_PRESENCE)
return GLFW_TRUE; return GLFW_TRUE;
if ((float) xis.Gamepad.sThumbLX * xis.Gamepad.sThumbLX + _glfwInputJoystickAxis(js, 0, (xis.Gamepad.sThumbLX + 0.5f) / 32767.5f);
(float) xis.Gamepad.sThumbLY * xis.Gamepad.sThumbLY > _glfwInputJoystickAxis(js, 1, -(xis.Gamepad.sThumbLY + 0.5f) / 32767.5f);
(float) XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE * _glfwInputJoystickAxis(js, 2, (xis.Gamepad.sThumbRX + 0.5f) / 32767.5f);
XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) _glfwInputJoystickAxis(js, 3, -(xis.Gamepad.sThumbRY + 0.5f) / 32767.5f);
{ _glfwInputJoystickAxis(js, 4, xis.Gamepad.bLeftTrigger / 127.5f - 1.f);
axes[0] = (xis.Gamepad.sThumbLX + 0.5f) / 32767.f; _glfwInputJoystickAxis(js, 5, xis.Gamepad.bRightTrigger / 127.5f - 1.f);
axes[1] = (xis.Gamepad.sThumbLY + 0.5f) / 32767.f;
}
if ((float) xis.Gamepad.sThumbRX * xis.Gamepad.sThumbRX +
(float) xis.Gamepad.sThumbRY * xis.Gamepad.sThumbRY >
(float) XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE *
XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)
{
axes[2] = (xis.Gamepad.sThumbRX + 0.5f) / 32767.f;
axes[3] = (xis.Gamepad.sThumbRY + 0.5f) / 32767.f;
}
if (xis.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
axes[4] = xis.Gamepad.bLeftTrigger / 127.5f - 1.f;
if (xis.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
axes[5] = xis.Gamepad.bRightTrigger / 127.5f - 1.f;
for (i = 0; i < 6; i++)
_glfwInputJoystickAxis(jid, i, axes[i]);
for (i = 0; i < 10; i++) for (i = 0; i < 10; i++)
{ {
const char value = (xis.Gamepad.wButtons & buttons[i]) ? 1 : 0; const char value = (xis.Gamepad.wButtons & buttons[i]) ? 1 : 0;
_glfwInputJoystickButton(jid, i, value); _glfwInputJoystickButton(js, i, value);
} }
if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP)
@ -712,9 +732,20 @@ int _glfwPlatformPollJoystick(int jid, int mode)
if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT)
dpad |= GLFW_HAT_LEFT; dpad |= GLFW_HAT_LEFT;
_glfwInputJoystickHat(jid, 0, dpad); _glfwInputJoystickHat(js, 0, dpad);
} }
return GLFW_TRUE; return GLFW_TRUE;
} }
void _glfwPlatformUpdateGamepadGUID(char* guid)
{
if (strcmp(guid + 20, "504944564944") == 0)
{
char original[33];
strcpy(original, guid);
sprintf(guid, "03000000%.4s0000%.4s000000000000",
original, original + 4);
}
}

View File

@ -24,12 +24,10 @@
// //
//======================================================================== //========================================================================
#ifndef _glfw3_win32_joystick_h_
#define _glfw3_win32_joystick_h_
#define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickWin32 win32 #define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickWin32 win32
#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE int dummy #define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE int dummy
#define _GLFW_PLATFORM_MAPPING_NAME "Windows"
// Joystick element (axis, button or slider) // Joystick element (axis, button or slider)
// //
@ -56,4 +54,3 @@ void _glfwTerminateJoysticksWin32(void);
void _glfwDetectJoystickConnectionWin32(void); void _glfwDetectJoystickConnectionWin32(void);
void _glfwDetectJoystickDisconnectionWin32(void); void _glfwDetectJoystickDisconnectionWin32(void);
#endif // _glfw3_win32_joystick_h_

View File

@ -33,14 +33,38 @@
#include <malloc.h> #include <malloc.h>
// Callback for EnumDisplayMonitors in createMonitor
//
static BOOL CALLBACK monitorCallback(HMONITOR handle,
HDC dc,
RECT* rect,
LPARAM data)
{
MONITORINFOEXW mi;
ZeroMemory(&mi, sizeof(mi));
mi.cbSize = sizeof(mi);
if (GetMonitorInfoW(handle, (MONITORINFO*) &mi))
{
_GLFWmonitor* monitor = (_GLFWmonitor*) data;
if (wcscmp(mi.szDevice, monitor->win32.adapterName) == 0)
monitor->win32.handle = handle;
}
return TRUE;
}
// Create monitor from an adapter and (optionally) a display // Create monitor from an adapter and (optionally) a display
// //
static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter,
DISPLAY_DEVICEW* display) DISPLAY_DEVICEW* display)
{ {
_GLFWmonitor* monitor; _GLFWmonitor* monitor;
int widthMM, heightMM;
char* name; char* name;
HDC dc; HDC dc;
DEVMODEW dm;
RECT rect;
if (display) if (display)
name = _glfwCreateUTF8FromWideStringWin32(display->DeviceString); name = _glfwCreateUTF8FromWideStringWin32(display->DeviceString);
@ -49,13 +73,26 @@ static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter,
if (!name) if (!name)
return NULL; return NULL;
ZeroMemory(&dm, sizeof(dm));
dm.dmSize = sizeof(dm);
EnumDisplaySettingsW(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &dm);
dc = CreateDCW(L"DISPLAY", adapter->DeviceName, NULL, NULL); dc = CreateDCW(L"DISPLAY", adapter->DeviceName, NULL, NULL);
monitor = _glfwAllocMonitor(name, if (IsWindows8Point1OrGreater())
GetDeviceCaps(dc, HORZSIZE), {
GetDeviceCaps(dc, VERTSIZE)); widthMM = GetDeviceCaps(dc, HORZSIZE);
heightMM = GetDeviceCaps(dc, VERTSIZE);
}
else
{
widthMM = (int) (dm.dmPelsWidth * 25.4f / GetDeviceCaps(dc, LOGPIXELSX));
heightMM = (int) (dm.dmPelsHeight * 25.4f / GetDeviceCaps(dc, LOGPIXELSY));
}
DeleteDC(dc); DeleteDC(dc);
monitor = _glfwAllocMonitor(name, widthMM, heightMM);
free(name); free(name);
if (adapter->StateFlags & DISPLAY_DEVICE_MODESPRUNED) if (adapter->StateFlags & DISPLAY_DEVICE_MODESPRUNED)
@ -78,6 +115,12 @@ static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter,
NULL, NULL); NULL, NULL);
} }
rect.left = dm.dmPosition.x;
rect.top = dm.dmPosition.y;
rect.right = dm.dmPosition.x + dm.dmPelsWidth;
rect.bottom = dm.dmPosition.y + dm.dmPelsHeight;
EnumDisplayMonitors(NULL, &rect, monitorCallback, (LPARAM) monitor);
return monitor; return monitor;
} }
@ -94,6 +137,7 @@ void _glfwPollMonitorsWin32(void)
_GLFWmonitor** disconnected = NULL; _GLFWmonitor** disconnected = NULL;
DWORD adapterIndex, displayIndex; DWORD adapterIndex, displayIndex;
DISPLAY_DEVICEW adapter, display; DISPLAY_DEVICEW adapter, display;
_GLFWmonitor* monitor;
disconnectedCount = _glfw.monitorCount; disconnectedCount = _glfw.monitorCount;
if (disconnectedCount) if (disconnectedCount)
@ -108,8 +152,8 @@ void _glfwPollMonitorsWin32(void)
{ {
int type = _GLFW_INSERT_LAST; int type = _GLFW_INSERT_LAST;
ZeroMemory(&adapter, sizeof(DISPLAY_DEVICEW)); ZeroMemory(&adapter, sizeof(adapter));
adapter.cb = sizeof(DISPLAY_DEVICEW); adapter.cb = sizeof(adapter);
if (!EnumDisplayDevicesW(NULL, adapterIndex, &adapter, 0)) if (!EnumDisplayDevicesW(NULL, adapterIndex, &adapter, 0))
break; break;
@ -122,8 +166,8 @@ void _glfwPollMonitorsWin32(void)
for (displayIndex = 0; ; displayIndex++) for (displayIndex = 0; ; displayIndex++)
{ {
ZeroMemory(&display, sizeof(DISPLAY_DEVICEW)); ZeroMemory(&display, sizeof(display));
display.cb = sizeof(DISPLAY_DEVICEW); display.cb = sizeof(display);
if (!EnumDisplayDevicesW(adapter.DeviceName, displayIndex, &display, 0)) if (!EnumDisplayDevicesW(adapter.DeviceName, displayIndex, &display, 0))
break; break;
@ -145,8 +189,14 @@ void _glfwPollMonitorsWin32(void)
if (i < disconnectedCount) if (i < disconnectedCount)
continue; continue;
_glfwInputMonitor(createMonitor(&adapter, &display), monitor = createMonitor(&adapter, &display);
GLFW_CONNECTED, type); if (!monitor)
{
free(disconnected);
return;
}
_glfwInputMonitor(monitor, GLFW_CONNECTED, type);
type = _GLFW_INSERT_LAST; type = _GLFW_INSERT_LAST;
} }
@ -169,8 +219,14 @@ void _glfwPollMonitorsWin32(void)
if (i < disconnectedCount) if (i < disconnectedCount)
continue; continue;
_glfwInputMonitor(createMonitor(&adapter, NULL), monitor = createMonitor(&adapter, NULL);
GLFW_CONNECTED, type); if (!monitor)
{
free(disconnected);
return;
}
_glfwInputMonitor(monitor, GLFW_CONNECTED, type);
} }
} }
@ -198,7 +254,7 @@ GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desire
return GLFW_TRUE; return GLFW_TRUE;
ZeroMemory(&dm, sizeof(dm)); ZeroMemory(&dm, sizeof(dm));
dm.dmSize = sizeof(DEVMODEW); dm.dmSize = sizeof(dm);
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL |
DM_DISPLAYFREQUENCY; DM_DISPLAYFREQUENCY;
dm.dmPelsWidth = best->width; dm.dmPelsWidth = best->width;
@ -256,6 +312,26 @@ void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor)
} }
} }
void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale)
{
UINT xdpi, ydpi;
if (IsWindows8Point1OrGreater())
GetDpiForMonitor(handle, MDT_EFFECTIVE_DPI, &xdpi, &ydpi);
else
{
const HDC dc = GetDC(NULL);
xdpi = GetDeviceCaps(dc, LOGPIXELSX);
ydpi = GetDeviceCaps(dc, LOGPIXELSY);
ReleaseDC(NULL, dc);
}
if (xscale)
*xscale = xdpi / 96.f;
if (yscale)
*yscale = ydpi / 96.f;
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
////// GLFW platform API ////// ////// GLFW platform API //////
@ -263,19 +339,25 @@ void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor)
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
{ {
DEVMODEW settings; DEVMODEW dm;
ZeroMemory(&settings, sizeof(DEVMODEW)); ZeroMemory(&dm, sizeof(dm));
settings.dmSize = sizeof(DEVMODEW); dm.dmSize = sizeof(dm);
EnumDisplaySettingsExW(monitor->win32.adapterName, EnumDisplaySettingsExW(monitor->win32.adapterName,
ENUM_CURRENT_SETTINGS, ENUM_CURRENT_SETTINGS,
&settings, &dm,
EDS_ROTATEDMODE); EDS_ROTATEDMODE);
if (xpos) if (xpos)
*xpos = settings.dmPosition.x; *xpos = dm.dmPosition.x;
if (ypos) if (ypos)
*ypos = settings.dmPosition.y; *ypos = dm.dmPosition.y;
}
void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
float* xscale, float* yscale)
{
_glfwGetMonitorContentScaleWin32(monitor->win32.handle, xscale, yscale);
} }
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
@ -291,8 +373,8 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
GLFWvidmode mode; GLFWvidmode mode;
DEVMODEW dm; DEVMODEW dm;
ZeroMemory(&dm, sizeof(DEVMODEW)); ZeroMemory(&dm, sizeof(dm));
dm.dmSize = sizeof(DEVMODEW); dm.dmSize = sizeof(dm);
if (!EnumDisplaySettingsW(monitor->win32.adapterName, modeIndex, &dm)) if (!EnumDisplaySettingsW(monitor->win32.adapterName, modeIndex, &dm))
break; break;
@ -358,9 +440,8 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode)
{ {
DEVMODEW dm; DEVMODEW dm;
ZeroMemory(&dm, sizeof(dm));
ZeroMemory(&dm, sizeof(DEVMODEW)); dm.dmSize = sizeof(dm);
dm.dmSize = sizeof(DEVMODEW);
EnumDisplaySettingsW(monitor->win32.adapterName, ENUM_CURRENT_SETTINGS, &dm); EnumDisplaySettingsW(monitor->win32.adapterName, ENUM_CURRENT_SETTINGS, &dm);

View File

@ -25,9 +25,6 @@
// //
//======================================================================== //========================================================================
#ifndef _glfw3_win32_platform_h_
#define _glfw3_win32_platform_h_
// We don't need all the fancy stuff // We don't need all the fancy stuff
#ifndef NOMINMAX #ifndef NOMINMAX
#define NOMINMAX #define NOMINMAX
@ -66,7 +63,6 @@
#include <wctype.h> #include <wctype.h>
#include <windows.h> #include <windows.h>
#include <mmsystem.h>
#include <dinput.h> #include <dinput.h>
#include <xinput.h> #include <xinput.h>
#include <dbt.h> #include <dbt.h>
@ -104,28 +100,64 @@
#ifndef DISPLAY_DEVICE_ACTIVE #ifndef DISPLAY_DEVICE_ACTIVE
#define DISPLAY_DEVICE_ACTIVE 0x00000001 #define DISPLAY_DEVICE_ACTIVE 0x00000001
#endif #endif
#ifndef _WIN32_WINNT_WINBLUE
#define _WIN32_WINNT_WINBLUE 0x0602
#endif
#if WINVER < 0x0601 #if WINVER < 0x0601
typedef struct tagCHANGEFILTERSTRUCT typedef struct
{ {
DWORD cbSize; DWORD cbSize;
DWORD ExtStatus; DWORD ExtStatus;
} CHANGEFILTERSTRUCT;
} CHANGEFILTERSTRUCT, *PCHANGEFILTERSTRUCT;
#ifndef MSGFLT_ALLOW #ifndef MSGFLT_ALLOW
#define MSGFLT_ALLOW 1 #define MSGFLT_ALLOW 1
#endif #endif
#endif /*Windows 7*/ #endif /*Windows 7*/
#if WINVER < 0x0600
#define DWM_BB_ENABLE 0x00000001
#define DWM_BB_BLURREGION 0x00000002
typedef struct
{
DWORD dwFlags;
BOOL fEnable;
HRGN hRgnBlur;
BOOL fTransitionOnMaximized;
} DWM_BLURBEHIND;
#endif /*Windows Vista*/
#ifndef DPI_ENUMS_DECLARED #ifndef DPI_ENUMS_DECLARED
typedef enum PROCESS_DPI_AWARENESS typedef enum
{ {
PROCESS_DPI_UNAWARE = 0, PROCESS_DPI_UNAWARE = 0,
PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_SYSTEM_DPI_AWARE = 1,
PROCESS_PER_MONITOR_DPI_AWARE = 2 PROCESS_PER_MONITOR_DPI_AWARE = 2
} PROCESS_DPI_AWARENESS; } PROCESS_DPI_AWARENESS;
typedef enum
{
MDT_EFFECTIVE_DPI = 0,
MDT_ANGULAR_DPI = 1,
MDT_RAW_DPI = 2,
MDT_DEFAULT = MDT_EFFECTIVE_DPI
} MONITOR_DPI_TYPE;
#endif /*DPI_ENUMS_DECLARED*/ #endif /*DPI_ENUMS_DECLARED*/
// HACK: Define versionhelpers.h functions manually as MinGW lacks the header
BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp);
#define IsWindowsVistaOrGreater() \
IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), \
LOBYTE(_WIN32_WINNT_VISTA), 0)
#define IsWindows7OrGreater() \
IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), \
LOBYTE(_WIN32_WINNT_WIN7), 0)
#define IsWindows8OrGreater() \
IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), \
LOBYTE(_WIN32_WINNT_WIN8), 0)
#define IsWindows8Point1OrGreater() \
IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINBLUE), \
LOBYTE(_WIN32_WINNT_WINBLUE), 0)
// HACK: Define macros that some xinput.h variants don't // HACK: Define macros that some xinput.h variants don't
#ifndef XINPUT_CAPS_WIRELESS #ifndef XINPUT_CAPS_WIRELESS
#define XINPUT_CAPS_WIRELESS 0x0002 #define XINPUT_CAPS_WIRELESS 0x0002
@ -176,19 +208,23 @@ typedef HRESULT (WINAPI * PFN_DirectInput8Create)(HINSTANCE,DWORD,REFIID,LPVOID*
// user32.dll function pointer typedefs // user32.dll function pointer typedefs
typedef BOOL (WINAPI * PFN_SetProcessDPIAware)(void); typedef BOOL (WINAPI * PFN_SetProcessDPIAware)(void);
typedef BOOL (WINAPI * PFN_ChangeWindowMessageFilterEx)(HWND,UINT,DWORD,PCHANGEFILTERSTRUCT); typedef BOOL (WINAPI * PFN_ChangeWindowMessageFilterEx)(HWND,UINT,DWORD,CHANGEFILTERSTRUCT*);
#define _glfw_SetProcessDPIAware _glfw.win32.user32.SetProcessDPIAware #define SetProcessDPIAware _glfw.win32.user32.SetProcessDPIAware_
#define _glfw_ChangeWindowMessageFilterEx _glfw.win32.user32.ChangeWindowMessageFilterEx #define ChangeWindowMessageFilterEx _glfw.win32.user32.ChangeWindowMessageFilterEx_
// dwmapi.dll function pointer typedefs // dwmapi.dll function pointer typedefs
typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*); typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*);
typedef HRESULT (WINAPI * PFN_DwmFlush)(VOID); typedef HRESULT (WINAPI * PFN_DwmFlush)(VOID);
#define _glfw_DwmIsCompositionEnabled _glfw.win32.dwmapi.DwmIsCompositionEnabled typedef HRESULT(WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND,const DWM_BLURBEHIND*);
#define _glfw_DwmFlush _glfw.win32.dwmapi.DwmFlush #define DwmIsCompositionEnabled _glfw.win32.dwmapi.IsCompositionEnabled
#define DwmFlush _glfw.win32.dwmapi.Flush
#define DwmEnableBlurBehindWindow _glfw.win32.dwmapi.EnableBlurBehindWindow
// shcore.dll function pointer typedefs // shcore.dll function pointer typedefs
typedef HRESULT (WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); typedef HRESULT (WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS);
#define _glfw_SetProcessDpiAwareness _glfw.win32.shcore.SetProcessDpiAwareness typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*);
#define SetProcessDpiAwareness _glfw.win32.shcore.SetProcessDpiAwareness_
#define GetDpiForMonitor _glfw.win32.shcore.GetDpiForMonitor_
typedef VkFlags VkWin32SurfaceCreateFlagsKHR; typedef VkFlags VkWin32SurfaceCreateFlagsKHR;
@ -220,10 +256,11 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)(
#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowWin32 win32 #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowWin32 win32
#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryWin32 win32 #define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryWin32 win32
#define _GLFW_PLATFORM_LIBRARY_TIME_STATE _GLFWtimeWin32 win32_time #define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerWin32 win32
#define _GLFW_PLATFORM_LIBRARY_TLS_STATE _GLFWtlsWin32 win32_tls
#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorWin32 win32 #define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorWin32 win32
#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorWin32 win32 #define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorWin32 win32
#define _GLFW_PLATFORM_TLS_STATE _GLFWtlsWin32 win32
#define _GLFW_PLATFORM_MUTEX_STATE _GLFWmutexWin32 win32
// Win32-specific per-window data // Win32-specific per-window data
@ -235,8 +272,11 @@ typedef struct _GLFWwindowWin32
HICON smallIcon; HICON smallIcon;
GLFWbool cursorTracked; GLFWbool cursorTracked;
GLFWbool frameAction;
GLFWbool iconified; GLFWbool iconified;
GLFWbool maximized; GLFWbool maximized;
// Whether to enable framebuffer transparency on DWM
GLFWbool transparent;
// The last received cursor position, regardless of source // The last received cursor position, regardless of source
int lastCursorPosX, lastCursorPosY; int lastCursorPosX, lastCursorPosY;
@ -251,9 +291,9 @@ typedef struct _GLFWlibraryWin32
DWORD foregroundLockTimeout; DWORD foregroundLockTimeout;
int acquiredMonitorCount; int acquiredMonitorCount;
char* clipboardString; char* clipboardString;
char keyName[64];
short int keycodes[512]; short int keycodes[512];
short int scancodes[GLFW_KEY_LAST + 1]; short int scancodes[GLFW_KEY_LAST + 1];
char keynames[GLFW_KEY_LAST + 1][5];
// Where to place the cursor when re-enabled // Where to place the cursor when re-enabled
double restoreCursorPosX, restoreCursorPosY; double restoreCursorPosX, restoreCursorPosY;
// The window whose disabled cursor mode is active // The window whose disabled cursor mode is active
@ -280,19 +320,21 @@ typedef struct _GLFWlibraryWin32
struct { struct {
HINSTANCE instance; HINSTANCE instance;
PFN_SetProcessDPIAware SetProcessDPIAware; PFN_SetProcessDPIAware SetProcessDPIAware_;
PFN_ChangeWindowMessageFilterEx ChangeWindowMessageFilterEx; PFN_ChangeWindowMessageFilterEx ChangeWindowMessageFilterEx_;
} user32; } user32;
struct { struct {
HINSTANCE instance; HINSTANCE instance;
PFN_DwmIsCompositionEnabled DwmIsCompositionEnabled; PFN_DwmIsCompositionEnabled IsCompositionEnabled;
PFN_DwmFlush DwmFlush; PFN_DwmFlush Flush;
PFN_DwmEnableBlurBehindWindow EnableBlurBehindWindow;
} dwmapi; } dwmapi;
struct { struct {
HINSTANCE instance; HINSTANCE instance;
PFN_SetProcessDpiAwareness SetProcessDpiAwareness; PFN_SetProcessDpiAwareness SetProcessDpiAwareness_;
PFN_GetDpiForMonitor GetDpiForMonitor_;
} shcore; } shcore;
} _GLFWlibraryWin32; } _GLFWlibraryWin32;
@ -301,11 +343,12 @@ typedef struct _GLFWlibraryWin32
// //
typedef struct _GLFWmonitorWin32 typedef struct _GLFWmonitorWin32
{ {
HMONITOR handle;
// This size matches the static size of DISPLAY_DEVICE.DeviceName // This size matches the static size of DISPLAY_DEVICE.DeviceName
WCHAR adapterName[32]; WCHAR adapterName[32];
WCHAR displayName[32]; WCHAR displayName[32];
char publicAdapterName[64]; char publicAdapterName[32];
char publicDisplayName[64]; char publicDisplayName[32];
GLFWbool modesPruned; GLFWbool modesPruned;
GLFWbool modeChanged; GLFWbool modeChanged;
@ -321,37 +364,45 @@ typedef struct _GLFWcursorWin32
// Win32-specific global timer data // Win32-specific global timer data
// //
typedef struct _GLFWtimeWin32 typedef struct _GLFWtimerWin32
{ {
GLFWbool hasPC; GLFWbool hasPC;
uint64_t frequency; uint64_t frequency;
} _GLFWtimeWin32; } _GLFWtimerWin32;
// Win32-specific global TLS data // Win32-specific thread local storage data
// //
typedef struct _GLFWtlsWin32 typedef struct _GLFWtlsWin32
{ {
GLFWbool allocated; GLFWbool allocated;
DWORD context; DWORD index;
} _GLFWtlsWin32; } _GLFWtlsWin32;
// Win32-specific mutex data
//
typedef struct _GLFWmutexWin32
{
GLFWbool allocated;
CRITICAL_SECTION section;
} _GLFWmutexWin32;
GLFWbool _glfwRegisterWindowClassWin32(void); GLFWbool _glfwRegisterWindowClassWin32(void);
void _glfwUnregisterWindowClassWin32(void); void _glfwUnregisterWindowClassWin32(void);
GLFWbool _glfwIsCompositionEnabledWin32(void);
GLFWbool _glfwInitThreadLocalStorageWin32(void);
void _glfwTerminateThreadLocalStorageWin32(void);
WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source); WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source);
char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source); char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source);
void _glfwInputErrorWin32(int error, const char* description); void _glfwInputErrorWin32(int error, const char* description);
void _glfwUpdateKeyNamesWin32(void);
void _glfwInitTimerWin32(void); void _glfwInitTimerWin32(void);
void _glfwPollMonitorsWin32(void); void _glfwPollMonitorsWin32(void);
GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired); GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired);
void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor); void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor);
void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale);
#endif // _glfw3_win32_platform_h_

View File

@ -27,43 +27,71 @@
#include "internal.h" #include "internal.h"
#include <assert.h>
//////////////////////////////////////////////////////////////////////////
////// GLFW internal API //////
//////////////////////////////////////////////////////////////////////////
GLFWbool _glfwInitThreadLocalStorageWin32(void)
{
_glfw.win32_tls.context = TlsAlloc();
if (_glfw.win32_tls.context == TLS_OUT_OF_INDEXES)
{
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"Win32: Failed to allocate TLS index");
return GLFW_FALSE;
}
_glfw.win32_tls.allocated = GLFW_TRUE;
return GLFW_TRUE;
}
void _glfwTerminateThreadLocalStorageWin32(void)
{
if (_glfw.win32_tls.allocated)
TlsFree(_glfw.win32_tls.context);
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
////// GLFW platform API ////// ////// GLFW platform API //////
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
void _glfwPlatformSetCurrentContext(_GLFWwindow* context) GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls)
{ {
TlsSetValue(_glfw.win32_tls.context, context); assert(tls->win32.allocated == GLFW_FALSE);
tls->win32.index = TlsAlloc();
if (tls->win32.index == TLS_OUT_OF_INDEXES)
{
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"Win32: Failed to allocate TLS index");
return GLFW_FALSE;
}
tls->win32.allocated = GLFW_TRUE;
return GLFW_TRUE;
} }
_GLFWwindow* _glfwPlatformGetCurrentContext(void) void _glfwPlatformDestroyTls(_GLFWtls* tls)
{ {
return TlsGetValue(_glfw.win32_tls.context); if (tls->win32.allocated)
TlsFree(tls->win32.index);
memset(tls, 0, sizeof(_GLFWtls));
}
void* _glfwPlatformGetTls(_GLFWtls* tls)
{
assert(tls->win32.allocated == GLFW_TRUE);
return TlsGetValue(tls->win32.index);
}
void _glfwPlatformSetTls(_GLFWtls* tls, void* value)
{
assert(tls->win32.allocated == GLFW_TRUE);
TlsSetValue(tls->win32.index, value);
}
GLFWbool _glfwPlatformCreateMutex(_GLFWmutex* mutex)
{
assert(mutex->win32.allocated == GLFW_FALSE);
InitializeCriticalSection(&mutex->win32.section);
return mutex->win32.allocated = GLFW_TRUE;
}
void _glfwPlatformDestroyMutex(_GLFWmutex* mutex)
{
if (mutex->win32.allocated)
DeleteCriticalSection(&mutex->win32.section);
memset(mutex, 0, sizeof(_GLFWmutex));
}
void _glfwPlatformLockMutex(_GLFWmutex* mutex)
{
assert(mutex->win32.allocated == GLFW_TRUE);
EnterCriticalSection(&mutex->win32.section);
}
void _glfwPlatformUnlockMutex(_GLFWmutex* mutex)
{
assert(mutex->win32.allocated == GLFW_TRUE);
LeaveCriticalSection(&mutex->win32.section);
} }

View File

@ -40,13 +40,13 @@ void _glfwInitTimerWin32(void)
if (QueryPerformanceFrequency((LARGE_INTEGER*) &frequency)) if (QueryPerformanceFrequency((LARGE_INTEGER*) &frequency))
{ {
_glfw.win32_time.hasPC = GLFW_TRUE; _glfw.timer.win32.hasPC = GLFW_TRUE;
_glfw.win32_time.frequency = frequency; _glfw.timer.win32.frequency = frequency;
} }
else else
{ {
_glfw.win32_time.hasPC = GLFW_FALSE; _glfw.timer.win32.hasPC = GLFW_FALSE;
_glfw.win32_time.frequency = 1000; _glfw.timer.win32.frequency = 1000;
} }
} }
@ -57,7 +57,7 @@ void _glfwInitTimerWin32(void)
uint64_t _glfwPlatformGetTimerValue(void) uint64_t _glfwPlatformGetTimerValue(void)
{ {
if (_glfw.win32_time.hasPC) if (_glfw.timer.win32.hasPC)
{ {
uint64_t value; uint64_t value;
QueryPerformanceCounter((LARGE_INTEGER*) &value); QueryPerformanceCounter((LARGE_INTEGER*) &value);
@ -69,6 +69,6 @@ uint64_t _glfwPlatformGetTimerValue(void)
uint64_t _glfwPlatformGetTimerFrequency(void) uint64_t _glfwPlatformGetTimerFrequency(void)
{ {
return _glfw.win32_time.frequency; return _glfw.timer.win32.frequency;
} }

View File

@ -111,7 +111,7 @@ static HICON createIcon(const GLFWimage* image,
unsigned char* source = image->pixels; unsigned char* source = image->pixels;
ZeroMemory(&bi, sizeof(bi)); ZeroMemory(&bi, sizeof(bi));
bi.bV5Size = sizeof(BITMAPV5HEADER); bi.bV5Size = sizeof(bi);
bi.bV5Width = image->width; bi.bV5Width = image->width;
bi.bV5Height = -image->height; bi.bV5Height = -image->height;
bi.bV5Planes = 1; bi.bV5Planes = 1;
@ -306,6 +306,55 @@ static void updateWindowStyles(const _GLFWwindow* window)
SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER); SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER);
} }
// Update window framebuffer transparency
//
static void updateFramebufferTransparency(const _GLFWwindow* window)
{
if (!IsWindowsVistaOrGreater())
return;
if (_glfwIsCompositionEnabledWin32())
{
HRGN region = CreateRectRgn(0, 0, -1, -1);
DWM_BLURBEHIND bb = {0};
bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
bb.hRgnBlur = region;
bb.fEnable = TRUE;
if (SUCCEEDED(DwmEnableBlurBehindWindow(window->win32.handle, &bb)))
{
// Decorated windows don't repaint the transparent background
// leaving a trail behind animations
// HACK: Making the window layered with a transparency color key
// seems to fix this. Normally, when specifying
// a transparency color key to be used when composing the
// layered window, all pixels painted by the window in this
// color will be transparent. That doesn't seem to be the
// case anymore, at least when used with blur behind window
// plus negative region.
LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
exStyle |= WS_EX_LAYERED;
SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle);
// Using a color key not equal to black to fix the trailing
// issue. When set to black, something is making the hit test
// not resize with the window frame.
SetLayeredWindowAttributes(window->win32.handle,
RGB(0, 193, 48), 255, LWA_COLORKEY);
}
DeleteObject(region);
}
else
{
LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
exStyle &= ~WS_EX_LAYERED;
SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle);
RedrawWindow(window->win32.handle, NULL, NULL,
RDW_ERASE | RDW_INVALIDATE | RDW_FRAME);
}
}
// Translates a GLFW standard cursor to a resource ID // Translates a GLFW standard cursor to a resource ID
// //
static LPWSTR translateCursorShape(int shape) static LPWSTR translateCursorShape(int shape)
@ -335,14 +384,18 @@ static int getKeyMods(void)
{ {
int mods = 0; int mods = 0;
if (GetKeyState(VK_SHIFT) & (1 << 31)) if (GetKeyState(VK_SHIFT) & 0x8000)
mods |= GLFW_MOD_SHIFT; mods |= GLFW_MOD_SHIFT;
if (GetKeyState(VK_CONTROL) & (1 << 31)) if (GetKeyState(VK_CONTROL) & 0x8000)
mods |= GLFW_MOD_CONTROL; mods |= GLFW_MOD_CONTROL;
if (GetKeyState(VK_MENU) & (1 << 31)) if (GetKeyState(VK_MENU) & 0x8000)
mods |= GLFW_MOD_ALT; mods |= GLFW_MOD_ALT;
if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & (1 << 31)) if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x8000)
mods |= GLFW_MOD_SUPER; mods |= GLFW_MOD_SUPER;
if (GetKeyState(VK_CAPITAL) & 1)
mods |= GLFW_MOD_CAPS_LOCK;
if (GetKeyState(VK_NUMLOCK) & 1)
mods |= GLFW_MOD_NUM_LOCK;
return mods; return mods;
} }
@ -353,14 +406,18 @@ static int getAsyncKeyMods(void)
{ {
int mods = 0; int mods = 0;
if (GetAsyncKeyState(VK_SHIFT) & (1 << 31)) if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
mods |= GLFW_MOD_SHIFT; mods |= GLFW_MOD_SHIFT;
if (GetAsyncKeyState(VK_CONTROL) & (1 << 31)) if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
mods |= GLFW_MOD_CONTROL; mods |= GLFW_MOD_CONTROL;
if (GetAsyncKeyState(VK_MENU) & (1 << 31)) if (GetAsyncKeyState(VK_MENU) & 0x8000)
mods |= GLFW_MOD_ALT; mods |= GLFW_MOD_ALT;
if ((GetAsyncKeyState(VK_LWIN) | GetAsyncKeyState(VK_RWIN)) & (1 << 31)) if ((GetAsyncKeyState(VK_LWIN) | GetAsyncKeyState(VK_RWIN)) & 0x8000)
mods |= GLFW_MOD_SUPER; mods |= GLFW_MOD_SUPER;
if (GetAsyncKeyState(VK_CAPITAL) & 1)
mods |= GLFW_MOD_CAPS_LOCK;
if (GetAsyncKeyState(VK_NUMLOCK) & 1)
mods |= GLFW_MOD_NUM_LOCK;
return mods; return mods;
} }
@ -495,10 +552,47 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
switch (uMsg) switch (uMsg)
{ {
case WM_MOUSEACTIVATE:
{
// HACK: Postpone cursor disabling when the window was activated by
// clicking a caption button
if (HIWORD(lParam) == WM_LBUTTONDOWN)
{
if (LOWORD(lParam) == HTCLOSE ||
LOWORD(lParam) == HTMINBUTTON ||
LOWORD(lParam) == HTMAXBUTTON)
{
window->win32.frameAction = GLFW_TRUE;
}
}
break;
}
case WM_CAPTURECHANGED:
{
// HACK: Disable the cursor once the caption button action has been
// completed or cancelled
if (lParam == 0 && window->win32.frameAction)
{
if (window->cursorMode == GLFW_CURSOR_DISABLED)
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED);
window->win32.frameAction = GLFW_FALSE;
}
break;
}
case WM_SETFOCUS: case WM_SETFOCUS:
{ {
_glfwInputWindowFocus(window, GLFW_TRUE); _glfwInputWindowFocus(window, GLFW_TRUE);
// HACK: Do not disable cursor while the user is interacting with
// a caption button
if (window->win32.frameAction)
break;
if (window->cursorMode == GLFW_CURSOR_DISABLED) if (window->cursorMode == GLFW_CURSOR_DISABLED)
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED); _glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED);
@ -547,6 +641,12 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
return 0; return 0;
} }
case WM_INPUTLANGCHANGE:
{
_glfwUpdateKeyNamesWin32();
break;
}
case WM_CHAR: case WM_CHAR:
case WM_SYSCHAR: case WM_SYSCHAR:
case WM_UNICHAR: case WM_UNICHAR:
@ -628,24 +728,24 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
else else
action = GLFW_RELEASE; action = GLFW_RELEASE;
for (i = 0; i < GLFW_MOUSE_BUTTON_LAST; i++) for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++)
{ {
if (window->mouseButtons[i] == GLFW_PRESS) if (window->mouseButtons[i] == GLFW_PRESS)
break; break;
} }
if (i == GLFW_MOUSE_BUTTON_LAST) if (i > GLFW_MOUSE_BUTTON_LAST)
SetCapture(hWnd); SetCapture(hWnd);
_glfwInputMouseClick(window, button, action, getKeyMods()); _glfwInputMouseClick(window, button, action, getKeyMods());
for (i = 0; i < GLFW_MOUSE_BUTTON_LAST; i++) for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++)
{ {
if (window->mouseButtons[i] == GLFW_PRESS) if (window->mouseButtons[i] == GLFW_PRESS)
break; break;
} }
if (i == GLFW_MOUSE_BUTTON_LAST) if (i > GLFW_MOUSE_BUTTON_LAST)
ReleaseCapture(); ReleaseCapture();
if (uMsg == WM_XBUTTONDOWN || uMsg == WM_XBUTTONUP) if (uMsg == WM_XBUTTONDOWN || uMsg == WM_XBUTTONUP)
@ -758,6 +858,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
case WM_ENTERSIZEMOVE: case WM_ENTERSIZEMOVE:
case WM_ENTERMENULOOP: case WM_ENTERMENULOOP:
{ {
// HACK: Postpone cursor disabling while the user is moving or
// resizing the window or using the menu
if (window->cursorMode == GLFW_CURSOR_DISABLED) if (window->cursorMode == GLFW_CURSOR_DISABLED)
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL); _glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL);
@ -767,6 +869,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
case WM_EXITSIZEMOVE: case WM_EXITSIZEMOVE:
case WM_EXITMENULOOP: case WM_EXITMENULOOP:
{ {
// HACK: Disable the cursor once the user is done moving or
// resizing the window or using the menu
if (window->cursorMode == GLFW_CURSOR_DISABLED) if (window->cursorMode == GLFW_CURSOR_DISABLED)
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED); _glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED);
@ -855,6 +959,22 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
mmi->ptMaxTrackSize.y = window->maxheight + yoff; mmi->ptMaxTrackSize.y = window->maxheight + yoff;
} }
if (!window->decorated)
{
MONITORINFO mi;
const HMONITOR mh = MonitorFromWindow(window->win32.handle,
MONITOR_DEFAULTTONEAREST);
ZeroMemory(&mi, sizeof(mi));
mi.cbSize = sizeof(mi);
GetMonitorInfo(mh, &mi);
mmi->ptMaxPosition.x = mi.rcWork.left - mi.rcMonitor.left;
mmi->ptMaxPosition.y = mi.rcWork.top - mi.rcMonitor.top;
mmi->ptMaxSize.x = mi.rcWork.right - mi.rcWork.left;
mmi->ptMaxSize.y = mi.rcWork.bottom - mi.rcWork.top;
}
return 0; return 0;
} }
@ -869,6 +989,13 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
return TRUE; return TRUE;
} }
case WM_DWMCOMPOSITIONCHANGED:
{
if (window->win32.transparent)
updateFramebufferTransparency(window);
return 0;
}
case WM_SETCURSOR: case WM_SETCURSOR:
{ {
if (LOWORD(lParam) == HTCLIENT) if (LOWORD(lParam) == HTCLIENT)
@ -880,19 +1007,6 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
break; break;
} }
case WM_DPICHANGED:
{
RECT* rect = (RECT*) lParam;
SetWindowPos(window->win32.handle,
HWND_TOP,
rect->left,
rect->top,
rect->right - rect->left,
rect->bottom - rect->top,
SWP_NOACTIVATE | SWP_NOZORDER);
break;
}
case WM_DROPFILES: case WM_DROPFILES:
{ {
HDROP drop = (HDROP) wParam; HDROP drop = (HDROP) wParam;
@ -934,7 +1048,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
// Creates the GLFW window // Creates the GLFW window
// //
static int createNativeWindow(_GLFWwindow* window, static int createNativeWindow(_GLFWwindow* window,
const _GLFWwndconfig* wndconfig) const _GLFWwndconfig* wndconfig,
const _GLFWfbconfig* fbconfig)
{ {
int xpos, ypos, fullWidth, fullHeight; int xpos, ypos, fullWidth, fullHeight;
WCHAR* wideTitle; WCHAR* wideTitle;
@ -992,18 +1107,24 @@ static int createNativeWindow(_GLFWwindow* window,
SetPropW(window->win32.handle, L"GLFW", window); SetPropW(window->win32.handle, L"GLFW", window);
if (_glfw_ChangeWindowMessageFilterEx) if (IsWindows7OrGreater())
{ {
_glfw_ChangeWindowMessageFilterEx(window->win32.handle, ChangeWindowMessageFilterEx(window->win32.handle,
WM_DROPFILES, MSGFLT_ALLOW, NULL); WM_DROPFILES, MSGFLT_ALLOW, NULL);
_glfw_ChangeWindowMessageFilterEx(window->win32.handle, ChangeWindowMessageFilterEx(window->win32.handle,
WM_COPYDATA, MSGFLT_ALLOW, NULL); WM_COPYDATA, MSGFLT_ALLOW, NULL);
_glfw_ChangeWindowMessageFilterEx(window->win32.handle, ChangeWindowMessageFilterEx(window->win32.handle,
WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL); WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL);
} }
DragAcceptFiles(window->win32.handle, TRUE); DragAcceptFiles(window->win32.handle, TRUE);
if (fbconfig->transparent)
{
updateFramebufferTransparency(window);
window->win32.transparent = GLFW_TRUE;
}
return GLFW_TRUE; return GLFW_TRUE;
} }
@ -1055,6 +1176,20 @@ void _glfwUnregisterWindowClassWin32(void)
UnregisterClassW(_GLFW_WNDCLASSNAME, GetModuleHandleW(NULL)); UnregisterClassW(_GLFW_WNDCLASSNAME, GetModuleHandleW(NULL));
} }
// Returns whether desktop compositing is enabled
//
GLFWbool _glfwIsCompositionEnabledWin32(void)
{
if (IsWindowsVistaOrGreater())
{
BOOL enabled;
if (SUCCEEDED(DwmIsCompositionEnabled(&enabled)))
return enabled;
}
return FALSE;
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
////// GLFW platform API ////// ////// GLFW platform API //////
@ -1065,7 +1200,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig, const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig) const _GLFWfbconfig* fbconfig)
{ {
if (!createNativeWindow(window, wndconfig)) if (!createNativeWindow(window, wndconfig, fbconfig))
return GLFW_FALSE; return GLFW_FALSE;
if (ctxconfig->client != GLFW_NO_API) if (ctxconfig->client != GLFW_NO_API)
@ -1291,6 +1426,14 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
*bottom = rect.bottom - height; *bottom = rect.bottom - height;
} }
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
float* xscale, float* yscale)
{
const HANDLE handle = MonitorFromWindow(window->win32.handle,
MONITOR_DEFAULTTONEAREST);
_glfwGetMonitorContentScaleWin32(handle, xscale, yscale);
}
void _glfwPlatformIconifyWindow(_GLFWwindow* window) void _glfwPlatformIconifyWindow(_GLFWwindow* window)
{ {
ShowWindow(window->win32.handle, SW_MINIMIZE); ShowWindow(window->win32.handle, SW_MINIMIZE);
@ -1316,6 +1459,11 @@ void _glfwPlatformHideWindow(_GLFWwindow* window)
ShowWindow(window->win32.handle, SW_HIDE); ShowWindow(window->win32.handle, SW_HIDE);
} }
void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
{
FlashWindow(window->win32.handle, TRUE);
}
void _glfwPlatformFocusWindow(_GLFWwindow* window) void _glfwPlatformFocusWindow(_GLFWwindow* window)
{ {
BringWindowToTop(window->win32.handle); BringWindowToTop(window->win32.handle);
@ -1353,30 +1501,23 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
if (window->monitor) if (window->monitor)
releaseMonitor(window); releaseMonitor(window);
_glfwInputWindowMonitorChange(window, monitor); _glfwInputWindowMonitor(window, monitor);
if (monitor) if (monitor)
{ {
GLFWvidmode mode;
DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE);
UINT flags = SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOCOPYBITS;
if (window->decorated) if (window->decorated)
{ {
DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE);
UINT flags = SWP_FRAMECHANGED | SWP_SHOWWINDOW |
SWP_NOACTIVATE | SWP_NOCOPYBITS |
SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE;
style &= ~WS_OVERLAPPEDWINDOW; style &= ~WS_OVERLAPPEDWINDOW;
style |= getWindowStyle(window); style |= getWindowStyle(window);
SetWindowLongW(window->win32.handle, GWL_STYLE, style); SetWindowLongW(window->win32.handle, GWL_STYLE, style);
SetWindowPos(window->win32.handle, HWND_TOPMOST, 0, 0, 0, 0, flags);
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); acquireMonitor(window);
} }
else else
@ -1429,6 +1570,11 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window)
return IsZoomed(window->win32.handle); return IsZoomed(window->win32.handle);
} }
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
{
return window->win32.transparent && _glfwIsCompositionEnabledWin32();
}
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
{ {
updateWindowStyles(window); updateWindowStyles(window);
@ -1446,6 +1592,39 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
} }
float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
{
BYTE alpha;
DWORD flags;
if ((GetWindowLongW(window->win32.handle, GWL_EXSTYLE) & WS_EX_LAYERED) &&
GetLayeredWindowAttributes(window->win32.handle, NULL, &alpha, &flags))
{
if (flags & LWA_ALPHA)
return alpha / 255.f;
}
return 1.f;
}
void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
{
if (opacity < 1.f)
{
const BYTE alpha = (BYTE) (255 * opacity);
DWORD style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
style |= WS_EX_LAYERED;
SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style);
SetLayeredWindowAttributes(window->win32.handle, 0, alpha, LWA_ALPHA);
}
else
{
DWORD style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
style &= ~WS_EX_LAYERED;
SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style);
}
}
void _glfwPlatformPollEvents(void) void _glfwPlatformPollEvents(void)
{ {
MSG msg; MSG msg;
@ -1604,28 +1783,9 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
updateCursorImage(window); updateCursorImage(window);
} }
const char* _glfwPlatformGetKeyName(int key, int scancode) const char* _glfwPlatformGetScancodeName(int scancode)
{ {
WCHAR name[16]; return _glfw.win32.keynames[_glfw.win32.keycodes[scancode]];
if (key != GLFW_KEY_UNKNOWN)
scancode = _glfw.win32.scancodes[key];
if (!_glfwIsPrintable(_glfw.win32.keycodes[scancode]))
return NULL;
if (!GetKeyNameTextW(scancode << 16, name, sizeof(name) / sizeof(WCHAR)))
return NULL;
if (!WideCharToMultiByte(CP_UTF8, 0, name, -1,
_glfw.win32.keyName,
sizeof(_glfw.win32.keyName),
NULL, NULL))
{
return NULL;
}
return _glfw.win32.keyName;
} }
int _glfwPlatformGetKeyScancode(int key) int _glfwPlatformGetKeyScancode(int key)
@ -1670,7 +1830,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
updateCursorImage(window); updateCursorImage(window);
} }
void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) void _glfwPlatformSetClipboardString(const char* string)
{ {
int characterCount; int characterCount;
HANDLE object; HANDLE object;
@ -1713,7 +1873,7 @@ void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string)
CloseClipboard(); CloseClipboard();
} }
const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) const char* _glfwPlatformGetClipboardString(void)
{ {
HANDLE object; HANDLE object;
WCHAR* buffer; WCHAR* buffer;
@ -1765,7 +1925,8 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
VkPhysicalDevice device, VkPhysicalDevice device,
uint32_t queuefamily) uint32_t queuefamily)
{ {
PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR vkGetPhysicalDeviceWin32PresentationSupportKHR = PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR
vkGetPhysicalDeviceWin32PresentationSupportKHR =
(PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR) (PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)
vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR"); vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR");
if (!vkGetPhysicalDeviceWin32PresentationSupportKHR) if (!vkGetPhysicalDeviceWin32PresentationSupportKHR)

View File

@ -108,7 +108,7 @@ void _glfwInputWindowCloseRequest(_GLFWwindow* window)
window->callbacks.close((GLFWwindow*) window); window->callbacks.close((GLFWwindow*) window);
} }
void _glfwInputWindowMonitorChange(_GLFWwindow* window, _GLFWmonitor* monitor) void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor)
{ {
window->monitor = monitor; window->monitor = monitor;
} }
@ -192,7 +192,7 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height,
window->denom = GLFW_DONT_CARE; window->denom = GLFW_DONT_CARE;
// Save the currently current context so it can be restored later // Save the currently current context so it can be restored later
previous = _glfwPlatformGetCurrentContext(); previous = _glfwPlatformGetTls(&_glfw.contextSlot);
if (ctxconfig.client != GLFW_NO_API) if (ctxconfig.client != GLFW_NO_API)
glfwMakeContextCurrent(NULL); glfwMakeContextCurrent(NULL);
@ -251,6 +251,7 @@ void glfwDefaultWindowHints(void)
_glfw.hints.window.decorated = GLFW_TRUE; _glfw.hints.window.decorated = GLFW_TRUE;
_glfw.hints.window.focused = GLFW_TRUE; _glfw.hints.window.focused = GLFW_TRUE;
_glfw.hints.window.autoIconify = GLFW_TRUE; _glfw.hints.window.autoIconify = GLFW_TRUE;
_glfw.hints.window.centerCursor = GLFW_TRUE;
// The default is 24 bits of color, 24 bits of depth and 8 bits of stencil, // The default is 24 bits of color, 24 bits of depth and 8 bits of stencil,
// double buffered // double buffered
@ -278,119 +279,121 @@ GLFWAPI void glfwWindowHint(int hint, int value)
{ {
case GLFW_RED_BITS: case GLFW_RED_BITS:
_glfw.hints.framebuffer.redBits = value; _glfw.hints.framebuffer.redBits = value;
break; return;
case GLFW_GREEN_BITS: case GLFW_GREEN_BITS:
_glfw.hints.framebuffer.greenBits = value; _glfw.hints.framebuffer.greenBits = value;
break; return;
case GLFW_BLUE_BITS: case GLFW_BLUE_BITS:
_glfw.hints.framebuffer.blueBits = value; _glfw.hints.framebuffer.blueBits = value;
break; return;
case GLFW_ALPHA_BITS: case GLFW_ALPHA_BITS:
_glfw.hints.framebuffer.alphaBits = value; _glfw.hints.framebuffer.alphaBits = value;
break; return;
case GLFW_DEPTH_BITS: case GLFW_DEPTH_BITS:
_glfw.hints.framebuffer.depthBits = value; _glfw.hints.framebuffer.depthBits = value;
break; return;
case GLFW_STENCIL_BITS: case GLFW_STENCIL_BITS:
_glfw.hints.framebuffer.stencilBits = value; _glfw.hints.framebuffer.stencilBits = value;
break; return;
case GLFW_ACCUM_RED_BITS: case GLFW_ACCUM_RED_BITS:
_glfw.hints.framebuffer.accumRedBits = value; _glfw.hints.framebuffer.accumRedBits = value;
break; return;
case GLFW_ACCUM_GREEN_BITS: case GLFW_ACCUM_GREEN_BITS:
_glfw.hints.framebuffer.accumGreenBits = value; _glfw.hints.framebuffer.accumGreenBits = value;
break; return;
case GLFW_ACCUM_BLUE_BITS: case GLFW_ACCUM_BLUE_BITS:
_glfw.hints.framebuffer.accumBlueBits = value; _glfw.hints.framebuffer.accumBlueBits = value;
break; return;
case GLFW_ACCUM_ALPHA_BITS: case GLFW_ACCUM_ALPHA_BITS:
_glfw.hints.framebuffer.accumAlphaBits = value; _glfw.hints.framebuffer.accumAlphaBits = value;
break; return;
case GLFW_AUX_BUFFERS: case GLFW_AUX_BUFFERS:
_glfw.hints.framebuffer.auxBuffers = value; _glfw.hints.framebuffer.auxBuffers = value;
break; return;
case GLFW_STEREO: case GLFW_STEREO:
_glfw.hints.framebuffer.stereo = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.framebuffer.stereo = value ? GLFW_TRUE : GLFW_FALSE;
break; return;
case GLFW_DOUBLEBUFFER: case GLFW_DOUBLEBUFFER:
_glfw.hints.framebuffer.doublebuffer = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.framebuffer.doublebuffer = value ? GLFW_TRUE : GLFW_FALSE;
break; return;
case GLFW_TRANSPARENT_FRAMEBUFFER:
_glfw.hints.framebuffer.transparent = value ? GLFW_TRUE : GLFW_FALSE;
return;
case GLFW_SAMPLES: case GLFW_SAMPLES:
_glfw.hints.framebuffer.samples = value; _glfw.hints.framebuffer.samples = value;
break; return;
case GLFW_SRGB_CAPABLE: case GLFW_SRGB_CAPABLE:
_glfw.hints.framebuffer.sRGB = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.framebuffer.sRGB = value ? GLFW_TRUE : GLFW_FALSE;
break; return;
case GLFW_RESIZABLE: case GLFW_RESIZABLE:
_glfw.hints.window.resizable = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.window.resizable = value ? GLFW_TRUE : GLFW_FALSE;
break; return;
case GLFW_DECORATED: case GLFW_DECORATED:
_glfw.hints.window.decorated = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.window.decorated = value ? GLFW_TRUE : GLFW_FALSE;
break; return;
case GLFW_FOCUSED: case GLFW_FOCUSED:
_glfw.hints.window.focused = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.window.focused = value ? GLFW_TRUE : GLFW_FALSE;
break; return;
case GLFW_AUTO_ICONIFY: case GLFW_AUTO_ICONIFY:
_glfw.hints.window.autoIconify = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.window.autoIconify = value ? GLFW_TRUE : GLFW_FALSE;
break; return;
case GLFW_FLOATING: case GLFW_FLOATING:
_glfw.hints.window.floating = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.window.floating = value ? GLFW_TRUE : GLFW_FALSE;
break; return;
case GLFW_MAXIMIZED: case GLFW_MAXIMIZED:
_glfw.hints.window.maximized = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.window.maximized = value ? GLFW_TRUE : GLFW_FALSE;
break; return;
case GLFW_VISIBLE: case GLFW_VISIBLE:
_glfw.hints.window.visible = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.window.visible = value ? GLFW_TRUE : GLFW_FALSE;
break; return;
case GLFW_COCOA_RETINA_FRAMEBUFFER: case GLFW_COCOA_RETINA_FRAMEBUFFER:
_glfw.hints.window.ns.retina = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.window.ns.retina = value ? GLFW_TRUE : GLFW_FALSE;
break; return;
case GLFW_COCOA_FRAME_AUTOSAVE: case GLFW_COCOA_FRAME_AUTOSAVE:
_glfw.hints.window.ns.frame = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.window.ns.frame = value ? GLFW_TRUE : GLFW_FALSE;
break; return;
case GLFW_COCOA_GRAPHICS_SWITCHING: case GLFW_COCOA_GRAPHICS_SWITCHING:
_glfw.hints.context.nsgl.offline = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.context.nsgl.offline = value ? GLFW_TRUE : GLFW_FALSE;
break; return;
case GLFW_CENTER_CURSOR: case GLFW_CENTER_CURSOR:
_glfw.hints.window.centerCursor = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.window.centerCursor = value ? GLFW_TRUE : GLFW_FALSE;
break; return;
case GLFW_CLIENT_API: case GLFW_CLIENT_API:
_glfw.hints.context.client = value; _glfw.hints.context.client = value;
break; return;
case GLFW_CONTEXT_CREATION_API: case GLFW_CONTEXT_CREATION_API:
_glfw.hints.context.source = value; _glfw.hints.context.source = value;
break; return;
case GLFW_CONTEXT_VERSION_MAJOR: case GLFW_CONTEXT_VERSION_MAJOR:
_glfw.hints.context.major = value; _glfw.hints.context.major = value;
break; return;
case GLFW_CONTEXT_VERSION_MINOR: case GLFW_CONTEXT_VERSION_MINOR:
_glfw.hints.context.minor = value; _glfw.hints.context.minor = value;
break; return;
case GLFW_CONTEXT_ROBUSTNESS: case GLFW_CONTEXT_ROBUSTNESS:
_glfw.hints.context.robustness = value; _glfw.hints.context.robustness = value;
break; return;
case GLFW_OPENGL_FORWARD_COMPAT: case GLFW_OPENGL_FORWARD_COMPAT:
_glfw.hints.context.forward = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.context.forward = value ? GLFW_TRUE : GLFW_FALSE;
break; return;
case GLFW_OPENGL_DEBUG_CONTEXT: case GLFW_OPENGL_DEBUG_CONTEXT:
_glfw.hints.context.debug = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.context.debug = value ? GLFW_TRUE : GLFW_FALSE;
break; return;
case GLFW_CONTEXT_NO_ERROR: case GLFW_CONTEXT_NO_ERROR:
_glfw.hints.context.noerror = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.context.noerror = value ? GLFW_TRUE : GLFW_FALSE;
break; return;
case GLFW_OPENGL_PROFILE: case GLFW_OPENGL_PROFILE:
_glfw.hints.context.profile = value; _glfw.hints.context.profile = value;
break; return;
case GLFW_CONTEXT_RELEASE_BEHAVIOR: case GLFW_CONTEXT_RELEASE_BEHAVIOR:
_glfw.hints.context.release = value; _glfw.hints.context.release = value;
break; return;
case GLFW_REFRESH_RATE: case GLFW_REFRESH_RATE:
_glfw.hints.refreshRate = value; _glfw.hints.refreshRate = value;
break; return;
default:
_glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint %i", hint);
break;
} }
_glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint 0x%08X", hint);
} }
GLFWAPI void glfwDestroyWindow(GLFWwindow* handle) GLFWAPI void glfwDestroyWindow(GLFWwindow* handle)
@ -408,7 +411,7 @@ GLFWAPI void glfwDestroyWindow(GLFWwindow* handle)
// The window's context must not be current on another thread when the // The window's context must not be current on another thread when the
// window is destroyed // window is destroyed
if (window == _glfwPlatformGetCurrentContext()) if (window == _glfwPlatformGetTls(&_glfw.contextSlot))
glfwMakeContextCurrent(NULL); glfwMakeContextCurrent(NULL);
_glfwPlatformDestroyWindow(window); _glfwPlatformDestroyWindow(window);
@ -630,6 +633,49 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* handle,
_glfwPlatformGetWindowFrameSize(window, left, top, right, bottom); _glfwPlatformGetWindowFrameSize(window, left, top, right, bottom);
} }
GLFWAPI void glfwGetWindowContentScale(GLFWwindow* handle,
float* xscale, float* yscale)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL);
if (xscale)
*xscale = 0.f;
if (yscale)
*yscale = 0.f;
_GLFW_REQUIRE_INIT();
_glfwPlatformGetWindowContentScale(window, xscale, yscale);
}
GLFWAPI float glfwGetWindowOpacity(GLFWwindow* handle)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(1.f);
return _glfwPlatformGetWindowOpacity(window);
}
GLFWAPI void glfwSetWindowOpacity(GLFWwindow* handle, float opacity)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL);
assert(opacity == opacity);
assert(opacity >= 0.f);
assert(opacity <= 1.f);
_GLFW_REQUIRE_INIT();
if (opacity != opacity || opacity < 0.f || opacity > 1.f)
{
_glfwInputError(GLFW_INVALID_VALUE, "Invalid window opacity %f", opacity);
return;
}
_glfwPlatformSetWindowOpacity(window, opacity);
}
GLFWAPI void glfwIconifyWindow(GLFWwindow* handle) GLFWAPI void glfwIconifyWindow(GLFWwindow* handle)
{ {
_GLFWwindow* window = (_GLFWwindow*) handle; _GLFWwindow* window = (_GLFWwindow*) handle;
@ -675,6 +721,16 @@ GLFWAPI void glfwShowWindow(GLFWwindow* handle)
_glfwPlatformFocusWindow(window); _glfwPlatformFocusWindow(window);
} }
GLFWAPI void glfwRequestWindowAttention(GLFWwindow* handle)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL);
_GLFW_REQUIRE_INIT();
_glfwPlatformRequestWindowAttention(window);
}
GLFWAPI void glfwHideWindow(GLFWwindow* handle) GLFWAPI void glfwHideWindow(GLFWwindow* handle)
{ {
_GLFWwindow* window = (_GLFWwindow*) handle; _GLFWwindow* window = (_GLFWwindow*) handle;
@ -715,6 +771,8 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib)
return _glfwPlatformWindowVisible(window); return _glfwPlatformWindowVisible(window);
case GLFW_MAXIMIZED: case GLFW_MAXIMIZED:
return _glfwPlatformWindowMaximized(window); return _glfwPlatformWindowMaximized(window);
case GLFW_TRANSPARENT_FRAMEBUFFER:
return _glfwPlatformFramebufferTransparent(window);
case GLFW_RESIZABLE: case GLFW_RESIZABLE:
return window->resizable; return window->resizable;
case GLFW_DECORATED: case GLFW_DECORATED:
@ -747,7 +805,7 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib)
return window->context.noerror; return window->context.noerror;
} }
_glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute %i", attrib); _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib);
return 0; return 0;
} }
@ -760,41 +818,37 @@ GLFWAPI void glfwSetWindowAttrib(GLFWwindow* handle, int attrib, int value)
value = value ? GLFW_TRUE : GLFW_FALSE; value = value ? GLFW_TRUE : GLFW_FALSE;
switch (attrib) if (attrib == GLFW_AUTO_ICONIFY)
{ window->autoIconify = value;
case GLFW_RESIZABLE: else if (attrib == GLFW_RESIZABLE)
if (window->resizable != value)
{ {
if (window->resizable == value)
return;
window->resizable = value; window->resizable = value;
if (!window->monitor) if (!window->monitor)
_glfwPlatformSetWindowResizable(window, value); _glfwPlatformSetWindowResizable(window, value);
} }
else if (attrib == GLFW_DECORATED)
{
if (window->decorated == value)
return; return;
case GLFW_DECORATED:
if (window->decorated != value)
{
window->decorated = value; window->decorated = value;
if (!window->monitor) if (!window->monitor)
_glfwPlatformSetWindowDecorated(window, value); _glfwPlatformSetWindowDecorated(window, value);
} }
else if (attrib == GLFW_FLOATING)
{
if (window->floating == value)
return; return;
case GLFW_FLOATING:
if (window->floating != value)
{
window->floating = value; window->floating = value;
if (!window->monitor) if (!window->monitor)
_glfwPlatformSetWindowFloating(window, value); _glfwPlatformSetWindowFloating(window, value);
} }
return; else
_glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib);
case GLFW_AUTO_ICONIFY:
window->autoIconify = value;
return;
}
_glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute %i", attrib);
} }
GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* handle) GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* handle)

View File

@ -26,6 +26,7 @@
#include "internal.h" #include "internal.h"
#include <assert.h>
#include <linux/input.h> #include <linux/input.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -109,6 +110,8 @@ static void pointerHandleButton(void* data,
if (!window) if (!window)
return; return;
_glfw.wl.pointerSerial = serial;
/* Makes left, right and middle 0, 1 and 2. Overall order follows evdev /* Makes left, right and middle 0, 1 and 2. Overall order follows evdev
* codes. */ * codes. */
glfwButton = button - BTN_LEFT; glfwButton = button - BTN_LEFT;
@ -150,6 +153,7 @@ static void pointerHandleAxis(void* data,
y = wl_fixed_to_double(value) * scrollFactor; y = wl_fixed_to_double(value) * scrollFactor;
break; break;
default: default:
assert(GLFW_FALSE);
break; break;
} }
@ -172,8 +176,12 @@ static void keyboardHandleKeymap(void* data,
{ {
struct xkb_keymap* keymap; struct xkb_keymap* keymap;
struct xkb_state* state; struct xkb_state* state;
#ifdef HAVE_XKBCOMMON_COMPOSE_H
struct xkb_compose_table* composeTable; struct xkb_compose_table* composeTable;
struct xkb_compose_state* composeState; struct xkb_compose_state* composeState;
#endif
char* mapStr; char* mapStr;
const char* locale; const char* locale;
@ -221,6 +229,7 @@ static void keyboardHandleKeymap(void* data,
if (!locale) if (!locale)
locale = "C"; locale = "C";
#ifdef HAVE_XKBCOMMON_COMPOSE_H
composeTable = composeTable =
xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale, xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale,
XKB_COMPOSE_COMPILE_NO_FLAGS); XKB_COMPOSE_COMPILE_NO_FLAGS);
@ -240,6 +249,7 @@ static void keyboardHandleKeymap(void* data,
_glfwInputError(GLFW_PLATFORM_ERROR, _glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Failed to create XKB compose table"); "Wayland: Failed to create XKB compose table");
} }
#endif
xkb_keymap_unref(_glfw.wl.xkb.keymap); xkb_keymap_unref(_glfw.wl.xkb.keymap);
xkb_state_unref(_glfw.wl.xkb.state); xkb_state_unref(_glfw.wl.xkb.state);
@ -254,6 +264,10 @@ static void keyboardHandleKeymap(void* data,
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift"); 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift");
_glfw.wl.xkb.superMask = _glfw.wl.xkb.superMask =
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4"); 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4");
_glfw.wl.xkb.capsLockMask =
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Lock");
_glfw.wl.xkb.numLockMask =
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod2");
} }
static void keyboardHandleEnter(void* data, static void keyboardHandleEnter(void* data,
@ -290,9 +304,10 @@ static int toGLFWKeyCode(uint32_t key)
return GLFW_KEY_UNKNOWN; return GLFW_KEY_UNKNOWN;
} }
#ifdef HAVE_XKBCOMMON_COMPOSE_H
static xkb_keysym_t composeSymbol(xkb_keysym_t sym) static xkb_keysym_t composeSymbol(xkb_keysym_t sym)
{ {
if (sym == XKB_KEY_NoSymbol) if (sym == XKB_KEY_NoSymbol || !_glfw.wl.xkb.composeState)
return sym; return sym;
if (xkb_compose_state_feed(_glfw.wl.xkb.composeState, sym) if (xkb_compose_state_feed(_glfw.wl.xkb.composeState, sym)
!= XKB_COMPOSE_FEED_ACCEPTED) != XKB_COMPOSE_FEED_ACCEPTED)
@ -309,6 +324,7 @@ static xkb_keysym_t composeSymbol(xkb_keysym_t sym)
return sym; return sym;
} }
} }
#endif
static void inputChar(_GLFWwindow* window, uint32_t key) static void inputChar(_GLFWwindow* window, uint32_t key)
{ {
@ -322,7 +338,11 @@ static void inputChar(_GLFWwindow* window, uint32_t key)
if (numSyms == 1) if (numSyms == 1)
{ {
#ifdef HAVE_XKBCOMMON_COMPOSE_H
sym = composeSymbol(syms[0]); sym = composeSymbol(syms[0]);
#else
sym = syms[0];
#endif
cp = _glfwKeySym2Unicode(sym); cp = _glfwKeySym2Unicode(sym);
if (cp != -1) if (cp != -1)
{ {
@ -393,6 +413,10 @@ static void keyboardHandleModifiers(void* data,
modifiers |= GLFW_MOD_SHIFT; modifiers |= GLFW_MOD_SHIFT;
if (mask & _glfw.wl.xkb.superMask) if (mask & _glfw.wl.xkb.superMask)
modifiers |= GLFW_MOD_SUPER; modifiers |= GLFW_MOD_SUPER;
if (mask & _glfw.wl.xkb.capsLockMask)
modifiers |= GLFW_MOD_CAPS_LOCK;
if (mask & _glfw.wl.xkb.numLockMask)
modifiers |= GLFW_MOD_NUM_LOCK;
_glfw.wl.xkb.modifiers = modifiers; _glfw.wl.xkb.modifiers = modifiers;
} }
@ -485,6 +509,13 @@ static void registryHandleGlobal(void* data,
&zwp_pointer_constraints_v1_interface, &zwp_pointer_constraints_v1_interface,
1); 1);
} }
else if (strcmp(interface, "zwp_idle_inhibit_manager_v1") == 0)
{
_glfw.wl.idleInhibitManager =
wl_registry_bind(registry, name,
&zwp_idle_inhibit_manager_v1_interface,
1);
}
} }
static void registryHandleGlobalRemove(void *data, static void registryHandleGlobalRemove(void *data,
@ -640,6 +671,52 @@ static void createKeyTables(void)
int _glfwPlatformInit(void) int _glfwPlatformInit(void)
{ {
_glfw.wl.xkb.handle = dlopen("libxkbcommon.so.0", RTLD_LAZY | RTLD_GLOBAL);
if (!_glfw.wl.xkb.handle)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Failed to open libxkbcommon.");
return GLFW_FALSE;
}
_glfw.wl.xkb.context_new = (PFN_xkb_context_new)
dlsym(_glfw.wl.xkb.handle, "xkb_context_new");
_glfw.wl.xkb.context_unref = (PFN_xkb_context_unref)
dlsym(_glfw.wl.xkb.handle, "xkb_context_unref");
_glfw.wl.xkb.keymap_new_from_string = (PFN_xkb_keymap_new_from_string)
dlsym(_glfw.wl.xkb.handle, "xkb_keymap_new_from_string");
_glfw.wl.xkb.keymap_unref = (PFN_xkb_keymap_unref)
dlsym(_glfw.wl.xkb.handle, "xkb_keymap_unref");
_glfw.wl.xkb.keymap_mod_get_index = (PFN_xkb_keymap_mod_get_index)
dlsym(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_index");
_glfw.wl.xkb.state_new = (PFN_xkb_state_new)
dlsym(_glfw.wl.xkb.handle, "xkb_state_new");
_glfw.wl.xkb.state_unref = (PFN_xkb_state_unref)
dlsym(_glfw.wl.xkb.handle, "xkb_state_unref");
_glfw.wl.xkb.state_key_get_syms = (PFN_xkb_state_key_get_syms)
dlsym(_glfw.wl.xkb.handle, "xkb_state_key_get_syms");
_glfw.wl.xkb.state_update_mask = (PFN_xkb_state_update_mask)
dlsym(_glfw.wl.xkb.handle, "xkb_state_update_mask");
_glfw.wl.xkb.state_serialize_mods = (PFN_xkb_state_serialize_mods)
dlsym(_glfw.wl.xkb.handle, "xkb_state_serialize_mods");
#ifdef HAVE_XKBCOMMON_COMPOSE_H
_glfw.wl.xkb.compose_table_new_from_locale = (PFN_xkb_compose_table_new_from_locale)
dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_new_from_locale");
_glfw.wl.xkb.compose_table_unref = (PFN_xkb_compose_table_unref)
dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_unref");
_glfw.wl.xkb.compose_state_new = (PFN_xkb_compose_state_new)
dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_new");
_glfw.wl.xkb.compose_state_unref = (PFN_xkb_compose_state_unref)
dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_unref");
_glfw.wl.xkb.compose_state_feed = (PFN_xkb_compose_state_feed)
dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_feed");
_glfw.wl.xkb.compose_state_get_status = (PFN_xkb_compose_state_get_status)
dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_status");
_glfw.wl.xkb.compose_state_get_one_sym = (PFN_xkb_compose_state_get_one_sym)
dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_one_sym");
#endif
_glfw.wl.display = wl_display_connect(NULL); _glfw.wl.display = wl_display_connect(NULL);
if (!_glfw.wl.display) if (!_glfw.wl.display)
{ {
@ -667,9 +744,6 @@ int _glfwPlatformInit(void)
// Sync so we got all initial output events // Sync so we got all initial output events
wl_display_roundtrip(_glfw.wl.display); wl_display_roundtrip(_glfw.wl.display);
if (!_glfwInitThreadLocalStoragePOSIX())
return GLFW_FALSE;
if (!_glfwInitJoysticksLinux()) if (!_glfwInitJoysticksLinux())
return GLFW_FALSE; return GLFW_FALSE;
@ -695,13 +769,18 @@ void _glfwPlatformTerminate(void)
{ {
_glfwTerminateEGL(); _glfwTerminateEGL();
_glfwTerminateJoysticksLinux(); _glfwTerminateJoysticksLinux();
_glfwTerminateThreadLocalStoragePOSIX();
#ifdef HAVE_XKBCOMMON_COMPOSE_H
xkb_compose_state_unref(_glfw.wl.xkb.composeState); xkb_compose_state_unref(_glfw.wl.xkb.composeState);
#endif
xkb_keymap_unref(_glfw.wl.xkb.keymap); xkb_keymap_unref(_glfw.wl.xkb.keymap);
xkb_state_unref(_glfw.wl.xkb.state); xkb_state_unref(_glfw.wl.xkb.state);
xkb_context_unref(_glfw.wl.xkb.context); xkb_context_unref(_glfw.wl.xkb.context);
dlclose(_glfw.wl.xkb.handle);
_glfw.wl.xkb.handle = NULL;
if (_glfw.wl.cursorTheme) if (_glfw.wl.cursorTheme)
wl_cursor_theme_destroy(_glfw.wl.cursorTheme); wl_cursor_theme_destroy(_glfw.wl.cursorTheme);
if (_glfw.wl.cursorSurface) if (_glfw.wl.cursorSurface)
@ -722,6 +801,8 @@ void _glfwPlatformTerminate(void)
zwp_relative_pointer_manager_v1_destroy(_glfw.wl.relativePointerManager); zwp_relative_pointer_manager_v1_destroy(_glfw.wl.relativePointerManager);
if (_glfw.wl.pointerConstraints) if (_glfw.wl.pointerConstraints)
zwp_pointer_constraints_v1_destroy(_glfw.wl.pointerConstraints); zwp_pointer_constraints_v1_destroy(_glfw.wl.pointerConstraints);
if (_glfw.wl.idleInhibitManager)
zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idleInhibitManager);
if (_glfw.wl.registry) if (_glfw.wl.registry)
wl_registry_destroy(_glfw.wl.registry); wl_registry_destroy(_glfw.wl.registry);
if (_glfw.wl.display) if (_glfw.wl.display)
@ -739,7 +820,7 @@ const char* _glfwPlatformGetVersionString(void)
#else #else
" gettimeofday" " gettimeofday"
#endif #endif
" /dev/js" " evdev"
#if defined(_GLFW_BUILD_DLL) #if defined(_GLFW_BUILD_DLL)
" shared" " shared"
#endif #endif

View File

@ -153,6 +153,15 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
*ypos = monitor->wl.y; *ypos = monitor->wl.y;
} }
void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
float* xscale, float* yscale)
{
if (xscale)
*xscale = (float) monitor->wl.scale;
if (yscale)
*yscale = (float) monitor->wl.scale;
}
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found)
{ {
*found = monitor->modeCount; *found = monitor->modeCount;

View File

@ -24,12 +24,11 @@
// //
//======================================================================== //========================================================================
#ifndef _glfw3_wayland_platform_h_
#define _glfw3_wayland_platform_h_
#include <wayland-client.h> #include <wayland-client.h>
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
#ifdef HAVE_XKBCOMMON_COMPOSE_H
#include <xkbcommon/xkbcommon-compose.h> #include <xkbcommon/xkbcommon-compose.h>
#endif
#include <dlfcn.h> #include <dlfcn.h>
typedef VkFlags VkWaylandSurfaceCreateFlagsKHR; typedef VkFlags VkWaylandSurfaceCreateFlagsKHR;
@ -46,7 +45,7 @@ typedef struct VkWaylandSurfaceCreateInfoKHR
typedef VkResult (APIENTRY *PFN_vkCreateWaylandSurfaceKHR)(VkInstance,const VkWaylandSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); typedef VkResult (APIENTRY *PFN_vkCreateWaylandSurfaceKHR)(VkInstance,const VkWaylandSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*);
typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)(VkPhysicalDevice,uint32_t,struct wl_display*); typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)(VkPhysicalDevice,uint32_t,struct wl_display*);
#include "posix_tls.h" #include "posix_thread.h"
#include "posix_time.h" #include "posix_time.h"
#include "linux_joystick.h" #include "linux_joystick.h"
#include "xkb_unicode.h" #include "xkb_unicode.h"
@ -55,6 +54,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
#include "wayland-relative-pointer-unstable-v1-client-protocol.h" #include "wayland-relative-pointer-unstable-v1-client-protocol.h"
#include "wayland-pointer-constraints-unstable-v1-client-protocol.h" #include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
#include "wayland-idle-inhibit-unstable-v1-client-protocol.h"
#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)
#define _glfw_dlclose(handle) dlclose(handle) #define _glfw_dlclose(handle) dlclose(handle)
@ -71,6 +71,44 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
#define _GLFW_PLATFORM_CONTEXT_STATE #define _GLFW_PLATFORM_CONTEXT_STATE
#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE #define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE
typedef struct xkb_context* (* PFN_xkb_context_new)(enum xkb_context_flags);
typedef void (* PFN_xkb_context_unref)(struct xkb_context*);
typedef struct xkb_keymap* (* PFN_xkb_keymap_new_from_string)(struct xkb_context*, const char*, enum xkb_keymap_format, enum xkb_keymap_compile_flags);
typedef void (* PFN_xkb_keymap_unref)(struct xkb_keymap*);
typedef xkb_mod_index_t (* PFN_xkb_keymap_mod_get_index)(struct xkb_keymap*, const char*);
typedef struct xkb_state* (* PFN_xkb_state_new)(struct xkb_keymap*);
typedef void (* PFN_xkb_state_unref)(struct xkb_state*);
typedef int (* PFN_xkb_state_key_get_syms)(struct xkb_state*, xkb_keycode_t, const xkb_keysym_t**);
typedef enum xkb_state_component (* PFN_xkb_state_update_mask)(struct xkb_state*, xkb_mod_mask_t, xkb_mod_mask_t, xkb_mod_mask_t, xkb_layout_index_t, xkb_layout_index_t, xkb_layout_index_t);
typedef xkb_mod_mask_t (* PFN_xkb_state_serialize_mods)(struct xkb_state*, enum xkb_state_component);
#define xkb_context_new _glfw.wl.xkb.context_new
#define xkb_context_unref _glfw.wl.xkb.context_unref
#define xkb_keymap_new_from_string _glfw.wl.xkb.keymap_new_from_string
#define xkb_keymap_unref _glfw.wl.xkb.keymap_unref
#define xkb_keymap_mod_get_index _glfw.wl.xkb.keymap_mod_get_index
#define xkb_state_new _glfw.wl.xkb.state_new
#define xkb_state_unref _glfw.wl.xkb.state_unref
#define xkb_state_key_get_syms _glfw.wl.xkb.state_key_get_syms
#define xkb_state_update_mask _glfw.wl.xkb.state_update_mask
#define xkb_state_serialize_mods _glfw.wl.xkb.state_serialize_mods
#ifdef HAVE_XKBCOMMON_COMPOSE_H
typedef struct xkb_compose_table* (* PFN_xkb_compose_table_new_from_locale)(struct xkb_context*, const char*, enum xkb_compose_compile_flags);
typedef void (* PFN_xkb_compose_table_unref)(struct xkb_compose_table*);
typedef struct xkb_compose_state* (* PFN_xkb_compose_state_new)(struct xkb_compose_table*, enum xkb_compose_state_flags);
typedef void (* PFN_xkb_compose_state_unref)(struct xkb_compose_state*);
typedef enum xkb_compose_feed_result (* PFN_xkb_compose_state_feed)(struct xkb_compose_state*, xkb_keysym_t);
typedef enum xkb_compose_status (* PFN_xkb_compose_state_get_status)(struct xkb_compose_state*);
typedef xkb_keysym_t (* PFN_xkb_compose_state_get_one_sym)(struct xkb_compose_state*);
#define xkb_compose_table_new_from_locale _glfw.wl.xkb.compose_table_new_from_locale
#define xkb_compose_table_unref _glfw.wl.xkb.compose_table_unref
#define xkb_compose_state_new _glfw.wl.xkb.compose_state_new
#define xkb_compose_state_unref _glfw.wl.xkb.compose_state_unref
#define xkb_compose_state_feed _glfw.wl.xkb.compose_state_feed
#define xkb_compose_state_get_status _glfw.wl.xkb.compose_state_get_status
#define xkb_compose_state_get_one_sym _glfw.wl.xkb.compose_state_get_one_sym
#endif
// Wayland-specific per-window data // Wayland-specific per-window data
// //
@ -79,6 +117,7 @@ typedef struct _GLFWwindowWayland
int width, height; int width, height;
GLFWbool visible; GLFWbool visible;
GLFWbool maximized; GLFWbool maximized;
GLFWbool transparent;
struct wl_surface* surface; struct wl_surface* surface;
struct wl_egl_window* native; struct wl_egl_window* native;
struct wl_shell_surface* shellSurface; struct wl_shell_surface* shellSurface;
@ -100,6 +139,9 @@ typedef struct _GLFWwindowWayland
struct zwp_relative_pointer_v1* relativePointer; struct zwp_relative_pointer_v1* relativePointer;
struct zwp_locked_pointer_v1* lockedPointer; struct zwp_locked_pointer_v1* lockedPointer;
} pointerLock; } pointerLock;
struct zwp_idle_inhibitor_v1* idleInhibitor;
} _GLFWwindowWayland; } _GLFWwindowWayland;
// Wayland-specific global data // Wayland-specific global data
@ -116,6 +158,7 @@ typedef struct _GLFWlibraryWayland
struct wl_keyboard* keyboard; struct wl_keyboard* keyboard;
struct zwp_relative_pointer_manager_v1* relativePointerManager; struct zwp_relative_pointer_manager_v1* relativePointerManager;
struct zwp_pointer_constraints_v1* pointerConstraints; struct zwp_pointer_constraints_v1* pointerConstraints;
struct zwp_idle_inhibit_manager_v1* idleInhibitManager;
int compositorVersion; int compositorVersion;
@ -127,15 +170,43 @@ typedef struct _GLFWlibraryWayland
short int scancodes[GLFW_KEY_LAST + 1]; short int scancodes[GLFW_KEY_LAST + 1];
struct { struct {
void* handle;
struct xkb_context* context; struct xkb_context* context;
struct xkb_keymap* keymap; struct xkb_keymap* keymap;
struct xkb_state* state; struct xkb_state* state;
#ifdef HAVE_XKBCOMMON_COMPOSE_H
struct xkb_compose_state* composeState; struct xkb_compose_state* composeState;
#endif
xkb_mod_mask_t controlMask; xkb_mod_mask_t controlMask;
xkb_mod_mask_t altMask; xkb_mod_mask_t altMask;
xkb_mod_mask_t shiftMask; xkb_mod_mask_t shiftMask;
xkb_mod_mask_t superMask; xkb_mod_mask_t superMask;
xkb_mod_mask_t capsLockMask;
xkb_mod_mask_t numLockMask;
unsigned int modifiers; unsigned int modifiers;
PFN_xkb_context_new context_new;
PFN_xkb_context_unref context_unref;
PFN_xkb_keymap_new_from_string keymap_new_from_string;
PFN_xkb_keymap_unref keymap_unref;
PFN_xkb_keymap_mod_get_index keymap_mod_get_index;
PFN_xkb_state_new state_new;
PFN_xkb_state_unref state_unref;
PFN_xkb_state_key_get_syms state_key_get_syms;
PFN_xkb_state_update_mask state_update_mask;
PFN_xkb_state_serialize_mods state_serialize_mods;
#ifdef HAVE_XKBCOMMON_COMPOSE_H
PFN_xkb_compose_table_new_from_locale compose_table_new_from_locale;
PFN_xkb_compose_table_unref compose_table_unref;
PFN_xkb_compose_state_new compose_state_new;
PFN_xkb_compose_state_unref compose_state_unref;
PFN_xkb_compose_state_feed compose_state_feed;
PFN_xkb_compose_state_get_status compose_state_get_status;
PFN_xkb_compose_state_get_one_sym compose_state_get_one_sym;
#endif
} xkb; } xkb;
_GLFWwindow* pointerFocus; _GLFWwindow* pointerFocus;
@ -169,4 +240,3 @@ typedef struct _GLFWcursorWayland
void _glfwAddOutputWayland(uint32_t name, uint32_t version); void _glfwAddOutputWayland(uint32_t name, uint32_t version);
#endif // _glfw3_wayland_platform_h_

View File

@ -189,6 +189,24 @@ static void setOpaqueRegion(_GLFWwindow* window)
wl_region_destroy(region); wl_region_destroy(region);
} }
static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable)
{
if (enable && !window->wl.idleInhibitor && _glfw.wl.idleInhibitManager)
{
window->wl.idleInhibitor =
zwp_idle_inhibit_manager_v1_create_inhibitor(
_glfw.wl.idleInhibitManager, window->wl.surface);
if (!window->wl.idleInhibitor)
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Idle inhibitor creation failed");
}
else if (!enable && window->wl.idleInhibitor)
{
zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor);
window->wl.idleInhibitor = NULL;
}
}
static GLFWbool createSurface(_GLFWwindow* window, static GLFWbool createSurface(_GLFWwindow* window,
const _GLFWwndconfig* wndconfig) const _GLFWwndconfig* wndconfig)
{ {
@ -212,7 +230,7 @@ static GLFWbool createSurface(_GLFWwindow* window,
window->wl.height = wndconfig->height; window->wl.height = wndconfig->height;
window->wl.scale = 1; window->wl.scale = 1;
// TODO: make this optional once issue #197 is fixed. if (!window->wl.transparent)
setOpaqueRegion(window); setOpaqueRegion(window);
return GLFW_TRUE; return GLFW_TRUE;
@ -239,14 +257,17 @@ static GLFWbool createShellSurface(_GLFWwindow* window)
WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
0, 0,
window->monitor->wl.output); window->monitor->wl.output);
setIdleInhibitor(window, GLFW_TRUE);
} }
else if (window->wl.maximized) else if (window->wl.maximized)
{ {
wl_shell_surface_set_maximized(window->wl.shellSurface, NULL); wl_shell_surface_set_maximized(window->wl.shellSurface, NULL);
setIdleInhibitor(window, GLFW_FALSE);
} }
else else
{ {
wl_shell_surface_set_toplevel(window->wl.shellSurface); wl_shell_surface_set_toplevel(window->wl.shellSurface);
setIdleInhibitor(window, GLFW_FALSE);
} }
wl_surface_commit(window->wl.surface); wl_surface_commit(window->wl.surface);
@ -390,6 +411,8 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig, const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig) const _GLFWfbconfig* fbconfig)
{ {
window->wl.transparent = fbconfig->transparent;
if (!createSurface(window, wndconfig)) if (!createSurface(window, wndconfig))
return GLFW_FALSE; return GLFW_FALSE;
@ -450,6 +473,9 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
_glfwInputWindowFocus(window, GLFW_FALSE); _glfwInputWindowFocus(window, GLFW_FALSE);
} }
if (window->wl.idleInhibitor)
zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor);
if (window->context.destroy) if (window->context.destroy)
window->context.destroy(window); window->context.destroy(window);
@ -514,6 +540,7 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
window->wl.width = width; window->wl.width = width;
window->wl.height = height; window->wl.height = height;
wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0); wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
if (!window->wl.transparent)
setOpaqueRegion(window); setOpaqueRegion(window);
_glfwInputFramebufferSize(window, scaledWidth, scaledHeight); _glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
} }
@ -547,6 +574,15 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
// implemented, but for now just leave everything as 0. // implemented, but for now just leave everything as 0.
} }
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
float* xscale, float* yscale)
{
if (xscale)
*xscale = (float) window->wl.scale;
if (yscale)
*yscale = (float) window->wl.scale;
}
void _glfwPlatformIconifyWindow(_GLFWwindow* window) void _glfwPlatformIconifyWindow(_GLFWwindow* window)
{ {
// TODO: move to xdg_shell instead of wl_shell. // TODO: move to xdg_shell instead of wl_shell.
@ -599,6 +635,13 @@ void _glfwPlatformHideWindow(_GLFWwindow* window)
} }
} }
void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
{
// TODO
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Window attention request not implemented yet");
}
void _glfwPlatformFocusWindow(_GLFWwindow* window) void _glfwPlatformFocusWindow(_GLFWwindow* window)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, _glfwInputError(GLFW_PLATFORM_ERROR,
@ -618,12 +661,14 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
refreshRate * 1000, // Convert Hz to mHz. refreshRate * 1000, // Convert Hz to mHz.
monitor->wl.output); monitor->wl.output);
setIdleInhibitor(window, GLFW_TRUE);
} }
else else
{ {
wl_shell_surface_set_toplevel(window->wl.shellSurface); wl_shell_surface_set_toplevel(window->wl.shellSurface);
setIdleInhibitor(window, GLFW_FALSE);
} }
_glfwInputWindowMonitorChange(window, monitor); _glfwInputWindowMonitor(window, monitor);
} }
int _glfwPlatformWindowFocused(_GLFWwindow* window) int _glfwPlatformWindowFocused(_GLFWwindow* window)
@ -647,6 +692,11 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window)
return window->wl.maximized; return window->wl.maximized;
} }
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
{
return window->wl.transparent;
}
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
{ {
// TODO // TODO
@ -668,6 +718,15 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
"Wayland: Window attribute setting not implemented yet"); "Wayland: Window attribute setting not implemented yet");
} }
float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
{
return 1.f;
}
void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
{
}
void _glfwPlatformPollEvents(void) void _glfwPlatformPollEvents(void)
{ {
handleEvents(0); handleEvents(0);
@ -714,7 +773,7 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
_glfwPlatformSetCursor(window, window->wl.currentCursor); _glfwPlatformSetCursor(window, window->wl.currentCursor);
} }
const char* _glfwPlatformGetKeyName(int key, int scancode) const char* _glfwPlatformGetScancodeName(int scancode)
{ {
// TODO // TODO
return NULL; return NULL;
@ -984,14 +1043,14 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
} }
} }
void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) void _glfwPlatformSetClipboardString(const char* string)
{ {
// TODO // TODO
_glfwInputError(GLFW_PLATFORM_ERROR, _glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Clipboard setting not implemented yet"); "Wayland: Clipboard setting not implemented yet");
} }
const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) const char* _glfwPlatformGetClipboardString(void)
{ {
// TODO // TODO
_glfwInputError(GLFW_PLATFORM_ERROR, _glfwInputError(GLFW_PLATFORM_ERROR,
@ -1012,7 +1071,8 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
VkPhysicalDevice device, VkPhysicalDevice device,
uint32_t queuefamily) uint32_t queuefamily)
{ {
PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR = PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
vkGetPhysicalDeviceWaylandPresentationSupportKHR =
(PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR) (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)
vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR"); vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR");
if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR) if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR)

View File

@ -237,8 +237,8 @@ static void createKeyTables(void)
if (_glfw.x11.xkb.available) if (_glfw.x11.xkb.available)
{ {
// Use XKB to determine physical key locations independently of the current // Use XKB to determine physical key locations independently of the
// keyboard layout // current keyboard layout
char name[XkbKeyNameLength + 1]; char name[XkbKeyNameLength + 1];
XkbDescPtr desc = XkbGetMap(_glfw.x11.display, 0, XkbUseCoreKbd); XkbDescPtr desc = XkbGetMap(_glfw.x11.display, 0, XkbUseCoreKbd);
@ -438,6 +438,8 @@ static void detectEWMH(void)
getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_VERT"); getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_VERT");
_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ =
getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_HORZ"); getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_HORZ");
_glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION =
getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_DEMANDS_ATTENTION");
_glfw.x11.NET_WM_FULLSCREEN_MONITORS = _glfw.x11.NET_WM_FULLSCREEN_MONITORS =
getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_FULLSCREEN_MONITORS"); getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_FULLSCREEN_MONITORS");
_glfw.x11.NET_WM_WINDOW_TYPE = _glfw.x11.NET_WM_WINDOW_TYPE =
@ -455,7 +457,7 @@ static void detectEWMH(void)
XFree(supportedAtoms); XFree(supportedAtoms);
} }
// Initialize X11 display and look for supported X11 extensions // Look for and initialize supported X11 extensions
// //
static GLFWbool initExtensions(void) static GLFWbool initExtensions(void)
{ {
@ -477,7 +479,7 @@ static GLFWbool initExtensions(void)
&_glfw.x11.vidmode.errorBase); &_glfw.x11.vidmode.errorBase);
} }
_glfw.x11.xi.handle = dlopen("libXi.so", RTLD_LAZY | RTLD_GLOBAL); _glfw.x11.xi.handle = dlopen("libXi.so.6", RTLD_LAZY | RTLD_GLOBAL);
if (_glfw.x11.xi.handle) if (_glfw.x11.xi.handle)
{ {
_glfw.x11.xi.QueryVersion = (PFN_XIQueryVersion) _glfw.x11.xi.QueryVersion = (PFN_XIQueryVersion)
@ -503,7 +505,46 @@ static GLFWbool initExtensions(void)
} }
} }
// Check for RandR extension _glfw.x11.randr.handle = dlopen("libXrandr.so.2", RTLD_LAZY | RTLD_GLOBAL);
if (_glfw.x11.randr.handle)
{
_glfw.x11.randr.AllocGamma = (PFN_XRRAllocGamma)
dlsym(_glfw.x11.randr.handle, "XRRAllocGamma");
_glfw.x11.randr.FreeGamma = (PFN_XRRFreeGamma)
dlsym(_glfw.x11.randr.handle, "XRRFreeGamma");
_glfw.x11.randr.FreeCrtcInfo = (PFN_XRRFreeCrtcInfo)
dlsym(_glfw.x11.randr.handle, "XRRFreeCrtcInfo");
_glfw.x11.randr.FreeGamma = (PFN_XRRFreeGamma)
dlsym(_glfw.x11.randr.handle, "XRRFreeGamma");
_glfw.x11.randr.FreeOutputInfo = (PFN_XRRFreeOutputInfo)
dlsym(_glfw.x11.randr.handle, "XRRFreeOutputInfo");
_glfw.x11.randr.FreeScreenResources = (PFN_XRRFreeScreenResources)
dlsym(_glfw.x11.randr.handle, "XRRFreeScreenResources");
_glfw.x11.randr.GetCrtcGamma = (PFN_XRRGetCrtcGamma)
dlsym(_glfw.x11.randr.handle, "XRRGetCrtcGamma");
_glfw.x11.randr.GetCrtcGammaSize = (PFN_XRRGetCrtcGammaSize)
dlsym(_glfw.x11.randr.handle, "XRRGetCrtcGammaSize");
_glfw.x11.randr.GetCrtcInfo = (PFN_XRRGetCrtcInfo)
dlsym(_glfw.x11.randr.handle, "XRRGetCrtcInfo");
_glfw.x11.randr.GetOutputInfo = (PFN_XRRGetOutputInfo)
dlsym(_glfw.x11.randr.handle, "XRRGetOutputInfo");
_glfw.x11.randr.GetOutputPrimary = (PFN_XRRGetOutputPrimary)
dlsym(_glfw.x11.randr.handle, "XRRGetOutputPrimary");
_glfw.x11.randr.GetScreenResourcesCurrent = (PFN_XRRGetScreenResourcesCurrent)
dlsym(_glfw.x11.randr.handle, "XRRGetScreenResourcesCurrent");
_glfw.x11.randr.QueryExtension = (PFN_XRRQueryExtension)
dlsym(_glfw.x11.randr.handle, "XRRQueryExtension");
_glfw.x11.randr.QueryVersion = (PFN_XRRQueryVersion)
dlsym(_glfw.x11.randr.handle, "XRRQueryVersion");
_glfw.x11.randr.SelectInput = (PFN_XRRSelectInput)
dlsym(_glfw.x11.randr.handle, "XRRSelectInput");
_glfw.x11.randr.SetCrtcConfig = (PFN_XRRSetCrtcConfig)
dlsym(_glfw.x11.randr.handle, "XRRSetCrtcConfig");
_glfw.x11.randr.SetCrtcGamma = (PFN_XRRSetCrtcGamma)
dlsym(_glfw.x11.randr.handle, "XRRSetCrtcGamma");
_glfw.x11.randr.UpdateConfiguration = (PFN_XRRUpdateConfiguration)
dlsym(_glfw.x11.randr.handle, "XRRUpdateConfiguration");
if (XRRQueryExtension(_glfw.x11.display, if (XRRQueryExtension(_glfw.x11.display,
&_glfw.x11.randr.eventBase, &_glfw.x11.randr.eventBase,
&_glfw.x11.randr.errorBase)) &_glfw.x11.randr.errorBase))
@ -522,6 +563,7 @@ static GLFWbool initExtensions(void)
"X11: Failed to query RandR version"); "X11: Failed to query RandR version");
} }
} }
}
if (_glfw.x11.randr.available) if (_glfw.x11.randr.available)
{ {
@ -530,30 +572,48 @@ static GLFWbool initExtensions(void)
if (!sr->ncrtc || !XRRGetCrtcGammaSize(_glfw.x11.display, sr->crtcs[0])) if (!sr->ncrtc || !XRRGetCrtcGammaSize(_glfw.x11.display, sr->crtcs[0]))
{ {
// This is either a headless system or an older Nvidia binary driver // This is likely an older Nvidia driver with broken gamma support
// with broken gamma support // Flag it as useless and fall back to xf86vm gamma, if available
// Flag it as useless and fall back to Xf86VidMode gamma, if
// available
_glfwInputError(GLFW_PLATFORM_ERROR,
"X11: Detected broken RandR gamma ramp support");
_glfw.x11.randr.gammaBroken = GLFW_TRUE; _glfw.x11.randr.gammaBroken = GLFW_TRUE;
} }
if (!sr->ncrtc || !sr->noutput || !sr->nmode) if (!sr->ncrtc)
{ {
// This is either a headless system or broken Cygwin/X RandR // A system without CRTCs is likely a system with broken RandR
// Flag it as useless and fall back to Xlib display functions // Disable the RandR monitor path and fall back to core functions
_glfwInputError(GLFW_PLATFORM_ERROR,
"X11: Detected broken RandR monitor support");
_glfw.x11.randr.monitorBroken = GLFW_TRUE; _glfw.x11.randr.monitorBroken = GLFW_TRUE;
} }
XRRFreeScreenResources(sr); XRRFreeScreenResources(sr);
}
if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
{
XRRSelectInput(_glfw.x11.display, _glfw.x11.root, XRRSelectInput(_glfw.x11.display, _glfw.x11.root,
RROutputChangeNotifyMask); RROutputChangeNotifyMask);
} }
_glfw.x11.xcursor.handle = dlopen("libXcursor.so.1", RTLD_LAZY | RTLD_GLOBAL);
if (_glfw.x11.xcursor.handle)
{
_glfw.x11.xcursor.ImageCreate = (PFN_XcursorImageCreate)
dlsym(_glfw.x11.xcursor.handle, "XcursorImageCreate");
_glfw.x11.xcursor.ImageDestroy = (PFN_XcursorImageDestroy)
dlsym(_glfw.x11.xcursor.handle, "XcursorImageDestroy");
_glfw.x11.xcursor.ImageLoadCursor = (PFN_XcursorImageLoadCursor)
dlsym(_glfw.x11.xcursor.handle, "XcursorImageLoadCursor");
}
_glfw.x11.xinerama.handle = dlopen("libXinerama.so.1", RTLD_LAZY | RTLD_GLOBAL);
if (_glfw.x11.xinerama.handle)
{
_glfw.x11.xinerama.IsActive = (PFN_XineramaIsActive)
dlsym(_glfw.x11.xinerama.handle, "XineramaIsActive");
_glfw.x11.xinerama.QueryExtension = (PFN_XineramaQueryExtension)
dlsym(_glfw.x11.xinerama.handle, "XineramaQueryExtension");
_glfw.x11.xinerama.QueryScreens = (PFN_XineramaQueryScreens)
dlsym(_glfw.x11.xinerama.handle, "XineramaQueryScreens");
if (XineramaQueryExtension(_glfw.x11.display, if (XineramaQueryExtension(_glfw.x11.display,
&_glfw.x11.xinerama.major, &_glfw.x11.xinerama.major,
&_glfw.x11.xinerama.minor)) &_glfw.x11.xinerama.minor))
@ -561,8 +621,8 @@ static GLFWbool initExtensions(void)
if (XineramaIsActive(_glfw.x11.display)) if (XineramaIsActive(_glfw.x11.display))
_glfw.x11.xinerama.available = GLFW_TRUE; _glfw.x11.xinerama.available = GLFW_TRUE;
} }
}
// Check if Xkb is supported on this display
_glfw.x11.xkb.major = 1; _glfw.x11.xkb.major = 1;
_glfw.x11.xkb.minor = 0; _glfw.x11.xkb.minor = 0;
_glfw.x11.xkb.available = _glfw.x11.xkb.available =
@ -587,10 +647,33 @@ static GLFWbool initExtensions(void)
_glfw.x11.x11xcb.handle = dlopen("libX11-xcb.so.1", RTLD_LAZY | RTLD_GLOBAL); _glfw.x11.x11xcb.handle = dlopen("libX11-xcb.so.1", RTLD_LAZY | RTLD_GLOBAL);
if (_glfw.x11.x11xcb.handle) if (_glfw.x11.x11xcb.handle)
{ {
_glfw.x11.x11xcb.XGetXCBConnection = (PFN_XGetXCBConnection) _glfw.x11.x11xcb.GetXCBConnection = (PFN_XGetXCBConnection)
dlsym(_glfw.x11.x11xcb.handle, "XGetXCBConnection"); dlsym(_glfw.x11.x11xcb.handle, "XGetXCBConnection");
} }
_glfw.x11.xrender.handle = dlopen("libXrender.so.1", RTLD_LAZY | RTLD_GLOBAL);
if (_glfw.x11.xrender.handle)
{
_glfw.x11.xrender.QueryExtension = (PFN_XRenderQueryExtension)
dlsym(_glfw.x11.xrender.handle, "XRenderQueryExtension");
_glfw.x11.xrender.QueryVersion = (PFN_XRenderQueryVersion)
dlsym(_glfw.x11.xrender.handle, "XRenderQueryVersion");
_glfw.x11.xrender.FindVisualFormat = (PFN_XRenderFindVisualFormat)
dlsym(_glfw.x11.xrender.handle, "XRenderFindVisualFormat");
if (XRenderQueryExtension(_glfw.x11.display,
&_glfw.x11.xrender.errorBase,
&_glfw.x11.xrender.eventBase))
{
if (XRenderQueryVersion(_glfw.x11.display,
&_glfw.x11.xrender.major,
&_glfw.x11.xrender.minor))
{
_glfw.x11.xrender.available = GLFW_TRUE;
}
}
}
// Update the key code LUT // Update the key code LUT
// FIXME: We should listen to XkbMapNotify events to track changes to // FIXME: We should listen to XkbMapNotify events to track changes to
// the keyboard mapping. // the keyboard mapping.
@ -601,10 +684,7 @@ static GLFWbool initExtensions(void)
// String format atoms // String format atoms
_glfw.x11.NULL_ = XInternAtom(_glfw.x11.display, "NULL", False); _glfw.x11.NULL_ = XInternAtom(_glfw.x11.display, "NULL", False);
_glfw.x11.UTF8_STRING = _glfw.x11.UTF8_STRING = XInternAtom(_glfw.x11.display, "UTF8_STRING", False);
XInternAtom(_glfw.x11.display, "UTF8_STRING", False);
_glfw.x11.COMPOUND_STRING =
XInternAtom(_glfw.x11.display, "COMPOUND_STRING", False);
_glfw.x11.ATOM_PAIR = XInternAtom(_glfw.x11.display, "ATOM_PAIR", False); _glfw.x11.ATOM_PAIR = XInternAtom(_glfw.x11.display, "ATOM_PAIR", False);
// Custom selection property atom // Custom selection property atom
@ -614,6 +694,8 @@ static GLFWbool initExtensions(void)
// ICCCM standard clipboard atoms // ICCCM standard clipboard atoms
_glfw.x11.TARGETS = XInternAtom(_glfw.x11.display, "TARGETS", False); _glfw.x11.TARGETS = XInternAtom(_glfw.x11.display, "TARGETS", False);
_glfw.x11.MULTIPLE = XInternAtom(_glfw.x11.display, "MULTIPLE", False); _glfw.x11.MULTIPLE = XInternAtom(_glfw.x11.display, "MULTIPLE", False);
_glfw.x11.PRIMARY = XInternAtom(_glfw.x11.display, "PRIMARY", False);
_glfw.x11.INCR = XInternAtom(_glfw.x11.display, "INCR", False);
_glfw.x11.CLIPBOARD = XInternAtom(_glfw.x11.display, "CLIPBOARD", False); _glfw.x11.CLIPBOARD = XInternAtom(_glfw.x11.display, "CLIPBOARD", False);
// Clipboard manager atoms // Clipboard manager atoms
@ -629,9 +711,10 @@ static GLFWbool initExtensions(void)
_glfw.x11.XdndStatus = XInternAtom(_glfw.x11.display, "XdndStatus", False); _glfw.x11.XdndStatus = XInternAtom(_glfw.x11.display, "XdndStatus", False);
_glfw.x11.XdndActionCopy = XInternAtom(_glfw.x11.display, "XdndActionCopy", False); _glfw.x11.XdndActionCopy = XInternAtom(_glfw.x11.display, "XdndActionCopy", False);
_glfw.x11.XdndDrop = XInternAtom(_glfw.x11.display, "XdndDrop", 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.XdndFinished = XInternAtom(_glfw.x11.display, "XdndFinished", False);
_glfw.x11.XdndSelection = XInternAtom(_glfw.x11.display, "XdndSelection", False); _glfw.x11.XdndSelection = XInternAtom(_glfw.x11.display, "XdndSelection", False);
_glfw.x11.XdndTypeList = XInternAtom(_glfw.x11.display, "XdndTypeList", False);
_glfw.x11.text_uri_list = XInternAtom(_glfw.x11.display, "text/uri-list", False);
// ICCCM, EWMH and Motif window property atoms // ICCCM, EWMH and Motif window property atoms
// These can be set safely even without WM support // These can be set safely even without WM support
@ -654,21 +737,64 @@ static GLFWbool initExtensions(void)
XInternAtom(_glfw.x11.display, "_NET_WM_ICON_NAME", False); XInternAtom(_glfw.x11.display, "_NET_WM_ICON_NAME", False);
_glfw.x11.NET_WM_BYPASS_COMPOSITOR = _glfw.x11.NET_WM_BYPASS_COMPOSITOR =
XInternAtom(_glfw.x11.display, "_NET_WM_BYPASS_COMPOSITOR", False); XInternAtom(_glfw.x11.display, "_NET_WM_BYPASS_COMPOSITOR", False);
_glfw.x11.NET_WM_WINDOW_OPACITY =
XInternAtom(_glfw.x11.display, "_NET_WM_WINDOW_OPACITY", False);
_glfw.x11.MOTIF_WM_HINTS = _glfw.x11.MOTIF_WM_HINTS =
XInternAtom(_glfw.x11.display, "_MOTIF_WM_HINTS", False); XInternAtom(_glfw.x11.display, "_MOTIF_WM_HINTS", False);
// The compositing manager selection name contains the screen number
{
char name[32];
snprintf(name, sizeof(name), "_NET_WM_CM_S%u", _glfw.x11.screen);
_glfw.x11.NET_WM_CM_Sx = XInternAtom(_glfw.x11.display, name, False);
}
return GLFW_TRUE; return GLFW_TRUE;
} }
// Retrieve system content scale via folklore heuristics
//
static void getSystemContentScale(float* xscale, float* yscale)
{
// NOTE: Default to the display-wide DPI as we don't currently have a policy
// for which monitor a window is considered to be on
float xdpi = DisplayWidth(_glfw.x11.display, _glfw.x11.screen) *
25.4f / DisplayWidthMM(_glfw.x11.display, _glfw.x11.screen);
float ydpi = DisplayHeight(_glfw.x11.display, _glfw.x11.screen) *
25.4f / DisplayHeightMM(_glfw.x11.display, _glfw.x11.screen);
// NOTE: Basing the scale on Xft.dpi where available should provide the most
// consistent user experience (matches Qt, Gtk, etc), although not
// always the most accurate one
char* rms = XResourceManagerString(_glfw.x11.display);
if (rms)
{
XrmDatabase db = XrmGetStringDatabase(rms);
if (db)
{
XrmValue value;
char* type = NULL;
if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value))
{
if (type && strcmp(type, "String") == 0)
xdpi = ydpi = atof(value.addr);
}
XrmDestroyDatabase(db);
}
}
*xscale = xdpi / 96.f;
*yscale = ydpi / 96.f;
}
// Create a blank cursor for hidden and disabled cursor modes // Create a blank cursor for hidden and disabled cursor modes
// //
static Cursor createHiddenCursor(void) static Cursor createHiddenCursor(void)
{ {
unsigned char pixels[16 * 16 * 4]; unsigned char pixels[16 * 16 * 4] = { 0 };
GLFWimage image = { 16, 16, pixels }; GLFWimage image = { 16, 16, pixels };
memset(pixels, 0, sizeof(pixels));
return _glfwCreateCursorX11(&image, 0, 0); return _glfwCreateCursorX11(&image, 0, 0);
} }
@ -720,7 +846,7 @@ void _glfwReleaseErrorHandlerX11(void)
// //
void _glfwInputErrorX11(int error, const char* message) void _glfwInputErrorX11(int error, const char* message)
{ {
char buffer[8192]; char buffer[_GLFW_MESSAGE_SIZE];
XGetErrorText(_glfw.x11.display, _glfw.x11.errorCode, XGetErrorText(_glfw.x11.display, _glfw.x11.errorCode,
buffer, sizeof(buffer)); buffer, sizeof(buffer));
@ -734,6 +860,9 @@ Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot)
int i; int i;
Cursor cursor; Cursor cursor;
if (!_glfw.x11.xcursor.handle)
return None;
XcursorImage* native = XcursorImageCreate(image->width, image->height); XcursorImage* native = XcursorImageCreate(image->width, image->height);
if (native == NULL) if (native == NULL)
return None; return None;
@ -778,6 +907,7 @@ int _glfwPlatformInit(void)
#endif #endif
XInitThreads(); XInitThreads();
XrmInitialize();
_glfw.x11.display = XOpenDisplay(NULL); _glfw.x11.display = XOpenDisplay(NULL);
if (!_glfw.x11.display) if (!_glfw.x11.display)
@ -800,12 +930,15 @@ int _glfwPlatformInit(void)
_glfw.x11.screen = DefaultScreen(_glfw.x11.display); _glfw.x11.screen = DefaultScreen(_glfw.x11.display);
_glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen); _glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen);
_glfw.x11.context = XUniqueContext(); _glfw.x11.context = XUniqueContext();
_glfw.x11.helperWindowHandle = createHelperWindow();
_glfw.x11.hiddenCursorHandle = createHiddenCursor(); getSystemContentScale(&_glfw.x11.contentScaleX, &_glfw.x11.contentScaleY);
if (!initExtensions()) if (!initExtensions())
return GLFW_FALSE; return GLFW_FALSE;
_glfw.x11.helperWindowHandle = createHelperWindow();
_glfw.x11.hiddenCursorHandle = createHiddenCursor();
if (XSupportsLocale()) if (XSupportsLocale())
{ {
XSetLocaleModifiers(""); XSetLocaleModifiers("");
@ -821,9 +954,6 @@ int _glfwPlatformInit(void)
} }
} }
if (!_glfwInitThreadLocalStoragePOSIX())
return GLFW_FALSE;
#if defined(__linux__) #if defined(__linux__)
if (!_glfwInitJoysticksLinux()) if (!_glfwInitJoysticksLinux())
return GLFW_FALSE; return GLFW_FALSE;
@ -837,12 +967,6 @@ int _glfwPlatformInit(void)
void _glfwPlatformTerminate(void) void _glfwPlatformTerminate(void)
{ {
if (_glfw.x11.x11xcb.handle)
{
dlclose(_glfw.x11.x11xcb.handle);
_glfw.x11.x11xcb.handle = NULL;
}
if (_glfw.x11.helperWindowHandle) if (_glfw.x11.helperWindowHandle)
{ {
if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) == if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) ==
@ -861,6 +985,7 @@ void _glfwPlatformTerminate(void)
_glfw.x11.hiddenCursorHandle = (Cursor) 0; _glfw.x11.hiddenCursorHandle = (Cursor) 0;
} }
free(_glfw.x11.primarySelectionString);
free(_glfw.x11.clipboardString); free(_glfw.x11.clipboardString);
if (_glfw.x11.im) if (_glfw.x11.im)
@ -869,22 +994,44 @@ void _glfwPlatformTerminate(void)
_glfw.x11.im = NULL; _glfw.x11.im = NULL;
} }
_glfwTerminateEGL();
if (_glfw.x11.display) if (_glfw.x11.display)
{ {
XCloseDisplay(_glfw.x11.display); XCloseDisplay(_glfw.x11.display);
_glfw.x11.display = NULL; _glfw.x11.display = NULL;
} }
// NOTE: This needs to be done after XCloseDisplay, as libGL registers if (_glfw.x11.x11xcb.handle)
// cleanup callbacks that get called by it {
dlclose(_glfw.x11.x11xcb.handle);
_glfw.x11.x11xcb.handle = NULL;
}
if (_glfw.x11.xcursor.handle)
{
dlclose(_glfw.x11.xcursor.handle);
_glfw.x11.xcursor.handle = NULL;
}
if (_glfw.x11.randr.handle)
{
dlclose(_glfw.x11.randr.handle);
_glfw.x11.randr.handle = NULL;
}
if (_glfw.x11.xinerama.handle)
{
dlclose(_glfw.x11.xinerama.handle);
_glfw.x11.xinerama.handle = NULL;
}
// NOTE: These need to be unloaded after XCloseDisplay, as they register
// cleanup callbacks that get called by that function
_glfwTerminateEGL();
_glfwTerminateGLX(); _glfwTerminateGLX();
#if defined(__linux__) #if defined(__linux__)
_glfwTerminateJoysticksLinux(); _glfwTerminateJoysticksLinux();
#endif #endif
_glfwTerminateThreadLocalStoragePOSIX();
} }
const char* _glfwPlatformGetVersionString(void) const char* _glfwPlatformGetVersionString(void)
@ -896,7 +1043,7 @@ const char* _glfwPlatformGetVersionString(void)
" gettimeofday" " gettimeofday"
#endif #endif
#if defined(__linux__) #if defined(__linux__)
" /dev/js" " evdev"
#endif #endif
#if defined(_GLFW_BUILD_DLL) #if defined(_GLFW_BUILD_DLL)
" shared" " shared"

View File

@ -202,16 +202,8 @@ void _glfwPollMonitorsX11(void)
} }
free(disconnected); free(disconnected);
if (!_glfw.monitorCount)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"X11: RandR monitor support seems broken");
_glfw.x11.randr.monitorBroken = GLFW_TRUE;
} }
} else
if (!_glfw.monitorCount)
{ {
const int widthMM = DisplayWidthMM(_glfw.x11.display, _glfw.x11.screen); const int widthMM = DisplayWidthMM(_glfw.x11.display, _glfw.x11.screen);
const int heightMM = DisplayHeightMM(_glfw.x11.display, _glfw.x11.screen); const int heightMM = DisplayHeightMM(_glfw.x11.display, _glfw.x11.screen);
@ -345,6 +337,15 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
} }
} }
void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
float* xscale, float* yscale)
{
if (xscale)
*xscale = _glfw.x11.contentScaleX;
if (yscale)
*yscale = _glfw.x11.contentScaleY;
}
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
{ {
GLFWvidmode* result; GLFWvidmode* result;
@ -460,6 +461,13 @@ void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
{ {
if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken) if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken)
{ {
if (XRRGetCrtcGammaSize(_glfw.x11.display, monitor->x11.crtc) != ramp->size)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"X11: Gamma ramp size must match current ramp size");
return;
}
XRRCrtcGamma* gamma = XRRAllocGamma(ramp->size); XRRCrtcGamma* gamma = XRRAllocGamma(ramp->size);
memcpy(gamma->red, ramp->red, ramp->size * sizeof(unsigned short)); memcpy(gamma->red, ramp->red, ramp->size * sizeof(unsigned short));

View File

@ -25,9 +25,6 @@
// //
//======================================================================== //========================================================================
#ifndef _glfw3_x11_platform_h_
#define _glfw3_x11_platform_h_
#include <unistd.h> #include <unistd.h>
#include <signal.h> #include <signal.h>
#include <stdint.h> #include <stdint.h>
@ -50,10 +47,60 @@
// The XInput extension provides raw mouse motion input // The XInput extension provides raw mouse motion input
#include <X11/extensions/XInput2.h> #include <X11/extensions/XInput2.h>
typedef XRRCrtcGamma* (* PFN_XRRAllocGamma)(int);
typedef void (* PFN_XRRFreeCrtcInfo)(XRRCrtcInfo*);
typedef void (* PFN_XRRFreeGamma)(XRRCrtcGamma*);
typedef void (* PFN_XRRFreeOutputInfo)(XRROutputInfo*);
typedef void (* PFN_XRRFreeScreenResources)(XRRScreenResources*);
typedef XRRCrtcGamma* (* PFN_XRRGetCrtcGamma)(Display*,RRCrtc);
typedef int (* PFN_XRRGetCrtcGammaSize)(Display*,RRCrtc);
typedef XRRCrtcInfo* (* PFN_XRRGetCrtcInfo) (Display*,XRRScreenResources*,RRCrtc);
typedef XRROutputInfo* (* PFN_XRRGetOutputInfo)(Display*,XRRScreenResources*,RROutput);
typedef RROutput (* PFN_XRRGetOutputPrimary)(Display*,Window);
typedef XRRScreenResources* (* PFN_XRRGetScreenResourcesCurrent)(Display*,Window);
typedef Bool (* PFN_XRRQueryExtension)(Display*,int*,int*);
typedef Status (* PFN_XRRQueryVersion)(Display*,int*,int*);
typedef void (* PFN_XRRSelectInput)(Display*,Window,int);
typedef Status (* PFN_XRRSetCrtcConfig)(Display*,XRRScreenResources*,RRCrtc,Time,int,int,RRMode,Rotation,RROutput*,int);
typedef void (* PFN_XRRSetCrtcGamma)(Display*,RRCrtc,XRRCrtcGamma*);
typedef int (* PFN_XRRUpdateConfiguration)(XEvent*);
#define XRRAllocGamma _glfw.x11.randr.AllocGamma
#define XRRFreeCrtcInfo _glfw.x11.randr.FreeCrtcInfo
#define XRRFreeGamma _glfw.x11.randr.FreeGamma
#define XRRFreeOutputInfo _glfw.x11.randr.FreeOutputInfo
#define XRRFreeScreenResources _glfw.x11.randr.FreeScreenResources
#define XRRGetCrtcGamma _glfw.x11.randr.GetCrtcGamma
#define XRRGetCrtcGammaSize _glfw.x11.randr.GetCrtcGammaSize
#define XRRGetCrtcInfo _glfw.x11.randr.GetCrtcInfo
#define XRRGetOutputInfo _glfw.x11.randr.GetOutputInfo
#define XRRGetOutputPrimary _glfw.x11.randr.GetOutputPrimary
#define XRRGetScreenResourcesCurrent _glfw.x11.randr.GetScreenResourcesCurrent
#define XRRQueryExtension _glfw.x11.randr.QueryExtension
#define XRRQueryVersion _glfw.x11.randr.QueryVersion
#define XRRSelectInput _glfw.x11.randr.SelectInput
#define XRRSetCrtcConfig _glfw.x11.randr.SetCrtcConfig
#define XRRSetCrtcGamma _glfw.x11.randr.SetCrtcGamma
#define XRRUpdateConfiguration _glfw.x11.randr.UpdateConfiguration
typedef XcursorImage* (* PFN_XcursorImageCreate)(int,int);
typedef void (* PFN_XcursorImageDestroy)(XcursorImage*);
typedef Cursor (* PFN_XcursorImageLoadCursor)(Display*,const XcursorImage*);
#define XcursorImageCreate _glfw.x11.xcursor.ImageCreate
#define XcursorImageDestroy _glfw.x11.xcursor.ImageDestroy
#define XcursorImageLoadCursor _glfw.x11.xcursor.ImageLoadCursor
typedef Bool (* PFN_XineramaIsActive)(Display*);
typedef Bool (* PFN_XineramaQueryExtension)(Display*,int*,int*);
typedef XineramaScreenInfo* (* PFN_XineramaQueryScreens)(Display*,int*);
#define XineramaIsActive _glfw.x11.xinerama.IsActive
#define XineramaQueryExtension _glfw.x11.xinerama.QueryExtension
#define XineramaQueryScreens _glfw.x11.xinerama.QueryScreens
typedef XID xcb_window_t; typedef XID xcb_window_t;
typedef XID xcb_visualid_t; typedef XID xcb_visualid_t;
typedef struct xcb_connection_t xcb_connection_t; typedef struct xcb_connection_t xcb_connection_t;
typedef xcb_connection_t* (* PFN_XGetXCBConnection)(Display*); typedef xcb_connection_t* (* PFN_XGetXCBConnection)(Display*);
#define XGetXCBConnection _glfw.x11.x11xcb.GetXCBConnection
typedef Bool (* PFN_XF86VidModeQueryExtension)(Display*,int*,int*); typedef Bool (* PFN_XF86VidModeQueryExtension)(Display*,int*,int*);
typedef Bool (* PFN_XF86VidModeGetGammaRamp)(Display*,int,int,unsigned short*,unsigned short*,unsigned short*); typedef Bool (* PFN_XF86VidModeGetGammaRamp)(Display*,int,int,unsigned short*,unsigned short*,unsigned short*);
@ -69,6 +116,13 @@ typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int);
#define XIQueryVersion _glfw.x11.xi.QueryVersion #define XIQueryVersion _glfw.x11.xi.QueryVersion
#define XISelectEvents _glfw.x11.xi.SelectEvents #define XISelectEvents _glfw.x11.xi.SelectEvents
typedef Bool (* PFN_XRenderQueryExtension)(Display*,int*,int*);
typedef Status (* PFN_XRenderQueryVersion)(Display*dpy,int*,int*);
typedef XRenderPictFormat* (* PFN_XRenderFindVisualFormat)(Display*,Visual const*);
#define XRenderQueryExtension _glfw.x11.xrender.QueryExtension
#define XRenderQueryVersion _glfw.x11.xrender.QueryVersion
#define XRenderFindVisualFormat _glfw.x11.xrender.FindVisualFormat
typedef VkFlags VkXlibSurfaceCreateFlagsKHR; typedef VkFlags VkXlibSurfaceCreateFlagsKHR;
typedef VkFlags VkXcbSurfaceCreateFlagsKHR; typedef VkFlags VkXcbSurfaceCreateFlagsKHR;
@ -95,7 +149,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)(V
typedef VkResult (APIENTRY *PFN_vkCreateXcbSurfaceKHR)(VkInstance,const VkXcbSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); typedef VkResult (APIENTRY *PFN_vkCreateXcbSurfaceKHR)(VkInstance,const VkXcbSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*);
typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(VkPhysicalDevice,uint32_t,xcb_connection_t*,xcb_visualid_t); typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(VkPhysicalDevice,uint32_t,xcb_connection_t*,xcb_visualid_t);
#include "posix_tls.h" #include "posix_thread.h"
#include "posix_time.h" #include "posix_time.h"
#include "xkb_unicode.h" #include "xkb_unicode.h"
#include "glx_context.h" #include "glx_context.h"
@ -132,6 +186,9 @@ typedef struct _GLFWwindowX11
GLFWbool iconified; GLFWbool iconified;
GLFWbool maximized; GLFWbool maximized;
// Whether the visual supports framebuffer transparency
GLFWbool transparent;
// Cached position and size used to filter out duplicate events // Cached position and size used to filter out duplicate events
int width, height; int width, height;
int xpos, ypos; int xpos, ypos;
@ -141,8 +198,7 @@ typedef struct _GLFWwindowX11
// The last position the cursor was warped to by GLFW // The last position the cursor was warped to by GLFW
int warpCursorPosX, warpCursorPosY; int warpCursorPosX, warpCursorPosY;
// The information from the last KeyPress event // The time of the last KeyPress event
unsigned int lastKeyCode;
Time lastKeyTime; Time lastKeyTime;
} _GLFWwindowX11; } _GLFWwindowX11;
@ -155,6 +211,8 @@ typedef struct _GLFWlibraryX11
int screen; int screen;
Window root; Window root;
// System content scale
float contentScaleX, contentScaleY;
// Helper window for IPC // Helper window for IPC
Window helperWindowHandle; Window helperWindowHandle;
// Invisible cursor for hidden cursor mode // Invisible cursor for hidden cursor mode
@ -165,10 +223,12 @@ typedef struct _GLFWlibraryX11
XIM im; XIM im;
// Most recent error code received by X error handler // Most recent error code received by X error handler
int errorCode; int errorCode;
// Primary selection string (while the primary selection is owned)
char* primarySelectionString;
// Clipboard string (while the selection is owned) // Clipboard string (while the selection is owned)
char* clipboardString; char* clipboardString;
// Key name string // Key name string
char keyName[64]; char keyName[5];
// X11 keycode to GLFW key LUT // X11 keycode to GLFW key LUT
short int keycodes[256]; short int keycodes[256];
// GLFW key to X11 keycode LUT // GLFW key to X11 keycode LUT
@ -194,8 +254,11 @@ typedef struct _GLFWlibraryX11
Atom NET_WM_STATE_FULLSCREEN; Atom NET_WM_STATE_FULLSCREEN;
Atom NET_WM_STATE_MAXIMIZED_VERT; Atom NET_WM_STATE_MAXIMIZED_VERT;
Atom NET_WM_STATE_MAXIMIZED_HORZ; Atom NET_WM_STATE_MAXIMIZED_HORZ;
Atom NET_WM_STATE_DEMANDS_ATTENTION;
Atom NET_WM_BYPASS_COMPOSITOR; Atom NET_WM_BYPASS_COMPOSITOR;
Atom NET_WM_FULLSCREEN_MONITORS; Atom NET_WM_FULLSCREEN_MONITORS;
Atom NET_WM_WINDOW_OPACITY;
Atom NET_WM_CM_Sx;
Atom NET_ACTIVE_WINDOW; Atom NET_ACTIVE_WINDOW;
Atom NET_FRAME_EXTENTS; Atom NET_FRAME_EXTENTS;
Atom NET_REQUEST_FRAME_EXTENTS; Atom NET_REQUEST_FRAME_EXTENTS;
@ -208,14 +271,17 @@ typedef struct _GLFWlibraryX11
Atom XdndStatus; Atom XdndStatus;
Atom XdndActionCopy; Atom XdndActionCopy;
Atom XdndDrop; Atom XdndDrop;
Atom XdndLeave;
Atom XdndFinished; Atom XdndFinished;
Atom XdndSelection; Atom XdndSelection;
Atom XdndTypeList;
Atom text_uri_list;
// Selection (clipboard) atoms // Selection (clipboard) atoms
Atom TARGETS; Atom TARGETS;
Atom MULTIPLE; Atom MULTIPLE;
Atom INCR;
Atom CLIPBOARD; Atom CLIPBOARD;
Atom PRIMARY;
Atom CLIPBOARD_MANAGER; Atom CLIPBOARD_MANAGER;
Atom SAVE_TARGETS; Atom SAVE_TARGETS;
Atom NULL_; Atom NULL_;
@ -226,12 +292,30 @@ typedef struct _GLFWlibraryX11
struct { struct {
GLFWbool available; GLFWbool available;
void* handle;
int eventBase; int eventBase;
int errorBase; int errorBase;
int major; int major;
int minor; int minor;
GLFWbool gammaBroken; GLFWbool gammaBroken;
GLFWbool monitorBroken; GLFWbool monitorBroken;
PFN_XRRAllocGamma AllocGamma;
PFN_XRRFreeCrtcInfo FreeCrtcInfo;
PFN_XRRFreeGamma FreeGamma;
PFN_XRRFreeOutputInfo FreeOutputInfo;
PFN_XRRFreeScreenResources FreeScreenResources;
PFN_XRRGetCrtcGamma GetCrtcGamma;
PFN_XRRGetCrtcGammaSize GetCrtcGammaSize;
PFN_XRRGetCrtcInfo GetCrtcInfo;
PFN_XRRGetOutputInfo GetOutputInfo;
PFN_XRRGetOutputPrimary GetOutputPrimary;
PFN_XRRGetScreenResourcesCurrent GetScreenResourcesCurrent;
PFN_XRRQueryExtension QueryExtension;
PFN_XRRQueryVersion QueryVersion;
PFN_XRRSelectInput SelectInput;
PFN_XRRSetCrtcConfig SetCrtcConfig;
PFN_XRRSetCrtcGamma SetCrtcGamma;
PFN_XRRUpdateConfiguration UpdateConfiguration;
} randr; } randr;
struct { struct {
@ -253,18 +337,31 @@ typedef struct _GLFWlibraryX11
} saver; } saver;
struct { struct {
int version;
Window source; Window source;
Atom format;
} xdnd; } xdnd;
struct {
void* handle;
PFN_XcursorImageCreate ImageCreate;
PFN_XcursorImageDestroy ImageDestroy;
PFN_XcursorImageLoadCursor ImageLoadCursor;
} xcursor;
struct { struct {
GLFWbool available; GLFWbool available;
void* handle;
int major; int major;
int minor; int minor;
PFN_XineramaIsActive IsActive;
PFN_XineramaQueryExtension QueryExtension;
PFN_XineramaQueryScreens QueryScreens;
} xinerama; } xinerama;
struct { struct {
void* handle; void* handle;
PFN_XGetXCBConnection XGetXCBConnection; PFN_XGetXCBConnection GetXCBConnection;
} x11xcb; } x11xcb;
struct { struct {
@ -290,6 +387,18 @@ typedef struct _GLFWlibraryX11
PFN_XISelectEvents SelectEvents; PFN_XISelectEvents SelectEvents;
} xi; } xi;
struct {
GLFWbool available;
void* handle;
int major;
int minor;
int eventBase;
int errorBase;
PFN_XRenderQueryExtension QueryExtension;
PFN_XRenderQueryVersion QueryVersion;
PFN_XRenderFindVisualFormat FindVisualFormat;
} xrender;
} _GLFWlibraryX11; } _GLFWlibraryX11;
// X11-specific per-monitor data // X11-specific per-monitor data
@ -325,6 +434,7 @@ unsigned long _glfwGetWindowPropertyX11(Window window,
Atom property, Atom property,
Atom type, Atom type,
unsigned char** value); unsigned char** value);
GLFWbool _glfwIsVisualTransparentX11(Visual* visual);
void _glfwGrabErrorHandlerX11(void); void _glfwGrabErrorHandlerX11(void);
void _glfwReleaseErrorHandlerX11(void); void _glfwReleaseErrorHandlerX11(void);
@ -332,4 +442,3 @@ void _glfwInputErrorX11(int error, const char* message);
void _glfwPushSelectionToManagerX11(void); void _glfwPushSelectionToManagerX11(void);
#endif // _glfw3_x11_platform_h_

View File

@ -48,6 +48,8 @@
#define Button6 6 #define Button6 6
#define Button7 7 #define Button7 7
#define _GLFW_XDND_VERSION 5
// Wait for data to arrive using select // Wait for data to arrive using select
// This avoids blocking other threads via the per-display Xlib lock that also // This avoids blocking other threads via the per-display Xlib lock that also
@ -162,6 +164,17 @@ static Bool isFrameExtentsEvent(Display* display, XEvent* event, XPointer pointe
event->xproperty.atom == _glfw.x11.NET_FRAME_EXTENTS; event->xproperty.atom == _glfw.x11.NET_FRAME_EXTENTS;
} }
// Returns whether it is a property event for the specified selection transfer
//
static Bool isSelPropNewValueNotify(Display* display, XEvent* event, XPointer pointer)
{
XEvent* notification = (XEvent*) pointer;
return event->type == PropertyNotify &&
event->xproperty.state == PropertyNewValue &&
event->xproperty.window == notification->xselection.requestor &&
event->xproperty.atom == notification->xselection.property;
}
// Translates a GLFW standard cursor to a font cursor shape // Translates a GLFW standard cursor to a font cursor shape
// //
static int translateCursorShape(int shape) static int translateCursorShape(int shape)
@ -199,6 +212,10 @@ static int translateState(int state)
mods |= GLFW_MOD_ALT; mods |= GLFW_MOD_ALT;
if (state & Mod4Mask) if (state & Mod4Mask)
mods |= GLFW_MOD_SUPER; mods |= GLFW_MOD_SUPER;
if (state & LockMask)
mods |= GLFW_MOD_CAPS_LOCK;
if (state & Mod2Mask)
mods |= GLFW_MOD_NUM_LOCK;
return mods; return mods;
} }
@ -351,6 +368,7 @@ static void updateWindowMode(_GLFWwindow* window)
} }
// Enable compositor bypass // Enable compositor bypass
if (!window->x11.transparent)
{ {
const unsigned long value = 1; const unsigned long value = 1;
@ -389,6 +407,7 @@ static void updateWindowMode(_GLFWwindow* window)
} }
// Disable compositor bypass // Disable compositor bypass
if (!window->x11.transparent)
{ {
XDeleteProperty(_glfw.x11.display, window->x11.handle, XDeleteProperty(_glfw.x11.display, window->x11.handle,
_glfw.x11.NET_WM_BYPASS_COMPOSITOR); _glfw.x11.NET_WM_BYPASS_COMPOSITOR);
@ -415,7 +434,12 @@ static char** parseUriList(char* text, int* count)
continue; continue;
if (strncmp(line, prefix, strlen(prefix)) == 0) if (strncmp(line, prefix, strlen(prefix)) == 0)
{
line += strlen(prefix); line += strlen(prefix);
// TODO: Validate hostname
while (*line != '/')
line++;
}
(*count)++; (*count)++;
@ -442,6 +466,81 @@ static char** parseUriList(char* text, int* count)
return paths; return paths;
} }
// Encode a Unicode code point to a UTF-8 stream
// Based on cutef8 by Jeff Bezanson (Public Domain)
//
static size_t encodeUTF8(char* s, unsigned int ch)
{
size_t count = 0;
if (ch < 0x80)
s[count++] = (char) ch;
else if (ch < 0x800)
{
s[count++] = (ch >> 6) | 0xc0;
s[count++] = (ch & 0x3f) | 0x80;
}
else if (ch < 0x10000)
{
s[count++] = (ch >> 12) | 0xe0;
s[count++] = ((ch >> 6) & 0x3f) | 0x80;
s[count++] = (ch & 0x3f) | 0x80;
}
else if (ch < 0x110000)
{
s[count++] = (ch >> 18) | 0xf0;
s[count++] = ((ch >> 12) & 0x3f) | 0x80;
s[count++] = ((ch >> 6) & 0x3f) | 0x80;
s[count++] = (ch & 0x3f) | 0x80;
}
return count;
}
// Decode a Unicode code point from a UTF-8 stream
// Based on cutef8 by Jeff Bezanson (Public Domain)
//
#if defined(X_HAVE_UTF8_STRING)
static unsigned int decodeUTF8(const char** s)
{
unsigned int ch = 0, count = 0;
static const unsigned int offsets[] =
{
0x00000000u, 0x00003080u, 0x000e2080u,
0x03c82080u, 0xfa082080u, 0x82082080u
};
do
{
ch = (ch << 6) + (unsigned char) **s;
(*s)++;
count++;
} while ((**s & 0xc0) == 0x80);
assert(count <= 6);
return ch - offsets[count - 1];
}
#endif /*X_HAVE_UTF8_STRING*/
// Convert the specified Latin-1 string to UTF-8
//
static char* convertLatin1toUTF8(const char* source)
{
size_t size = 1;
const char* sp;
for (sp = source; *sp; sp++)
size += (*sp & 0x80) ? 2 : 1;
char* target = calloc(size, 1);
char* tp = target;
for (sp = source; *sp; sp++)
tp += encodeUTF8(tp, *sp);
return target;
}
// Centers the cursor over the window client area // Centers the cursor over the window client area
// //
static void centerCursor(_GLFWwindow* window) static void centerCursor(_GLFWwindow* window)
@ -484,6 +583,8 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
visual, visual,
AllocNone); AllocNone);
window->x11.transparent = _glfwIsVisualTransparentX11(visual);
// Create the actual window // Create the actual window
{ {
XSetWindowAttributes wa; XSetWindowAttributes wa;
@ -607,22 +708,33 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
updateNormalHints(window, wndconfig->width, wndconfig->height); updateNormalHints(window, wndconfig->width, wndconfig->height);
// Set ICCCM WM_CLASS property // Set ICCCM WM_CLASS property
// HACK: Until a mechanism for specifying the application name is added, the
// initial window title is used as the window class name
if (strlen(wndconfig->title))
{ {
XClassHint* hint = XAllocClassHint(); XClassHint* hint = XAllocClassHint();
if (strlen(_glfw.hints.init.x11.className) &&
strlen(_glfw.hints.init.x11.classClass))
{
hint->res_name = (char*) _glfw.hints.init.x11.className;
hint->res_class = (char*) _glfw.hints.init.x11.classClass;
}
else if (strlen(wndconfig->title))
{
hint->res_name = (char*) wndconfig->title; hint->res_name = (char*) wndconfig->title;
hint->res_class = (char*) wndconfig->title; hint->res_class = (char*) wndconfig->title;
}
else
{
hint->res_name = (char*) "glfw-application";
hint->res_class = (char*) "GLFW-Application";
}
XSetClassHint(_glfw.x11.display, window->x11.handle, hint); XSetClassHint(_glfw.x11.display, window->x11.handle, hint);
XFree(hint); XFree(hint);
} }
if (_glfw.x11.XdndAware)
{
// Announce support for Xdnd (drag and drop) // Announce support for Xdnd (drag and drop)
const Atom version = 5; {
const Atom version = _GLFW_XDND_VERSION;
XChangeProperty(_glfw.x11.display, window->x11.handle, XChangeProperty(_glfw.x11.display, window->x11.handle,
_glfw.x11.XdndAware, XA_ATOM, 32, _glfw.x11.XdndAware, XA_ATOM, 32,
PropModeReplace, (unsigned char*) &version, 1); PropModeReplace, (unsigned char*) &version, 1);
@ -653,11 +765,15 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
static Atom writeTargetToProperty(const XSelectionRequestEvent* request) static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
{ {
int i; int i;
const Atom formats[] = { _glfw.x11.UTF8_STRING, char* selectionString = NULL;
_glfw.x11.COMPOUND_STRING, const Atom formats[] = { _glfw.x11.UTF8_STRING, XA_STRING };
XA_STRING };
const int formatCount = sizeof(formats) / sizeof(formats[0]); const int formatCount = sizeof(formats) / sizeof(formats[0]);
if (request->selection == _glfw.x11.PRIMARY)
selectionString = _glfw.x11.primarySelectionString;
else
selectionString = _glfw.x11.clipboardString;
if (request->property == None) if (request->property == None)
{ {
// The requester is a legacy client (ICCCM section 2.2) // The requester is a legacy client (ICCCM section 2.2)
@ -672,7 +788,6 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
const Atom targets[] = { _glfw.x11.TARGETS, const Atom targets[] = { _glfw.x11.TARGETS,
_glfw.x11.MULTIPLE, _glfw.x11.MULTIPLE,
_glfw.x11.UTF8_STRING, _glfw.x11.UTF8_STRING,
_glfw.x11.COMPOUND_STRING,
XA_STRING }; XA_STRING };
XChangeProperty(_glfw.x11.display, XChangeProperty(_glfw.x11.display,
@ -717,8 +832,8 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
targets[i], targets[i],
8, 8,
PropModeReplace, PropModeReplace,
(unsigned char*) _glfw.x11.clipboardString, (unsigned char *) selectionString,
strlen(_glfw.x11.clipboardString)); strlen(selectionString));
} }
else else
targets[i + 1] = None; targets[i + 1] = None;
@ -769,8 +884,8 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
request->target, request->target,
8, 8,
PropModeReplace, PropModeReplace,
(unsigned char*) _glfw.x11.clipboardString, (unsigned char *) selectionString,
strlen(_glfw.x11.clipboardString)); strlen(selectionString));
return request->property; return request->property;
} }
@ -783,8 +898,16 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
static void handleSelectionClear(XEvent* event) static void handleSelectionClear(XEvent* event)
{ {
if (event->xselectionclear.selection == _glfw.x11.PRIMARY)
{
free(_glfw.x11.primarySelectionString);
_glfw.x11.primarySelectionString = NULL;
}
else
{
free(_glfw.x11.clipboardString); free(_glfw.x11.clipboardString);
_glfw.x11.clipboardString = NULL; _glfw.x11.clipboardString = NULL;
}
} }
static void handleSelectionRequest(XEvent* event) static void handleSelectionRequest(XEvent* event)
@ -805,6 +928,147 @@ static void handleSelectionRequest(XEvent* event)
XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply); XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply);
} }
static const char* getSelectionString(Atom selection)
{
size_t i;
char** selectionString = NULL;
const Atom targets[] = { _glfw.x11.UTF8_STRING, XA_STRING };
const size_t targetCount = sizeof(targets) / sizeof(targets[0]);
if (selection == _glfw.x11.PRIMARY)
selectionString = &_glfw.x11.primarySelectionString;
else
selectionString = &_glfw.x11.clipboardString;
if (XGetSelectionOwner(_glfw.x11.display, selection) ==
_glfw.x11.helperWindowHandle)
{
// Instead of doing a large number of X round-trips just to put this
// string into a window property and then read it back, just return it
return *selectionString;
}
free(*selectionString);
*selectionString = NULL;
for (i = 0; i < targetCount; i++)
{
char* data;
Atom actualType;
int actualFormat;
unsigned long itemCount, bytesAfter;
XEvent notification, dummy;
XConvertSelection(_glfw.x11.display,
selection,
targets[i],
_glfw.x11.GLFW_SELECTION,
_glfw.x11.helperWindowHandle,
CurrentTime);
while (!XCheckTypedWindowEvent(_glfw.x11.display,
_glfw.x11.helperWindowHandle,
SelectionNotify,
&notification))
{
waitForEvent(NULL);
}
if (notification.xselection.property == None)
continue;
XCheckIfEvent(_glfw.x11.display,
&dummy,
isSelPropNewValueNotify,
(XPointer) &notification);
XGetWindowProperty(_glfw.x11.display,
notification.xselection.requestor,
notification.xselection.property,
0,
LONG_MAX,
True,
AnyPropertyType,
&actualType,
&actualFormat,
&itemCount,
&bytesAfter,
(unsigned char**) &data);
if (actualType == _glfw.x11.INCR)
{
size_t size = 1;
char* string = NULL;
for (;;)
{
while (!XCheckIfEvent(_glfw.x11.display,
&dummy,
isSelPropNewValueNotify,
(XPointer) &notification))
{
waitForEvent(NULL);
}
XFree(data);
XGetWindowProperty(_glfw.x11.display,
notification.xselection.requestor,
notification.xselection.property,
0,
LONG_MAX,
True,
AnyPropertyType,
&actualType,
&actualFormat,
&itemCount,
&bytesAfter,
(unsigned char**) &data);
if (itemCount)
{
size += itemCount;
string = realloc(string, size);
string[size - itemCount - 1] = '\0';
strcat(string, data);
}
if (!itemCount)
{
if (targets[i] == XA_STRING)
{
*selectionString = convertLatin1toUTF8(string);
free(string);
}
else
*selectionString = string;
break;
}
}
}
else if (actualType == targets[i])
{
if (targets[i] == XA_STRING)
*selectionString = convertLatin1toUTF8(data);
else
*selectionString = strdup(data);
}
XFree(data);
if (*selectionString)
break;
}
if (!*selectionString)
{
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
"X11: Failed to convert selection to string");
}
return *selectionString;
}
// Make the specified window and its video mode active on its monitor // Make the specified window and its video mode active on its monitor
// //
static GLFWbool acquireMonitor(_GLFWwindow* window) static GLFWbool acquireMonitor(_GLFWwindow* window)
@ -870,31 +1134,6 @@ static void releaseMonitor(_GLFWwindow* window)
} }
} }
// Decode a Unicode code point from a UTF-8 stream
// Based on cutef8 by Jeff Bezanson (Public Domain)
//
#if defined(X_HAVE_UTF8_STRING)
static unsigned int decodeUTF8(const char** s)
{
unsigned int ch = 0, count = 0;
static const unsigned int offsets[] =
{
0x00000000u, 0x00003080u, 0x000e2080u,
0x03c82080u, 0xfa082080u, 0x82082080u
};
do
{
ch = (ch << 6) + (unsigned char) **s;
(*s)++;
count++;
} while ((**s & 0xc0) == 0x80);
assert(count <= 6);
return ch - offsets[count - 1];
}
#endif /*X_HAVE_UTF8_STRING*/
// Process the specified X event // Process the specified X event
// //
static void processEvent(XEvent *event) static void processEvent(XEvent *event)
@ -986,17 +1225,16 @@ static void processEvent(XEvent *event)
if (window->x11.ic) if (window->x11.ic)
{ {
// HACK: Ignore duplicate key press events generated by ibus // HACK: Ignore duplicate key press events generated by ibus
// Corresponding release events are filtered out by the // These have the same timestamp as the original event
// GLFW key repeat logic // Corresponding release events are filtered out
if (window->x11.lastKeyCode != keycode || // implicitly by the GLFW key repeat logic
window->x11.lastKeyTime != event->xkey.time) if (window->x11.lastKeyTime < event->xkey.time)
{ {
if (keycode) if (keycode)
_glfwInputKey(window, key, keycode, GLFW_PRESS, mods); _glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
}
window->x11.lastKeyCode = keycode;
window->x11.lastKeyTime = event->xkey.time; window->x11.lastKeyTime = event->xkey.time;
}
if (!filtered) if (!filtered)
{ {
@ -1033,8 +1271,10 @@ static void processEvent(XEvent *event)
count = XwcLookupString(window->x11.ic, count = XwcLookupString(window->x11.ic,
&event->xkey, &event->xkey,
buffer, sizeof(buffer) / sizeof(wchar_t), buffer,
NULL, &status); sizeof(buffer) / sizeof(wchar_t),
NULL,
&status);
if (status == XBufferOverflow) if (status == XBufferOverflow)
{ {
@ -1207,7 +1447,8 @@ static void processEvent(XEvent *event)
const int x = event->xmotion.x; const int x = event->xmotion.x;
const int y = event->xmotion.y; const int y = event->xmotion.y;
if (x != window->x11.warpCursorPosX || y != window->x11.warpCursorPosY) if (x != window->x11.warpCursorPosX ||
y != window->x11.warpCursorPosY)
{ {
// The cursor was moved by something other than GLFW // The cursor was moved by something other than GLFW
@ -1286,14 +1527,15 @@ static void processEvent(XEvent *event)
if (protocol == _glfw.x11.WM_DELETE_WINDOW) if (protocol == _glfw.x11.WM_DELETE_WINDOW)
{ {
// The window manager was asked to close the window, for example by // The window manager was asked to close the window, for
// the user pressing a 'close' window decoration button // example by the user pressing a 'close' window decoration
// button
_glfwInputWindowCloseRequest(window); _glfwInputWindowCloseRequest(window);
} }
else if (protocol == _glfw.x11.NET_WM_PING) else if (protocol == _glfw.x11.NET_WM_PING)
{ {
// The window manager is pinging the application to ensure it's // The window manager is pinging the application to ensure
// still responding to events // it's still responding to events
XEvent reply = *event; XEvent reply = *event;
reply.xclient.window = _glfw.x11.root; reply.xclient.window = _glfw.x11.root;
@ -1307,44 +1549,121 @@ static void processEvent(XEvent *event)
else if (event->xclient.message_type == _glfw.x11.XdndEnter) else if (event->xclient.message_type == _glfw.x11.XdndEnter)
{ {
// A drag operation has entered the window // A drag operation has entered the window
// TODO: Check if UTF-8 string is supported by the source unsigned long i, count;
Atom* formats = NULL;
const GLFWbool list = event->xclient.data.l[1] & 1;
_glfw.x11.xdnd.source = event->xclient.data.l[0];
_glfw.x11.xdnd.version = event->xclient.data.l[1] >> 24;
_glfw.x11.xdnd.format = None;
if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
return;
if (list)
{
count = _glfwGetWindowPropertyX11(_glfw.x11.xdnd.source,
_glfw.x11.XdndTypeList,
XA_ATOM,
(unsigned char**) &formats);
}
else
{
count = 3;
formats = (Atom*) event->xclient.data.l + 2;
}
for (i = 0; i < count; i++)
{
if (formats[i] == _glfw.x11.text_uri_list)
{
_glfw.x11.xdnd.format = _glfw.x11.text_uri_list;
break;
}
}
if (list && formats)
XFree(formats);
} }
else if (event->xclient.message_type == _glfw.x11.XdndDrop) else if (event->xclient.message_type == _glfw.x11.XdndDrop)
{ {
// The drag operation has finished dropping on // The drag operation has finished by dropping on the window
// the window, ask to convert it to a UTF-8 string Time time = CurrentTime;
_glfw.x11.xdnd.source = event->xclient.data.l[0];
if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
return;
if (_glfw.x11.xdnd.format)
{
if (_glfw.x11.xdnd.version >= 1)
time = event->xclient.data.l[2];
// Request the chosen format from the source window
XConvertSelection(_glfw.x11.display, XConvertSelection(_glfw.x11.display,
_glfw.x11.XdndSelection, _glfw.x11.XdndSelection,
_glfw.x11.UTF8_STRING, _glfw.x11.xdnd.format,
_glfw.x11.XdndSelection, _glfw.x11.XdndSelection,
window->x11.handle, CurrentTime); window->x11.handle,
time);
} }
else if (event->xclient.message_type == _glfw.x11.XdndPosition) else if (_glfw.x11.xdnd.version >= 2)
{ {
// The drag operation has moved over the window
const int absX = (event->xclient.data.l[2] >> 16) & 0xFFFF;
const int absY = (event->xclient.data.l[2]) & 0xFFFF;
int x, y;
_glfwPlatformGetWindowPos(window, &x, &y);
_glfwInputCursorPos(window, absX - x, absY - y);
// Reply that we are ready to copy the dragged data
XEvent reply; XEvent reply;
memset(&reply, 0, sizeof(reply)); memset(&reply, 0, sizeof(reply));
reply.type = ClientMessage; reply.type = ClientMessage;
reply.xclient.window = event->xclient.data.l[0]; reply.xclient.window = _glfw.x11.xdnd.source;
reply.xclient.message_type = _glfw.x11.XdndFinished;
reply.xclient.format = 32;
reply.xclient.data.l[0] = window->x11.handle;
reply.xclient.data.l[1] = 0; // The drag was rejected
reply.xclient.data.l[2] = None;
XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
False, NoEventMask, &reply);
XFlush(_glfw.x11.display);
}
}
else if (event->xclient.message_type == _glfw.x11.XdndPosition)
{
// The drag operation has moved over the window
const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff;
const int yabs = (event->xclient.data.l[2]) & 0xffff;
Window dummy;
int xpos, ypos;
if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
return;
XTranslateCoordinates(_glfw.x11.display,
_glfw.x11.root,
window->x11.handle,
xabs, yabs,
&xpos, &ypos,
&dummy);
_glfwInputCursorPos(window, xpos, ypos);
XEvent reply;
memset(&reply, 0, sizeof(reply));
reply.type = ClientMessage;
reply.xclient.window = _glfw.x11.xdnd.source;
reply.xclient.message_type = _glfw.x11.XdndStatus; reply.xclient.message_type = _glfw.x11.XdndStatus;
reply.xclient.format = 32; reply.xclient.format = 32;
reply.xclient.data.l[0] = window->x11.handle; reply.xclient.data.l[0] = window->x11.handle;
reply.xclient.data.l[1] = 1; // Always accept the dnd with no rectangle
reply.xclient.data.l[2] = 0; // Specify an empty rectangle reply.xclient.data.l[2] = 0; // Specify an empty rectangle
reply.xclient.data.l[3] = 0; reply.xclient.data.l[3] = 0;
reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy;
XSendEvent(_glfw.x11.display, event->xclient.data.l[0], if (_glfw.x11.xdnd.format)
{
// Reply that we are ready to copy the dragged data
reply.xclient.data.l[1] = 1; // Accept with no rectangle
if (_glfw.x11.xdnd.version >= 2)
reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy;
}
XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
False, NoEventMask, &reply); False, NoEventMask, &reply);
XFlush(_glfw.x11.display); XFlush(_glfw.x11.display);
} }
@ -1354,11 +1673,11 @@ static void processEvent(XEvent *event)
case SelectionNotify: case SelectionNotify:
{ {
if (event->xselection.property) if (event->xselection.property == _glfw.x11.XdndSelection)
{ {
// The converted data from the drag operation has arrived // The converted data from the drag operation has arrived
char* data; char* data;
const int result = const unsigned long result =
_glfwGetWindowPropertyX11(event->xselection.requestor, _glfwGetWindowPropertyX11(event->xselection.requestor,
event->xselection.property, event->xselection.property,
event->xselection.target, event->xselection.target,
@ -1379,6 +1698,8 @@ static void processEvent(XEvent *event)
if (data) if (data)
XFree(data); XFree(data);
if (_glfw.x11.xdnd.version >= 2)
{
XEvent reply; XEvent reply;
memset(&reply, 0, sizeof(reply)); memset(&reply, 0, sizeof(reply));
@ -1390,11 +1711,11 @@ static void processEvent(XEvent *event)
reply.xclient.data.l[1] = result; reply.xclient.data.l[1] = result;
reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy; reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy;
// Reply that all is well
XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
False, NoEventMask, &reply); False, NoEventMask, &reply);
XFlush(_glfw.x11.display); XFlush(_glfw.x11.display);
} }
}
return; return;
} }
@ -1525,6 +1846,15 @@ unsigned long _glfwGetWindowPropertyX11(Window window,
return itemCount; return itemCount;
} }
GLFWbool _glfwIsVisualTransparentX11(Visual* visual)
{
if (!_glfw.x11.xrender.available)
return GLFW_FALSE;
XRenderPictFormat* pf = XRenderFindVisualFormat(_glfw.x11.display, visual);
return pf && pf->direct.alphaMask;
}
// Push contents of our selection to clipboard manager // Push contents of our selection to clipboard manager
// //
void _glfwPushSelectionToManagerX11(void) void _glfwPushSelectionToManagerX11(void)
@ -1556,9 +1886,10 @@ void _glfwPushSelectionToManagerX11(void)
{ {
if (event.xselection.target == _glfw.x11.SAVE_TARGETS) if (event.xselection.target == _glfw.x11.SAVE_TARGETS)
{ {
// This means one of two things; either the selection was // This means one of two things; either the selection
// not owned, which means there is no clipboard manager, or // was not owned, which means there is no clipboard
// the transfer to the clipboard manager has completed // manager, or the transfer to the clipboard manager has
// completed
// In either case, it means we are done here // In either case, it means we are done here
return; return;
} }
@ -1591,14 +1922,14 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
{ {
if (!_glfwInitGLX()) if (!_glfwInitGLX())
return GLFW_FALSE; return GLFW_FALSE;
if (!_glfwChooseVisualGLX(ctxconfig, fbconfig, &visual, &depth)) if (!_glfwChooseVisualGLX(wndconfig, ctxconfig, fbconfig, &visual, &depth))
return GLFW_FALSE; return GLFW_FALSE;
} }
else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
{ {
if (!_glfwInitEGL()) if (!_glfwInitEGL())
return GLFW_FALSE; return GLFW_FALSE;
if (!_glfwChooseVisualEGL(ctxconfig, fbconfig, &visual, &depth)) if (!_glfwChooseVisualEGL(wndconfig, ctxconfig, fbconfig, &visual, &depth))
return GLFW_FALSE; return GLFW_FALSE;
} }
else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
@ -1913,6 +2244,15 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
XFree(extents); XFree(extents);
} }
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
float* xscale, float* yscale)
{
if (xscale)
*xscale = _glfw.x11.contentScaleX;
if (yscale)
*yscale = _glfw.x11.contentScaleY;
}
void _glfwPlatformIconifyWindow(_GLFWwindow* window) void _glfwPlatformIconifyWindow(_GLFWwindow* window)
{ {
if (window->x11.overrideRedirect) if (window->x11.overrideRedirect)
@ -1993,6 +2333,15 @@ void _glfwPlatformHideWindow(_GLFWwindow* window)
XFlush(_glfw.x11.display); XFlush(_glfw.x11.display);
} }
void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
{
sendEventToWM(window,
_glfw.x11.NET_WM_STATE,
_NET_WM_STATE_ADD,
_glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION,
0, 1, 0);
}
void _glfwPlatformFocusWindow(_GLFWwindow* window) void _glfwPlatformFocusWindow(_GLFWwindow* window)
{ {
if (_glfw.x11.NET_ACTIVE_WINDOW) if (_glfw.x11.NET_ACTIVE_WINDOW)
@ -2032,7 +2381,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
if (window->monitor) if (window->monitor)
releaseMonitor(window); releaseMonitor(window);
_glfwInputWindowMonitorChange(window, monitor); _glfwInputWindowMonitor(window, monitor);
updateNormalHints(window, width, height); updateNormalHints(window, width, height);
updateWindowMode(window); updateWindowMode(window);
@ -2099,6 +2448,14 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window)
return maximized; return maximized;
} }
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
{
if (!window->x11.transparent)
return GLFW_FALSE;
return XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx) != None;
}
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
{ {
int width, height; int width, height;
@ -2202,6 +2559,37 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
XFlush(_glfw.x11.display); XFlush(_glfw.x11.display);
} }
float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
{
float opacity = 1.f;
if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx))
{
CARD32* value = NULL;
if (_glfwGetWindowPropertyX11(window->x11.handle,
_glfw.x11.NET_WM_WINDOW_OPACITY,
XA_CARDINAL,
(unsigned char**) &value))
{
opacity = (float) (*value / (double) 0xffffffffu);
}
if (value)
XFree(value);
}
return opacity;
}
void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
{
const CARD32 value = (CARD32) (0xffffffffu * (double) opacity);
XChangeProperty(_glfw.x11.display, window->x11.handle,
_glfw.x11.NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32,
PropModeReplace, (unsigned char*) &value, 1);
}
void _glfwPlatformPollEvents(void) void _glfwPlatformPollEvents(void)
{ {
_GLFWwindow* window; _GLFWwindow* window;
@ -2350,31 +2738,24 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
XFlush(_glfw.x11.display); XFlush(_glfw.x11.display);
} }
const char* _glfwPlatformGetKeyName(int key, int scancode) const char* _glfwPlatformGetScancodeName(int scancode)
{ {
KeySym keysym;
int extra;
if (!_glfw.x11.xkb.available) if (!_glfw.x11.xkb.available)
return NULL; return NULL;
if (key != GLFW_KEY_UNKNOWN) const KeySym keysym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, 0, 0);
scancode = _glfw.x11.scancodes[key];
if (!_glfwIsPrintable(_glfw.x11.keycodes[scancode]))
return NULL;
keysym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, 0, 0);
if (keysym == NoSymbol) if (keysym == NoSymbol)
return NULL; return NULL;
XkbTranslateKeySym(_glfw.x11.display, &keysym, 0, const long ch = _glfwKeySym2Unicode(keysym);
_glfw.x11.keyName, sizeof(_glfw.x11.keyName), if (ch == -1)
&extra);
if (!strlen(_glfw.x11.keyName))
return NULL; return NULL;
const size_t count = encodeUTF8(_glfw.x11.keyName, (unsigned int) ch);
if (count == 0)
return NULL;
_glfw.x11.keyName[count] = '\0';
return _glfw.x11.keyName; return _glfw.x11.keyName;
} }
@ -2423,7 +2804,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
} }
} }
void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) void _glfwPlatformSetClipboardString(const char* string)
{ {
free(_glfw.x11.clipboardString); free(_glfw.x11.clipboardString);
_glfw.x11.clipboardString = strdup(string); _glfw.x11.clipboardString = strdup(string);
@ -2441,74 +2822,9 @@ void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string)
} }
} }
const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) const char* _glfwPlatformGetClipboardString(void)
{ {
size_t i; return getSelectionString(_glfw.x11.CLIPBOARD);
const Atom formats[] = { _glfw.x11.UTF8_STRING,
_glfw.x11.COMPOUND_STRING,
XA_STRING };
const size_t formatCount = sizeof(formats) / sizeof(formats[0]);
if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) ==
_glfw.x11.helperWindowHandle)
{
// Instead of doing a large number of X round-trips just to put this
// string into a window property and then read it back, just return it
return _glfw.x11.clipboardString;
}
free(_glfw.x11.clipboardString);
_glfw.x11.clipboardString = NULL;
for (i = 0; i < formatCount; i++)
{
char* data;
XEvent event;
XConvertSelection(_glfw.x11.display,
_glfw.x11.CLIPBOARD,
formats[i],
_glfw.x11.GLFW_SELECTION,
_glfw.x11.helperWindowHandle,
CurrentTime);
while (!XCheckTypedWindowEvent(_glfw.x11.display,
_glfw.x11.helperWindowHandle,
SelectionNotify,
&event))
{
waitForEvent(NULL);
}
if (event.xselection.property == None)
continue;
if (_glfwGetWindowPropertyX11(event.xselection.requestor,
event.xselection.property,
event.xselection.target,
(unsigned char**) &data))
{
_glfw.x11.clipboardString = strdup(data);
}
if (data)
XFree(data);
XDeleteProperty(_glfw.x11.display,
event.xselection.requestor,
event.xselection.property);
if (_glfw.x11.clipboardString)
break;
}
if (_glfw.x11.clipboardString == NULL)
{
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
"X11: Failed to convert clipboard to string");
}
return _glfw.x11.clipboardString;
} }
void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
@ -2541,7 +2857,8 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
{ {
PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR vkGetPhysicalDeviceXcbPresentationSupportKHR = PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR
vkGetPhysicalDeviceXcbPresentationSupportKHR =
(PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR) (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)
vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR"); vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR");
if (!vkGetPhysicalDeviceXcbPresentationSupportKHR) if (!vkGetPhysicalDeviceXcbPresentationSupportKHR)
@ -2551,8 +2868,7 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
return GLFW_FALSE; return GLFW_FALSE;
} }
xcb_connection_t* connection = xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);
_glfw.x11.x11xcb.XGetXCBConnection(_glfw.x11.display);
if (!connection) if (!connection)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, _glfwInputError(GLFW_PLATFORM_ERROR,
@ -2567,7 +2883,8 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
} }
else else
{ {
PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR = PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR
vkGetPhysicalDeviceXlibPresentationSupportKHR =
(PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR) (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)
vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR"); vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR");
if (!vkGetPhysicalDeviceXlibPresentationSupportKHR) if (!vkGetPhysicalDeviceXlibPresentationSupportKHR)
@ -2595,8 +2912,7 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
VkXcbSurfaceCreateInfoKHR sci; VkXcbSurfaceCreateInfoKHR sci;
PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR; PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR;
xcb_connection_t* connection = xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);
_glfw.x11.x11xcb.XGetXCBConnection(_glfw.x11.display);
if (!connection) if (!connection)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, _glfwInputError(GLFW_PLATFORM_ERROR,
@ -2678,3 +2994,29 @@ GLFWAPI Window glfwGetX11Window(GLFWwindow* handle)
return window->x11.handle; return window->x11.handle;
} }
GLFWAPI void glfwSetX11SelectionString(const char* string)
{
_GLFW_REQUIRE_INIT();
free(_glfw.x11.primarySelectionString);
_glfw.x11.primarySelectionString = strdup(string);
XSetSelectionOwner(_glfw.x11.display,
_glfw.x11.PRIMARY,
_glfw.x11.helperWindowHandle,
CurrentTime);
if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.PRIMARY) !=
_glfw.x11.helperWindowHandle)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"X11: Failed to become owner of primary selection");
}
}
GLFWAPI const char* glfwGetX11SelectionString(void)
{
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
return getSelectionString(_glfw.x11.PRIMARY);
}

View File

@ -826,9 +826,59 @@ static const struct codepair {
{ 0x13bd, 0x0153 }, { 0x13bd, 0x0153 },
{ 0x13be, 0x0178 }, { 0x13be, 0x0178 },
{ 0x20ac, 0x20ac }, { 0x20ac, 0x20ac },
// Numeric keypad with numlock on { 0xfe50, '`' },
{ 0xfe51, 0x00b4 },
{ 0xfe52, '^' },
{ 0xfe53, '~' },
{ 0xfe54, 0x00af },
{ 0xfe55, 0x02d8 },
{ 0xfe56, 0x02d9 },
{ 0xfe57, 0x00a8 },
{ 0xfe58, 0x02da },
{ 0xfe59, 0x02dd },
{ 0xfe5a, 0x02c7 },
{ 0xfe5b, 0x00b8 },
{ 0xfe5c, 0x02db },
{ 0xfe5d, 0x037a },
{ 0xfe5e, 0x309b },
{ 0xfe5f, 0x309c },
{ 0xfe63, '/' },
{ 0xfe64, 0x02bc },
{ 0xfe65, 0x02bd },
{ 0xfe66, 0x02f5 },
{ 0xfe67, 0x02f3 },
{ 0xfe68, 0x02cd },
{ 0xfe69, 0xa788 },
{ 0xfe6a, 0x02f7 },
{ 0xfe6e, ',' },
{ 0xfe6f, 0x00a4 },
{ 0xfe80, 'a' }, // XK_dead_a
{ 0xfe81, 'A' }, // XK_dead_A
{ 0xfe82, 'e' }, // XK_dead_e
{ 0xfe83, 'E' }, // XK_dead_E
{ 0xfe84, 'i' }, // XK_dead_i
{ 0xfe85, 'I' }, // XK_dead_I
{ 0xfe86, 'o' }, // XK_dead_o
{ 0xfe87, 'O' }, // XK_dead_O
{ 0xfe88, 'u' }, // XK_dead_u
{ 0xfe89, 'U' }, // XK_dead_U
{ 0xfe8a, 0x0259 },
{ 0xfe8b, 0x018f },
{ 0xfe8c, 0x00b5 },
{ 0xfe90, '_' },
{ 0xfe91, 0x02c8 },
{ 0xfe92, 0x02cc },
{ 0xff80 /*XKB_KEY_KP_Space*/, ' ' }, { 0xff80 /*XKB_KEY_KP_Space*/, ' ' },
{ 0xffbd /*XKB_KEY_KP_Equal*/, '=' }, { 0xff95 /*XKB_KEY_KP_7*/, 0x0037 },
{ 0xff96 /*XKB_KEY_KP_4*/, 0x0034 },
{ 0xff97 /*XKB_KEY_KP_8*/, 0x0038 },
{ 0xff98 /*XKB_KEY_KP_6*/, 0x0036 },
{ 0xff99 /*XKB_KEY_KP_2*/, 0x0032 },
{ 0xff9a /*XKB_KEY_KP_9*/, 0x0039 },
{ 0xff9b /*XKB_KEY_KP_3*/, 0x0033 },
{ 0xff9c /*XKB_KEY_KP_1*/, 0x0031 },
{ 0xff9d /*XKB_KEY_KP_5*/, 0x0035 },
{ 0xff9e /*XKB_KEY_KP_0*/, 0x0030 },
{ 0xffaa /*XKB_KEY_KP_Multiply*/, '*' }, { 0xffaa /*XKB_KEY_KP_Multiply*/, '*' },
{ 0xffab /*XKB_KEY_KP_Add*/, '+' }, { 0xffab /*XKB_KEY_KP_Add*/, '+' },
{ 0xffac /*XKB_KEY_KP_Separator*/, ',' }, { 0xffac /*XKB_KEY_KP_Separator*/, ',' },
@ -844,7 +894,8 @@ static const struct codepair {
{ 0xffb6 /*XKB_KEY_KP_6*/, 0x0036 }, { 0xffb6 /*XKB_KEY_KP_6*/, 0x0036 },
{ 0xffb7 /*XKB_KEY_KP_7*/, 0x0037 }, { 0xffb7 /*XKB_KEY_KP_7*/, 0x0037 },
{ 0xffb8 /*XKB_KEY_KP_8*/, 0x0038 }, { 0xffb8 /*XKB_KEY_KP_8*/, 0x0038 },
{ 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 } { 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 },
{ 0xffbd /*XKB_KEY_KP_Equal*/, '=' }
}; };

View File

@ -24,10 +24,5 @@
// //
//======================================================================== //========================================================================
#ifndef _glfw3_xkb_unicode_h_
#define _glfw3_xkb_unicode_h_
long _glfwKeySym2Unicode(unsigned int keysym); long _glfwKeySym2Unicode(unsigned int keysym);
#endif // _glfw3_xkb_unicode_h_

View File

@ -30,9 +30,10 @@ add_executable(cursor cursor.c ${GLAD})
add_executable(empty WIN32 MACOSX_BUNDLE empty.c ${TINYCTHREAD} ${GLAD}) add_executable(empty WIN32 MACOSX_BUNDLE empty.c ${TINYCTHREAD} ${GLAD})
add_executable(gamma WIN32 MACOSX_BUNDLE gamma.c ${GLAD}) add_executable(gamma WIN32 MACOSX_BUNDLE gamma.c ${GLAD})
add_executable(icon WIN32 MACOSX_BUNDLE icon.c ${GLAD}) add_executable(icon WIN32 MACOSX_BUNDLE icon.c ${GLAD})
add_executable(inputlag WIN32 MACOSX_BUNDLE inputlag.c ${GETOPT} ${GLAD})
add_executable(joysticks WIN32 MACOSX_BUNDLE joysticks.c ${GLAD}) add_executable(joysticks WIN32 MACOSX_BUNDLE joysticks.c ${GLAD})
add_executable(sharing WIN32 MACOSX_BUNDLE sharing.c ${GETOPT} ${GLAD}) add_executable(opacity WIN32 MACOSX_BUNDLE opacity.c ${GLAD})
add_executable(tearing WIN32 MACOSX_BUNDLE tearing.c ${GETOPT} ${GLAD}) add_executable(tearing WIN32 MACOSX_BUNDLE tearing.c ${GLAD})
add_executable(threads WIN32 MACOSX_BUNDLE threads.c ${TINYCTHREAD} ${GLAD}) add_executable(threads WIN32 MACOSX_BUNDLE threads.c ${TINYCTHREAD} ${GLAD})
add_executable(timeout WIN32 MACOSX_BUNDLE timeout.c ${GLAD}) add_executable(timeout WIN32 MACOSX_BUNDLE timeout.c ${GLAD})
add_executable(title WIN32 MACOSX_BUNDLE title.c ${GLAD}) add_executable(title WIN32 MACOSX_BUNDLE title.c ${GLAD})
@ -45,16 +46,18 @@ if (RT_LIBRARY)
target_link_libraries(threads "${RT_LIBRARY}") target_link_libraries(threads "${RT_LIBRARY}")
endif() endif()
set(WINDOWS_BINARIES empty gamma icon joysticks sharing tearing threads timeout set(WINDOWS_BINARIES empty gamma icon inputlag joysticks opacity tearing
title windows) threads timeout title windows)
set(CONSOLE_BINARIES clipboard events msaa glfwinfo iconify monitors reopen set(CONSOLE_BINARIES clipboard events msaa glfwinfo iconify monitors reopen
cursor) cursor)
if (VULKAN_FOUND) if (VULKAN_FOUND)
add_executable(vulkan WIN32 vulkan.c ${ICON}) add_executable(vulkan WIN32 vulkan.c ${ICON})
target_include_directories(vulkan PRIVATE "${VULKAN_INCLUDE_DIR}") target_include_directories(vulkan PRIVATE "${VULKAN_INCLUDE_DIR}")
if (NOT GLFW_VULKAN_STATIC) if (GLFW_VULKAN_STATIC)
target_link_libraries(vulkan "${VULKAN_LIBRARY}" ${GLFW_VULKAN_DEPS}) target_link_libraries(vulkan "${VULKAN_STATIC_LIBRARY}" ${GLFW_VULKAN_DEPS})
else()
target_link_libraries(vulkan "${VULKAN_LIBRARY}")
endif() endif()
list(APPEND WINDOWS_BINARIES vulkan) list(APPEND WINDOWS_BINARIES vulkan)
endif() endif()
@ -71,8 +74,9 @@ endif()
if (APPLE) if (APPLE)
set_target_properties(empty PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Empty Event") set_target_properties(empty PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Empty Event")
set_target_properties(gamma PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Gamma") set_target_properties(gamma PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Gamma")
set_target_properties(inputlag PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Input Lag")
set_target_properties(joysticks PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Joysticks") set_target_properties(joysticks PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Joysticks")
set_target_properties(sharing PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Sharing") set_target_properties(opacity PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Opacity")
set_target_properties(tearing PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Tearing") set_target_properties(tearing PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Tearing")
set_target_properties(threads PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Threads") set_target_properties(threads PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Threads")
set_target_properties(timeout PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Timeout") set_target_properties(timeout PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Timeout")

View File

@ -67,7 +67,7 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action,
{ {
const char* string; const char* string;
string = glfwGetClipboardString(window); string = glfwGetClipboardString(NULL);
if (string) if (string)
printf("Clipboard contains \"%s\"\n", string); printf("Clipboard contains \"%s\"\n", string);
else else
@ -79,7 +79,7 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action,
if (mods == MODIFIER) if (mods == MODIFIER)
{ {
const char* string = "Hello GLFW World!"; const char* string = "Hello GLFW World!";
glfwSetClipboardString(window, string); glfwSetClipboardString(NULL, string);
printf("Setting clipboard to \"%s\"\n", string); printf("Setting clipboard to \"%s\"\n", string);
} }
break; break;

Some files were not shown because too many files have changed in this diff Show More