From a9fd85f9ef4dee3603f41d059ead286e814cb279 Mon Sep 17 00:00:00 2001 From: lfl-eholthouser <84752487+lfl-eholthouser@users.noreply.github.com> Date: Fri, 27 Jan 2023 12:43:08 -0800 Subject: [PATCH] Add MaterialX Graph Editor (#1169) The MaterialX Graph Editor is an example application for visualizing, creating, and editing MaterialX graphs. It utilizes the ImGui framework as well as ImGui extensions such as the Node Editor and File Browser. --- .github/workflows/main.yml | 2 +- .gitmodules | 9 + CMakeLists.txt | 12 +- documents/DeveloperGuide/GraphEditor.md | 78 + .../Images/MaterialXGraphEditor_Marble.png | Bin 0 -> 310802 bytes source/MaterialXCore/Node.cpp | 12 +- source/MaterialXGraphEditor/CMakeLists.txt | 113 + .../External/Glfw/CMakeLists.txt | 370 + .../External/Glfw/LICENSE.md | 23 + .../External/Glfw/include/GLFW/glfw3.h | 5973 +++++++++++++++++ .../External/Glfw/include/GLFW/glfw3native.h | 525 ++ .../External/Glfw/src/CMakeLists.txt | 179 + .../External/Glfw/src/cocoa_init.m | 621 ++ .../External/Glfw/src/cocoa_joystick.h | 50 + .../External/Glfw/src/cocoa_joystick.m | 487 ++ .../External/Glfw/src/cocoa_monitor.m | 612 ++ .../External/Glfw/src/cocoa_platform.h | 211 + .../External/Glfw/src/cocoa_time.c | 62 + .../External/Glfw/src/cocoa_window.m | 1846 +++++ .../External/Glfw/src/context.c | 760 +++ .../External/Glfw/src/egl_context.c | 789 +++ .../External/Glfw/src/egl_context.h | 215 + .../External/Glfw/src/glfw.rc.in | 30 + .../External/Glfw/src/glfw3.pc.in | 13 + .../External/Glfw/src/glfw3Config.cmake.in | 3 + .../External/Glfw/src/glfw_config.h.in | 58 + .../External/Glfw/src/glx_context.c | 698 ++ .../External/Glfw/src/glx_context.h | 181 + .../External/Glfw/src/init.c | 342 + .../External/Glfw/src/input.c | 1360 ++++ .../External/Glfw/src/internal.h | 781 +++ .../External/Glfw/src/linux_joystick.c | 433 ++ .../External/Glfw/src/linux_joystick.h | 62 + .../External/Glfw/src/mappings.h | 476 ++ .../External/Glfw/src/mappings.h.in | 73 + .../External/Glfw/src/monitor.c | 544 ++ .../External/Glfw/src/nsgl_context.h | 66 + .../External/Glfw/src/nsgl_context.m | 369 + .../External/Glfw/src/null_init.c | 52 + .../External/Glfw/src/null_joystick.c | 44 + .../External/Glfw/src/null_joystick.h | 31 + .../External/Glfw/src/null_monitor.c | 77 + .../External/Glfw/src/null_platform.h | 62 + .../External/Glfw/src/null_window.c | 332 + .../External/Glfw/src/osmesa_context.c | 372 + .../External/Glfw/src/osmesa_context.h | 94 + .../External/Glfw/src/posix_thread.c | 105 + .../External/Glfw/src/posix_thread.h | 51 + .../External/Glfw/src/posix_time.c | 90 + .../External/Glfw/src/posix_time.h | 44 + .../External/Glfw/src/vulkan.c | 332 + .../External/Glfw/src/wgl_context.c | 796 +++ .../External/Glfw/src/wgl_context.h | 164 + .../External/Glfw/src/win32_init.c | 631 ++ .../External/Glfw/src/win32_joystick.c | 755 +++ .../External/Glfw/src/win32_joystick.h | 56 + .../External/Glfw/src/win32_monitor.c | 535 ++ .../External/Glfw/src/win32_platform.h | 459 ++ .../External/Glfw/src/win32_thread.c | 99 + .../External/Glfw/src/win32_time.c | 76 + .../External/Glfw/src/win32_window.c | 2265 +++++++ .../External/Glfw/src/window.c | 1104 +++ .../External/Glfw/src/wl_init.c | 1318 ++++ .../External/Glfw/src/wl_monitor.c | 225 + .../External/Glfw/src/wl_platform.h | 357 + .../External/Glfw/src/wl_window.c | 1754 +++++ .../External/Glfw/src/x11_init.c | 1113 +++ .../External/Glfw/src/x11_monitor.c | 614 ++ .../External/Glfw/src/x11_platform.h | 459 ++ .../External/Glfw/src/x11_window.c | 3213 +++++++++ .../External/Glfw/src/xkb_unicode.c | 942 +++ .../External/Glfw/src/xkb_unicode.h | 28 + source/MaterialXGraphEditor/External/ImGui | 1 + .../External/ImGuiFileBrowser | 1 + .../External/ImGuiNodeEditor | 1 + source/MaterialXGraphEditor/Graph.cpp | 3811 +++++++++++ source/MaterialXGraphEditor/Graph.h | 269 + source/MaterialXGraphEditor/Main.cpp | 236 + source/MaterialXGraphEditor/Material.cpp | 355 + source/MaterialXGraphEditor/Material.h | 197 + source/MaterialXGraphEditor/RenderView.cpp | 1341 ++++ source/MaterialXGraphEditor/RenderView.h | 347 + source/MaterialXGraphEditor/UiNode.cpp | 146 + source/MaterialXGraphEditor/UiNode.h | 208 + source/MaterialXRenderGlsl/CMakeLists.txt | 4 + 85 files changed, 43997 insertions(+), 7 deletions(-) create mode 100644 documents/DeveloperGuide/GraphEditor.md create mode 100644 documents/Images/MaterialXGraphEditor_Marble.png create mode 100644 source/MaterialXGraphEditor/CMakeLists.txt create mode 100644 source/MaterialXGraphEditor/External/Glfw/CMakeLists.txt create mode 100644 source/MaterialXGraphEditor/External/Glfw/LICENSE.md create mode 100644 source/MaterialXGraphEditor/External/Glfw/include/GLFW/glfw3.h create mode 100644 source/MaterialXGraphEditor/External/Glfw/include/GLFW/glfw3native.h create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/CMakeLists.txt create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/cocoa_init.m create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/cocoa_joystick.h create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/cocoa_joystick.m create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/cocoa_monitor.m create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/cocoa_platform.h create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/cocoa_time.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/cocoa_window.m create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/context.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/egl_context.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/egl_context.h create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/glfw.rc.in create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/glfw3.pc.in create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/glfw3Config.cmake.in create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/glfw_config.h.in create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/glx_context.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/glx_context.h create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/init.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/input.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/internal.h create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/linux_joystick.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/linux_joystick.h create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/mappings.h create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/mappings.h.in create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/monitor.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/nsgl_context.h create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/nsgl_context.m create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/null_init.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/null_joystick.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/null_joystick.h create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/null_monitor.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/null_platform.h create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/null_window.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/osmesa_context.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/osmesa_context.h create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/posix_thread.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/posix_thread.h create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/posix_time.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/posix_time.h create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/vulkan.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/wgl_context.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/wgl_context.h create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/win32_init.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/win32_joystick.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/win32_joystick.h create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/win32_monitor.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/win32_platform.h create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/win32_thread.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/win32_time.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/win32_window.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/window.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/wl_init.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/wl_monitor.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/wl_platform.h create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/wl_window.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/x11_init.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/x11_monitor.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/x11_platform.h create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/x11_window.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/xkb_unicode.c create mode 100644 source/MaterialXGraphEditor/External/Glfw/src/xkb_unicode.h create mode 160000 source/MaterialXGraphEditor/External/ImGui create mode 160000 source/MaterialXGraphEditor/External/ImGuiFileBrowser create mode 160000 source/MaterialXGraphEditor/External/ImGuiNodeEditor create mode 100644 source/MaterialXGraphEditor/Graph.cpp create mode 100644 source/MaterialXGraphEditor/Graph.h create mode 100644 source/MaterialXGraphEditor/Main.cpp create mode 100644 source/MaterialXGraphEditor/Material.cpp create mode 100644 source/MaterialXGraphEditor/Material.h create mode 100644 source/MaterialXGraphEditor/RenderView.cpp create mode 100644 source/MaterialXGraphEditor/RenderView.h create mode 100644 source/MaterialXGraphEditor/UiNode.cpp create mode 100644 source/MaterialXGraphEditor/UiNode.h diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0c3501e215..e3f1686286 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -180,7 +180,7 @@ jobs: run: | mkdir build cd build - cmake -DMATERIALX_BUILD_PYTHON=ON -DMATERIALX_BUILD_VIEWER=ON -DMATERIALX_TEST_RENDER=OFF -DMATERIALX_WARNINGS_AS_ERRORS=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ${{matrix.cmake_config}} .. + cmake -DMATERIALX_BUILD_PYTHON=ON -DMATERIALX_BUILD_VIEWER=ON -DMATERIALX_BUILD_GRAPH_EDITOR=ON -DMATERIALX_TEST_RENDER=OFF -DMATERIALX_WARNINGS_AS_ERRORS=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ${{matrix.cmake_config}} .. - name: CMake Build run: cmake --build . --target install --config Release --parallel 2 diff --git a/.gitmodules b/.gitmodules index 11c8f9ecfd..da4b8f9fa7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,12 @@ [submodule "source/MaterialXView/NanoGUI"] path = source/MaterialXView/NanoGUI url = https://github.com/mitsuba-renderer/nanogui +[submodule "source/MaterialXGraphEditor/External/ImGui"] + path = source/MaterialXGraphEditor/External/ImGui + url = https://github.com/ocornut/imgui +[submodule "source/MaterialXGraphEditor/External/ImGuiNodeEditor"] + path = source/MaterialXGraphEditor/External/ImGuiNodeEditor + url = https://github.com/thedmd/imgui-node-editor +[submodule "source/MaterialXGraphEditor/External/ImGuiFileBrowser"] + path = source/MaterialXGraphEditor/External/ImGuiFileBrowser + url = https://github.com/AirGuanZ/imgui-filebrowser diff --git a/CMakeLists.txt b/CMakeLists.txt index 22cff8bc9d..03d4be9c84 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,7 @@ project(MaterialX) option(MATERIALX_BUILD_PYTHON "Build the MaterialX Python package from C++ bindings. Requires Python 2.7 or greater." OFF) option(MATERIALX_BUILD_VIEWER "Build the MaterialX Viewer." OFF) +option(MATERIALX_BUILD_GRAPH_EDITOR "Build the MaterialX Graph Editor." OFF) option(MATERIALX_BUILD_DOCS "Create HTML documentation using Doxygen. Requires that Doxygen be installed." OFF) option(MATERIALX_BUILD_GEN_GLSL "Build the GLSL shader generator back-end." ON) @@ -249,6 +250,9 @@ if(MATERIALX_BUILD_RENDER) if(MATERIALX_BUILD_VIEWER) add_subdirectory(source/MaterialXView) endif() + if(MATERIALX_BUILD_GRAPH_EDITOR) + add_subdirectory(source/MaterialXGraphEditor) + endif() add_subdirectory(resources) endif() @@ -274,10 +278,10 @@ endif() if(${CMAKE_VERSION} VERSION_GREATER "3.6.2") if(MATERIALX_BUILD_VIEWER) set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT MaterialXView) - else() - if(MATERIALX_BUILD_TESTS) - set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT MaterialXTest) - endif() + elseif(MATERIALX_BUILD_GRAPH_EDITOR) + set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT MaterialXGraphEditor) + elseif(MATERIALX_BUILD_TESTS) + set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT MaterialXTest) endif() endif() diff --git a/documents/DeveloperGuide/GraphEditor.md b/documents/DeveloperGuide/GraphEditor.md new file mode 100644 index 0000000000..03f8983e3a --- /dev/null +++ b/documents/DeveloperGuide/GraphEditor.md @@ -0,0 +1,78 @@ +# MaterialX Graph Editor + +The MaterialX Graph Editor is an example application for visualizing, creating, and editing MaterialX graphs. It utilizes the ImGui framework as well as additional ImGui extensions such as the Node Editor and File Browser. + +### Example Images + +**Figure 1:** MaterialX Graph Editor with procedural marble example + + +## Building The MaterialX Graph Editor +Select the `MATERIALX_BUILD_GRAPH_EDITOR` option in CMake to build the MaterialX Graph Editor. Installation will copy the **MaterialXGraphEditor** executable to a `/bin` directory within the selected install folder. + +### Summary of Graph Editor Features + +1. **Load Material**: Load a material document in the MTLX format. +2. **Save Material**: Save out a graph as a mterial document in MTLX format. +3. **New Material**: Clear all information to set up for the creation of a new material +4. **Node Property Editor**: View or edit properties of the selected node. +5. **Render View**: View the rendered material. + +### Buttons + +To display a new material and graph, click the `Load Material` button and and navigate to the [Example Materials](../../resources/Materials/Examples) folder, which contains a selection of materials in the MTLX format, and select a document to load. The Graph Editor will display the graph hierarchy of the selected document for visualization and editing. + +To save out changes to the graphs as MTLX files click the `Save Material` button. This will save the position of the nodes in the graph for future use as well. + +### Editor Window + +The MaterialX document is displayed as nodes in the Editor window. When a file is intially loaded the material node, surface shader node, and any enclosing nodegraphs will be displayed. Double-clicking on a nodegraph, or any node defined as a subgraph, will display the contents of that graph. + +The current graph hierarchy is displayed above the editor. To back up and out of a subgraph click the `<` button to the left of the graph names. + +Each node and nodegraph displays its name and pins for all of its inputs and outputs. Nodes can be connected by clicking on the output pin of one node and connecting it to the input pin of another node, thus creating a link. Links can only be created if the input and output pins have the same type, as designated by the color of the pin. When a new link is created the material in the render view will automatically be updated. + +Using the tab key on the editor allows the user to add a certain node by bringing up the `Add Node` pop-up. The nodes are organized in the pop-up window based on their node group but a specfic node can also be searched for by name. To create a nodegraph select `Node Graph` in the `Add Node` popup and dive into the node in order to add nodes inside of it and populate it. + +In order to connect to the nodegraph to a shader add output nodes inside the nodegraph then travel back up outside the nodegraph and connect the corresponding output pin to the surface shader. By default, the nodegraph does not contain any output nodes or pins. + +Another type of node present in the `Add Node` pop-up is the group, or background node. This background node can be used to group specific nodes and label by them by dragging them on to the background node. + +To search the editor window for a specific node use `CTRL` + `F` to bring up the search bar. + +### Node Property Editor +When a node is selected in the graph, its information is displayed on the left-hand column in the `Node Property Editor`. This editor displays the name of the node, its category, its inputs, the input name, types and values. Inputs that are connected to other nodes will not display a value. + +This is where a node's properties such as its name and input values can be adjusted. When an input value is changed the material is automatically updated to reflect that change. The node info button displays the `doc` string for the selected node and its inputs if they exist. This `doc` string is currently read only. + +The show All Inputs checkbox displays all possible inputs for a node. With the box unchecked only inputs that have a connection or have had a value set will be shown. Only these inputs will be saved out when the graph is saved. + +### Render View +Above the `Node Property Editor`, the `Render View` displays the current material on the Arnold Shader Ball. If inside a subgraph it will display the material associated with that subgraph; otherwise it will display the output of the selected node. It automatically updates when any changes are made to the graph. + +To adjust the relative sizes of the Node Property Editor and Render View windows, drag the separator between these windows in the application. The render view window camera can be changed using the left or right mouse buttons to manipulate the shader ball. + +### Keyboard Shortcuts + +- `TAB`: Add Node Popup +- `Right Click`: pan along the editor +- `Double Click on Node`: Dive into node's subgraph if it has one +- `U`: Go up and out of a subgraph +- `F`: Frame selected node(s) +- `Ctrl + F` to search for a node in the editor by name +- `Ctrl/Cmd + C` for Copying Nodes +- `Ctrl/Cmd+X` for Cutting Nodes +- `Ctrl/Cmd+V` for Pasting Nodes +- `+` : Zoom in with the camera when mouse is over the Render View Window. +- `-` : Zoom out with the camera when mouse is over the Render View Window. + +### Command-Line Options + +The following are common command-line options for MaterialXGraphEditor, and a complete list can be displayed with the `--help` option. +- `--path [FILEPATH]` : Specify an additional absolute search path location (e.g. '/projects/MaterialX'). This path will be queried when locating standard data libraries, XInclude references, and referenced images. +- `--library [FILEPATH]` : Specify an additional relative path to a custom data library folder (e.g. 'libraries/custom'). MaterialX files at the root of this folder will be included in all content documents. + +### Known Limitations + +- Creating new connections from multi-output nodes is not yet supported, though existing multi-output connections will be displayed in graphs. +- Creating new connections using the `channels` attribute of an input is not yet supported, though existing `channels` connections will be displayed in graphs. diff --git a/documents/Images/MaterialXGraphEditor_Marble.png b/documents/Images/MaterialXGraphEditor_Marble.png new file mode 100644 index 0000000000000000000000000000000000000000..96a92194ed0bb179b29d5cf50fd603b145876275 GIT binary patch literal 310802 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�qXt1jHWclnn=1i9|tBll|y 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%0DeGCg_Q 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ℜFN-m?gb>$j34^GCM5HPR6$KCC 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 KxvX GraphElement::topologicalSort() const size_t connectionCount = 0; for (size_t i = 0; i < child->getUpstreamEdgeCount(); ++i) { - if (child->getUpstreamEdge(i)) + Edge upstreamEdge = child->getUpstreamEdge(i); + if (upstreamEdge) { - connectionCount++; + if (upstreamEdge.getUpstreamElement()) + { + ElementPtr elem = upstreamEdge.getUpstreamElement()->getParent(); + if (elem == child->getParent()) + { + connectionCount++; + } + } } } diff --git a/source/MaterialXGraphEditor/CMakeLists.txt b/source/MaterialXGraphEditor/CMakeLists.txt new file mode 100644 index 0000000000..606121d4f4 --- /dev/null +++ b/source/MaterialXGraphEditor/CMakeLists.txt @@ -0,0 +1,113 @@ +set(CMAKE_CXX_STANDARD 17) + +set(DEAR_IMGUI_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/External/ImGui" CACHE STRING "Path to Dear ImGui") + +if (NOT IS_DIRECTORY "${DEAR_IMGUI_PREFIX}/backends") + message(FATAL_ERROR "Building the MaterialX graph editor requires the ImGui submodule " + "to be present. Update your repository by calling the following:\n" + "git submodule update --init --recursive") +endif() + +if(MSVC) + add_compile_options(-wd4100) +elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wno-unused -Wno-deprecated -Wno-comment -Wno-unused-variable) +elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + add_compile_options(-Wno-format-truncation -Wno-use-after-free -Wno-comment -Wno-unused-but-set-variable) +endif() + +file(GLOB materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") +file(GLOB materialx_headers "${CMAKE_CURRENT_SOURCE_DIR}/*.h*") + +file(GLOB imgui_source "${DEAR_IMGUI_PREFIX}/*.cpp") +file(GLOB imgui_headers "${DEAR_IMGUI_PREFIX}/*.h*") + +set(imgui_source ${imgui_source} + "${DEAR_IMGUI_PREFIX}/backends/imgui_impl_glfw.cpp" + "${DEAR_IMGUI_PREFIX}/backends/imgui_impl_opengl3.cpp" + "${DEAR_IMGUI_PREFIX}/misc/cpp/imgui_stdlib.cpp") +set(imgui_headers ${imgui_headers} + "${DEAR_IMGUI_PREFIX}/backends/imgui_impl_glfw.h" + "${DEAR_IMGUI_PREFIX}/backends/imgui_impl_opengl3.h" + "${DEAR_IMGUI_PREFIX}/misc/cpp/imgui_stdlib.h") + +file(GLOB imguinode_source "${CMAKE_CURRENT_SOURCE_DIR}/External/ImGuiNodeEditor/*.cpp") +file(GLOB imguinode_headers "${CMAKE_CURRENT_SOURCE_DIR}/External/ImGuiNodeEditor/*.h*") + +set(imguinode_source ${imguinode_source} + "${CMAKE_CURRENT_SOURCE_DIR}/External/ImGuiNodeEditor/examples/blueprints-example/utilities/drawing.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/External/ImGuiNodeEditor/examples/blueprints-example/utilities/widgets.cpp") +set(imguinode_headers ${imguinode_headers} + "${CMAKE_CURRENT_SOURCE_DIR}/External/ImGuiNodeEditor/examples/blueprints-example/utilities/drawing.h" + "${CMAKE_CURRENT_SOURCE_DIR}/External/ImGuiNodeEditor/examples/blueprints-example/utilities/widgets.h") + +assign_source_group("Source Files" ${materialx_source} ${imgui_source} ${imguinode_source}) +assign_source_group("Header Files" ${materialx_headers} ${imgui_headers} ${imguinode_headers}) + +find_package(OpenGL REQUIRED) + +if (UNIX AND NOT APPLE) + find_package(Threads REQUIRED) + find_package(X11 REQUIRED) +endif() + +include_directories(${OPENGL_INCLUDE_DIRS}) +include_directories("${DEAR_IMGUI_PREFIX}") +include_directories("${DEAR_IMGUI_PREFIX}/backends") +include_directories("${DEAR_IMGUI_PREFIX}/misc/cpp") +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/External/ImGuiNodeEditor") +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/External/ImGuiNodeEditor/examples/blueprints-example/utilities") +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/External/ImGuiFileBrowser") + +set(GLFW_BUILD_EXAMPLES OFF) +set(GLFW_BUILD_TESTS OFF) +set(GLFW_BUILD_DOCS OFF) +set(GLFW_INSTALL OFF) + +add_subdirectory(External/Glfw) + +if(MSVC) + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ENTRY:mainCRTStartup") +endif() + +add_executable(MaterialXGraphEditor + ${materialx_source} + ${materialx_headers} + ${imgui_source} + ${imgui_headers} + ${imguinode_source} + ${imguinode_headers}) + +set(MATERIALX_LIBRARIES + MaterialXFormat + MaterialXGenGlsl + MaterialXRenderGlsl) + +target_link_libraries( + MaterialXGraphEditor + PRIVATE + ${MATERIALX_LIBRARIES} + ${OPENGL_LIBRARIES} + glfw_minimal) + +if (UNIX) + if (APPLE) + target_link_libraries(MaterialXGraphEditor + PRIVATE + "-framework Cocoa" + "-framework IOKit" + "-framework CoreVideo") + else() + target_link_libraries(MaterialXGraphEditor + PRIVATE + ${CMAKE_THREAD_LIBS_INIT} + ${X11_LIBRARIES} + ${CMAKE_DL_LIBS}) + endif() +endif() + +install(TARGETS MaterialXGraphEditor + EXPORT MaterialX + RUNTIME DESTINATION bin) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/MaterialXGraphEditor.pdb" + DESTINATION "${CMAKE_INSTALL_PREFIX}/bin/" OPTIONAL) diff --git a/source/MaterialXGraphEditor/External/Glfw/CMakeLists.txt b/source/MaterialXGraphEditor/External/Glfw/CMakeLists.txt new file mode 100644 index 0000000000..eaae85a825 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/CMakeLists.txt @@ -0,0 +1,370 @@ +cmake_minimum_required(VERSION 3.1) + +project(GLFW VERSION 3.4.0 LANGUAGES C) + +set(CMAKE_LEGACY_CYGWIN_WIN32 OFF) + +if (POLICY CMP0054) + cmake_policy(SET CMP0054 NEW) +endif() + +if (POLICY CMP0077) + cmake_policy(SET CMP0077 NEW) +endif() + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") + set(GLFW_STANDALONE TRUE) +endif() + +option(BUILD_SHARED_LIBS "Build shared libraries" OFF) +option(GLFW_BUILD_EXAMPLES "Build the GLFW example programs" ${GLFW_STANDALONE}) +option(GLFW_BUILD_TESTS "Build the GLFW test programs" ${GLFW_STANDALONE}) +option(GLFW_BUILD_DOCS "Build the GLFW documentation" ON) +option(GLFW_INSTALL "Generate installation target" ON) +option(GLFW_VULKAN_STATIC "Assume the Vulkan loader is linked with the application" OFF) + +include(GNUInstallDirs) +include(CMakeDependentOption) + +cmake_dependent_option(GLFW_USE_OSMESA "Use OSMesa for offscreen context creation" OFF + "UNIX" OFF) +cmake_dependent_option(GLFW_USE_HYBRID_HPG "Force use of high-performance GPU on hybrid systems" OFF + "WIN32" OFF) +cmake_dependent_option(GLFW_USE_WAYLAND "Use Wayland for window creation" OFF + "UNIX;NOT APPLE" OFF) +cmake_dependent_option(USE_MSVC_RUNTIME_LIBRARY_DLL "Use MSVC runtime library DLL" ON + "MSVC" OFF) + +if (BUILD_SHARED_LIBS AND UNIX) + # On Unix-like systems, shared libraries can use the soname system. + set(GLFW_LIB_NAME glfw) +else() + set(GLFW_LIB_NAME glfw3) +endif() + +if (GLFW_VULKAN_STATIC) + if (BUILD_SHARED_LIBS) + # If you absolutely must do this, remove this line and add the Vulkan + # loader static library via the CMAKE_SHARED_LINKER_FLAGS + message(FATAL_ERROR "You are trying to link the Vulkan loader static library into the GLFW shared library") + endif() + set(_GLFW_VULKAN_STATIC 1) +endif() + +list(APPEND CMAKE_MODULE_PATH "${GLFW_SOURCE_DIR}/CMake/modules") + +find_package(Threads REQUIRED) + +if (GLFW_BUILD_DOCS) + set(DOXYGEN_SKIP_DOT TRUE) + find_package(Doxygen) +endif() + +#-------------------------------------------------------------------- +# Set compiler specific flags +#-------------------------------------------------------------------- +if (MSVC) + if (MSVC90) + # Workaround for VS 2008 not shipping with the DirectX 9 SDK + include(CheckIncludeFile) + check_include_file(dinput.h DINPUT_H_FOUND) + if (NOT DINPUT_H_FOUND) + message(FATAL_ERROR "DirectX 9 headers not found; install DirectX 9 SDK") + endif() + # Workaround for VS 2008 not shipping with stdint.h + list(APPEND glfw_INCLUDE_DIRS "${GLFW_SOURCE_DIR}/deps/vs2008") + endif() + + if (NOT USE_MSVC_RUNTIME_LIBRARY_DLL) + foreach (flag CMAKE_C_FLAGS + CMAKE_C_FLAGS_DEBUG + CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL + CMAKE_C_FLAGS_RELWITHDEBINFO) + + if (${flag} MATCHES "/MD") + string(REGEX REPLACE "/MD" "/MT" ${flag} "${${flag}}") + endif() + if (${flag} MATCHES "/MDd") + string(REGEX REPLACE "/MDd" "/MTd" ${flag} "${${flag}}") + endif() + + endforeach() + endif() +endif() + +if (MINGW) + # Workaround for legacy MinGW not providing XInput and DirectInput + include(CheckIncludeFile) + + check_include_file(dinput.h DINPUT_H_FOUND) + check_include_file(xinput.h XINPUT_H_FOUND) + if (NOT DINPUT_H_FOUND OR NOT XINPUT_H_FOUND) + list(APPEND glfw_INCLUDE_DIRS "${GLFW_SOURCE_DIR}/deps/mingw") + endif() + + # Enable link-time exploit mitigation features enabled by default on MSVC + include(CheckCCompilerFlag) + + # Compatibility with data execution prevention (DEP) + set(CMAKE_REQUIRED_FLAGS "-Wl,--nxcompat") + check_c_compiler_flag("" _GLFW_HAS_DEP) + if (_GLFW_HAS_DEP) + set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--nxcompat ${CMAKE_SHARED_LINKER_FLAGS}") + endif() + + # Compatibility with address space layout randomization (ASLR) + set(CMAKE_REQUIRED_FLAGS "-Wl,--dynamicbase") + check_c_compiler_flag("" _GLFW_HAS_ASLR) + if (_GLFW_HAS_ASLR) + set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--dynamicbase ${CMAKE_SHARED_LINKER_FLAGS}") + endif() + + # Compatibility with 64-bit address space layout randomization (ASLR) + set(CMAKE_REQUIRED_FLAGS "-Wl,--high-entropy-va") + check_c_compiler_flag("" _GLFW_HAS_64ASLR) + if (_GLFW_HAS_64ASLR) + set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--high-entropy-va ${CMAKE_SHARED_LINKER_FLAGS}") + endif() +endif() + +#-------------------------------------------------------------------- +# Detect and select backend APIs +#-------------------------------------------------------------------- +if (GLFW_USE_WAYLAND) + set(_GLFW_WAYLAND 1) + message(STATUS "Using Wayland for window creation") +elseif (GLFW_USE_OSMESA) + set(_GLFW_OSMESA 1) + message(STATUS "Using OSMesa for headless context creation") +elseif (WIN32) + set(_GLFW_WIN32 1) + message(STATUS "Using Win32 for window creation") +elseif (APPLE) + set(_GLFW_COCOA 1) + message(STATUS "Using Cocoa for window creation") +elseif (UNIX) + set(_GLFW_X11 1) + message(STATUS "Using X11 for window creation") +else() + message(FATAL_ERROR "No supported platform was detected") +endif() + +#-------------------------------------------------------------------- +# Find and add Unix math and time libraries +#-------------------------------------------------------------------- +if (UNIX AND NOT APPLE) + find_library(RT_LIBRARY rt) + mark_as_advanced(RT_LIBRARY) + if (RT_LIBRARY) + list(APPEND glfw_LIBRARIES "${RT_LIBRARY}") + list(APPEND glfw_PKG_LIBS "-lrt") + endif() + + find_library(MATH_LIBRARY m) + mark_as_advanced(MATH_LIBRARY) + if (MATH_LIBRARY) + list(APPEND glfw_LIBRARIES "${MATH_LIBRARY}") + list(APPEND glfw_PKG_LIBS "-lm") + endif() + + if (CMAKE_DL_LIBS) + list(APPEND glfw_LIBRARIES "${CMAKE_DL_LIBS}") + list(APPEND glfw_PKG_LIBS "-l${CMAKE_DL_LIBS}") + endif() +endif() + +#-------------------------------------------------------------------- +# Use Win32 for window creation +#-------------------------------------------------------------------- +if (_GLFW_WIN32) + + list(APPEND glfw_PKG_LIBS "-lgdi32") + + if (GLFW_USE_HYBRID_HPG) + set(_GLFW_USE_HYBRID_HPG 1) + endif() +endif() + +#-------------------------------------------------------------------- +# Use X11 for window creation +#-------------------------------------------------------------------- +if (_GLFW_X11) + + find_package(X11 REQUIRED) + + list(APPEND glfw_PKG_DEPS "x11") + + # Set up library and include paths + list(APPEND glfw_INCLUDE_DIRS "${X11_X11_INCLUDE_PATH}") + list(APPEND glfw_LIBRARIES "${X11_X11_LIB}") + + # Check for XRandR (modern resolution switching and gamma control) + if (NOT X11_Xrandr_INCLUDE_PATH) + message(FATAL_ERROR "RandR headers not found; install libxrandr development package") + endif() + + # Check for Xinerama (legacy multi-monitor support) + if (NOT X11_Xinerama_INCLUDE_PATH) + message(FATAL_ERROR "Xinerama headers not found; install libxinerama development package") + endif() + + # Check for Xkb (X keyboard extension) + if (NOT X11_Xkb_INCLUDE_PATH) + message(FATAL_ERROR "XKB headers not found; install X11 development package") + endif() + + # Check for Xcursor (cursor creation from RGBA images) + if (NOT X11_Xcursor_INCLUDE_PATH) + message(FATAL_ERROR "Xcursor headers not found; install libxcursor development package") + endif() + + # Check for XInput (modern HID input) + if (NOT X11_Xi_INCLUDE_PATH) + message(FATAL_ERROR "XInput headers not found; install libxi development package") + endif() +endif() + +#-------------------------------------------------------------------- +# Use Wayland for window creation +#-------------------------------------------------------------------- +if (_GLFW_WAYLAND) + find_package(ECM REQUIRED NO_MODULE) + list(APPEND CMAKE_MODULE_PATH "${ECM_MODULE_PATH}") + + find_package(Wayland REQUIRED Client Cursor Egl) + find_package(WaylandScanner REQUIRED) + find_package(WaylandProtocols 1.15 REQUIRED) + + list(APPEND glfw_PKG_DEPS "wayland-egl") + + list(APPEND glfw_INCLUDE_DIRS "${Wayland_INCLUDE_DIRS}") + list(APPEND glfw_LIBRARIES "${Wayland_LIBRARIES}") + + find_package(XKBCommon REQUIRED) + list(APPEND glfw_INCLUDE_DIRS "${XKBCOMMON_INCLUDE_DIRS}") + + include(CheckIncludeFiles) + include(CheckFunctionExists) + check_include_files(xkbcommon/xkbcommon-compose.h HAVE_XKBCOMMON_COMPOSE_H) + check_function_exists(memfd_create HAVE_MEMFD_CREATE) + + if (NOT ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")) + find_package(EpollShim) + if (EPOLLSHIM_FOUND) + list(APPEND glfw_INCLUDE_DIRS "${EPOLLSHIM_INCLUDE_DIRS}") + list(APPEND glfw_LIBRARIES "${EPOLLSHIM_LIBRARIES}") + endif() + endif() +endif() + +#-------------------------------------------------------------------- +# Use OSMesa for offscreen context creation +#-------------------------------------------------------------------- +if (_GLFW_OSMESA) + find_package(OSMesa REQUIRED) +endif() + +#-------------------------------------------------------------------- +# Use Cocoa for window creation and NSOpenGL for context creation +#-------------------------------------------------------------------- +if (_GLFW_COCOA) + + list(APPEND glfw_LIBRARIES + "-framework Cocoa" + "-framework IOKit" + "-framework CoreFoundation") + + set(glfw_PKG_DEPS "") + set(glfw_PKG_LIBS "-framework Cocoa -framework IOKit -framework CoreFoundation") +endif() + +#-------------------------------------------------------------------- +# Add the Vulkan loader as a dependency if necessary +#-------------------------------------------------------------------- +if (GLFW_VULKAN_STATIC) + list(APPEND glfw_PKG_DEPS "vulkan") +endif() + +#-------------------------------------------------------------------- +# Export GLFW library dependencies +#-------------------------------------------------------------------- +foreach(arg ${glfw_PKG_DEPS}) + set(GLFW_PKG_DEPS "${GLFW_PKG_DEPS} ${arg}") +endforeach() +foreach(arg ${glfw_PKG_LIBS}) + set(GLFW_PKG_LIBS "${GLFW_PKG_LIBS} ${arg}") +endforeach() + +#-------------------------------------------------------------------- +# Create generated files +#-------------------------------------------------------------------- +include(CMakePackageConfigHelpers) + +set(GLFW_CONFIG_PATH "${CMAKE_INSTALL_LIBDIR}/cmake/glfw3") + +configure_package_config_file(src/glfw3Config.cmake.in + src/glfw3Config.cmake + INSTALL_DESTINATION "${GLFW_CONFIG_PATH}" + NO_CHECK_REQUIRED_COMPONENTS_MACRO) + +write_basic_package_version_file(src/glfw3ConfigVersion.cmake + VERSION ${GLFW_VERSION} + COMPATIBILITY SameMajorVersion) + +configure_file(src/glfw3.pc.in src/glfw3.pc @ONLY) + +#-------------------------------------------------------------------- +# Add subdirectories +#-------------------------------------------------------------------- +add_subdirectory(src) + +if (GLFW_BUILD_EXAMPLES) + add_subdirectory(examples) +endif() + +if (GLFW_BUILD_TESTS) + add_subdirectory(tests) +endif() + +if (DOXYGEN_FOUND AND GLFW_BUILD_DOCS) + add_subdirectory(docs) +endif() + +#-------------------------------------------------------------------- +# Install files other than the library +# The library is installed by src/CMakeLists.txt +#-------------------------------------------------------------------- +if (GLFW_INSTALL) + install(DIRECTORY include/GLFW DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING PATTERN glfw3.h PATTERN glfw3native.h) + + install(FILES "${GLFW_BINARY_DIR}/src/glfw3Config.cmake" + "${GLFW_BINARY_DIR}/src/glfw3ConfigVersion.cmake" + DESTINATION "${GLFW_CONFIG_PATH}") + + install(EXPORT glfwTargets FILE glfw3Targets.cmake + EXPORT_LINK_INTERFACE_LIBRARIES + DESTINATION "${GLFW_CONFIG_PATH}") + install(FILES "${GLFW_BINARY_DIR}/src/glfw3.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + + if (DOXYGEN_FOUND AND GLFW_BUILD_DOCS) + install(DIRECTORY "${GLFW_BINARY_DIR}/docs/html" + DESTINATION "${CMAKE_INSTALL_DOCDIR}") + endif() + + # Only generate this target if no higher-level project already has + if (NOT TARGET uninstall) + configure_file(CMake/cmake_uninstall.cmake.in + cmake_uninstall.cmake IMMEDIATE @ONLY) + + add_custom_target(uninstall + "${CMAKE_COMMAND}" -P + "${GLFW_BINARY_DIR}/cmake_uninstall.cmake") + set_target_properties(uninstall PROPERTIES FOLDER "GLFW3") + endif() +endif() + diff --git a/source/MaterialXGraphEditor/External/Glfw/LICENSE.md b/source/MaterialXGraphEditor/External/Glfw/LICENSE.md new file mode 100644 index 0000000000..7494a3f689 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/LICENSE.md @@ -0,0 +1,23 @@ +Copyright (c) 2002-2006 Marcus Geelnard + +Copyright (c) 2006-2019 Camilla Löwy + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. + diff --git a/source/MaterialXGraphEditor/External/Glfw/include/GLFW/glfw3.h b/source/MaterialXGraphEditor/External/Glfw/include/GLFW/glfw3.h new file mode 100644 index 0000000000..4f5f3607f6 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/include/GLFW/glfw3.h @@ -0,0 +1,5973 @@ +/************************************************************************* + * GLFW 3.4 - www.glfw.org + * A library for OpenGL, window and input + *------------------------------------------------------------------------ + * Copyright (c) 2002-2006 Marcus Geelnard + * Copyright (c) 2006-2019 Camilla Löwy + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would + * be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + *************************************************************************/ + +#ifndef _glfw3_h_ +#define _glfw3_h_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/************************************************************************* + * Doxygen documentation + *************************************************************************/ + +/*! @file glfw3.h + * @brief The header of the GLFW 3 API. + * + * This is the header file of the GLFW 3 API. It defines all its types and + * declares all its functions. + * + * For more information about how to use this file, see @ref build_include. + */ +/*! @defgroup context Context reference + * @brief Functions and types related to OpenGL and OpenGL ES contexts. + * + * This is the reference documentation for OpenGL and OpenGL ES context related + * functions. For more task-oriented information, see the @ref context_guide. + */ +/*! @defgroup vulkan Vulkan reference + * @brief Functions and types related to Vulkan. + * + * This is the reference documentation for Vulkan related functions and types. + * For more task-oriented information, see the @ref vulkan_guide. + */ +/*! @defgroup init Initialization, version and error reference + * @brief Functions and types related to initialization and error handling. + * + * This is the reference documentation for initialization and termination of + * the library, version management and error handling. For more task-oriented + * information, see the @ref intro_guide. + */ +/*! @defgroup input Input reference + * @brief Functions and types related to input handling. + * + * This is the reference documentation for input related functions and types. + * For more task-oriented information, see the @ref input_guide. + */ +/*! @defgroup monitor Monitor reference + * @brief Functions and types related to monitors. + * + * This is the reference documentation for monitor related functions and types. + * For more task-oriented information, see the @ref monitor_guide. + */ +/*! @defgroup window Window reference + * @brief Functions and types related to windows. + * + * This is the reference documentation for window related functions and types, + * including creation, deletion and event polling. For more task-oriented + * information, see the @ref window_guide. + */ + + +/************************************************************************* + * Compiler- and platform-specific preprocessor work + *************************************************************************/ + +/* If we are we on Windows, we want a single define for it. + */ +#if !defined(_WIN32) && (defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__)) + #define _WIN32 +#endif /* _WIN32 */ + +/* Include because most Windows GLU headers need wchar_t and + * the macOS OpenGL header blocks the definition of ptrdiff_t by glext.h. + * Include it unconditionally to avoid surprising side-effects. + */ +#include + +/* Include because it is needed by Vulkan and related functions. + * Include it unconditionally to avoid surprising side-effects. + */ +#include + +#if defined(GLFW_INCLUDE_VULKAN) + #include +#endif /* Vulkan header */ + +/* The Vulkan header may have indirectly included windows.h (because of + * VK_USE_PLATFORM_WIN32_KHR) so we offer our replacement symbols after it. + */ + +/* It is customary to use APIENTRY for OpenGL function pointer declarations on + * all platforms. Additionally, the Windows OpenGL header needs APIENTRY. + */ +#if !defined(APIENTRY) + #if defined(_WIN32) + #define APIENTRY __stdcall + #else + #define APIENTRY + #endif + #define GLFW_APIENTRY_DEFINED +#endif /* APIENTRY */ + +/* Some Windows OpenGL headers need this. + */ +#if !defined(WINGDIAPI) && defined(_WIN32) + #define WINGDIAPI __declspec(dllimport) + #define GLFW_WINGDIAPI_DEFINED +#endif /* WINGDIAPI */ + +/* Some Windows GLU headers need this. + */ +#if !defined(CALLBACK) && defined(_WIN32) + #define CALLBACK __stdcall + #define GLFW_CALLBACK_DEFINED +#endif /* CALLBACK */ + +/* Include the chosen OpenGL or OpenGL ES headers. + */ +#if defined(GLFW_INCLUDE_ES1) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_ES2) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_ES3) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_ES31) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_ES32) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_GLCOREARB) + + #if defined(__APPLE__) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif /*GLFW_INCLUDE_GLEXT*/ + + #else /*__APPLE__*/ + + #include + + #endif /*__APPLE__*/ + +#elif !defined(GLFW_INCLUDE_NONE) + + #if defined(__APPLE__) + + #if !defined(GLFW_INCLUDE_GLEXT) + #define GL_GLEXT_LEGACY + #endif + #include + #if defined(GLFW_INCLUDE_GLU) + #include + #endif + + #else /*__APPLE__*/ + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + #if defined(GLFW_INCLUDE_GLU) + #include + #endif + + #endif /*__APPLE__*/ + +#endif /* OpenGL and OpenGL ES headers */ + +#if defined(GLFW_DLL) && defined(_GLFW_BUILD_DLL) + /* GLFW_DLL must be defined by applications that are linking against the DLL + * version of the GLFW library. _GLFW_BUILD_DLL is defined by the GLFW + * configuration header when compiling the DLL version of the library. + */ + #error "You must not have both GLFW_DLL and _GLFW_BUILD_DLL defined" +#endif + +/* GLFWAPI is used to declare public API functions for export + * from the DLL / shared library / dynamic library. + */ +#if defined(_WIN32) && defined(_GLFW_BUILD_DLL) + /* We are building GLFW as a Win32 DLL */ + #define GLFWAPI __declspec(dllexport) +#elif defined(_WIN32) && defined(GLFW_DLL) + /* We are calling GLFW as a Win32 DLL */ + #define GLFWAPI __declspec(dllimport) +#elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL) + /* We are building GLFW as a shared / dynamic library */ + #define GLFWAPI __attribute__((visibility("default"))) +#else + /* We are building or calling GLFW as a static library */ + #define GLFWAPI +#endif + + +/************************************************************************* + * GLFW API tokens + *************************************************************************/ + +/*! @name GLFW version macros + * @{ */ +/*! @brief The major version number of the GLFW library. + * + * This is incremented when the API is changed in non-compatible ways. + * @ingroup init + */ +#define GLFW_VERSION_MAJOR 3 +/*! @brief The minor version number of the GLFW library. + * + * This is incremented when features are added to the API but it remains + * backward-compatible. + * @ingroup init + */ +#define GLFW_VERSION_MINOR 4 +/*! @brief The revision number of the GLFW library. + * + * This is incremented when a bug fix release is made that does not contain any + * API changes. + * @ingroup init + */ +#define GLFW_VERSION_REVISION 0 +/*! @} */ + +/*! @brief One. + * + * This is only semantic sugar for the number 1. You can instead use `1` or + * `true` or `_True` or `GL_TRUE` or `VK_TRUE` or anything else that is equal + * to one. + * + * @ingroup init + */ +#define GLFW_TRUE 1 +/*! @brief Zero. + * + * This is only semantic sugar for the number 0. You can instead use `0` or + * `false` or `_False` or `GL_FALSE` or `VK_FALSE` or anything else that is + * equal to zero. + * + * @ingroup init + */ +#define GLFW_FALSE 0 + +/*! @name Key and button actions + * @{ */ +/*! @brief The key or mouse button was released. + * + * The key or mouse button was released. + * + * @ingroup input + */ +#define GLFW_RELEASE 0 +/*! @brief The key or mouse button was pressed. + * + * The key or mouse button was pressed. + * + * @ingroup input + */ +#define GLFW_PRESS 1 +/*! @brief The key was held down until it repeated. + * + * The key was held down until it repeated. + * + * @ingroup input + */ +#define GLFW_REPEAT 2 +/*! @} */ + +/*! @defgroup hat_state Joystick hat states + * @brief Joystick hat states. + * + * See [joystick hat input](@ref joystick_hat) for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_HAT_CENTERED 0 +#define GLFW_HAT_UP 1 +#define GLFW_HAT_RIGHT 2 +#define GLFW_HAT_DOWN 4 +#define GLFW_HAT_LEFT 8 +#define GLFW_HAT_RIGHT_UP (GLFW_HAT_RIGHT | GLFW_HAT_UP) +#define GLFW_HAT_RIGHT_DOWN (GLFW_HAT_RIGHT | GLFW_HAT_DOWN) +#define GLFW_HAT_LEFT_UP (GLFW_HAT_LEFT | GLFW_HAT_UP) +#define GLFW_HAT_LEFT_DOWN (GLFW_HAT_LEFT | GLFW_HAT_DOWN) +/*! @} */ + +/*! @defgroup keys Keyboard keys + * @brief Keyboard key IDs. + * + * See [key input](@ref input_key) for how these are used. + * + * These key codes are inspired by the _USB HID Usage Tables v1.12_ (p. 53-60), + * but re-arranged to map to 7-bit ASCII for printable keys (function keys are + * put in the 256+ range). + * + * The naming of the key codes follow these rules: + * - The US keyboard layout is used + * - Names of printable alpha-numeric characters are used (e.g. "A", "R", + * "3", etc.) + * - For non-alphanumeric characters, Unicode:ish names are used (e.g. + * "COMMA", "LEFT_SQUARE_BRACKET", etc.). Note that some names do not + * correspond to the Unicode standard (usually for brevity) + * - Keys that lack a clear US mapping are named "WORLD_x" + * - For non-printable keys, custom names are used (e.g. "F4", + * "BACKSPACE", etc.) + * + * @ingroup input + * @{ + */ + +/* The unknown key */ +#define GLFW_KEY_UNKNOWN -1 + +/* Printable keys */ +#define GLFW_KEY_SPACE 32 +#define GLFW_KEY_APOSTROPHE 39 /* ' */ +#define GLFW_KEY_COMMA 44 /* , */ +#define GLFW_KEY_MINUS 45 /* - */ +#define GLFW_KEY_PERIOD 46 /* . */ +#define GLFW_KEY_SLASH 47 /* / */ +#define GLFW_KEY_0 48 +#define GLFW_KEY_1 49 +#define GLFW_KEY_2 50 +#define GLFW_KEY_3 51 +#define GLFW_KEY_4 52 +#define GLFW_KEY_5 53 +#define GLFW_KEY_6 54 +#define GLFW_KEY_7 55 +#define GLFW_KEY_8 56 +#define GLFW_KEY_9 57 +#define GLFW_KEY_SEMICOLON 59 /* ; */ +#define GLFW_KEY_EQUAL 61 /* = */ +#define GLFW_KEY_A 65 +#define GLFW_KEY_B 66 +#define GLFW_KEY_C 67 +#define GLFW_KEY_D 68 +#define GLFW_KEY_E 69 +#define GLFW_KEY_F 70 +#define GLFW_KEY_G 71 +#define GLFW_KEY_H 72 +#define GLFW_KEY_I 73 +#define GLFW_KEY_J 74 +#define GLFW_KEY_K 75 +#define GLFW_KEY_L 76 +#define GLFW_KEY_M 77 +#define GLFW_KEY_N 78 +#define GLFW_KEY_O 79 +#define GLFW_KEY_P 80 +#define GLFW_KEY_Q 81 +#define GLFW_KEY_R 82 +#define GLFW_KEY_S 83 +#define GLFW_KEY_T 84 +#define GLFW_KEY_U 85 +#define GLFW_KEY_V 86 +#define GLFW_KEY_W 87 +#define GLFW_KEY_X 88 +#define GLFW_KEY_Y 89 +#define GLFW_KEY_Z 90 +#define GLFW_KEY_LEFT_BRACKET 91 /* [ */ +#define GLFW_KEY_BACKSLASH 92 /* \ */ +#define GLFW_KEY_RIGHT_BRACKET 93 /* ] */ +#define GLFW_KEY_GRAVE_ACCENT 96 /* ` */ +#define GLFW_KEY_WORLD_1 161 /* non-US #1 */ +#define GLFW_KEY_WORLD_2 162 /* non-US #2 */ + +/* Function keys */ +#define GLFW_KEY_ESCAPE 256 +#define GLFW_KEY_ENTER 257 +#define GLFW_KEY_TAB 258 +#define GLFW_KEY_BACKSPACE 259 +#define GLFW_KEY_INSERT 260 +#define GLFW_KEY_DELETE 261 +#define GLFW_KEY_RIGHT 262 +#define GLFW_KEY_LEFT 263 +#define GLFW_KEY_DOWN 264 +#define GLFW_KEY_UP 265 +#define GLFW_KEY_PAGE_UP 266 +#define GLFW_KEY_PAGE_DOWN 267 +#define GLFW_KEY_HOME 268 +#define GLFW_KEY_END 269 +#define GLFW_KEY_CAPS_LOCK 280 +#define GLFW_KEY_SCROLL_LOCK 281 +#define GLFW_KEY_NUM_LOCK 282 +#define GLFW_KEY_PRINT_SCREEN 283 +#define GLFW_KEY_PAUSE 284 +#define GLFW_KEY_F1 290 +#define GLFW_KEY_F2 291 +#define GLFW_KEY_F3 292 +#define GLFW_KEY_F4 293 +#define GLFW_KEY_F5 294 +#define GLFW_KEY_F6 295 +#define GLFW_KEY_F7 296 +#define GLFW_KEY_F8 297 +#define GLFW_KEY_F9 298 +#define GLFW_KEY_F10 299 +#define GLFW_KEY_F11 300 +#define GLFW_KEY_F12 301 +#define GLFW_KEY_F13 302 +#define GLFW_KEY_F14 303 +#define GLFW_KEY_F15 304 +#define GLFW_KEY_F16 305 +#define GLFW_KEY_F17 306 +#define GLFW_KEY_F18 307 +#define GLFW_KEY_F19 308 +#define GLFW_KEY_F20 309 +#define GLFW_KEY_F21 310 +#define GLFW_KEY_F22 311 +#define GLFW_KEY_F23 312 +#define GLFW_KEY_F24 313 +#define GLFW_KEY_F25 314 +#define GLFW_KEY_KP_0 320 +#define GLFW_KEY_KP_1 321 +#define GLFW_KEY_KP_2 322 +#define GLFW_KEY_KP_3 323 +#define GLFW_KEY_KP_4 324 +#define GLFW_KEY_KP_5 325 +#define GLFW_KEY_KP_6 326 +#define GLFW_KEY_KP_7 327 +#define GLFW_KEY_KP_8 328 +#define GLFW_KEY_KP_9 329 +#define GLFW_KEY_KP_DECIMAL 330 +#define GLFW_KEY_KP_DIVIDE 331 +#define GLFW_KEY_KP_MULTIPLY 332 +#define GLFW_KEY_KP_SUBTRACT 333 +#define GLFW_KEY_KP_ADD 334 +#define GLFW_KEY_KP_ENTER 335 +#define GLFW_KEY_KP_EQUAL 336 +#define GLFW_KEY_LEFT_SHIFT 340 +#define GLFW_KEY_LEFT_CONTROL 341 +#define GLFW_KEY_LEFT_ALT 342 +#define GLFW_KEY_LEFT_SUPER 343 +#define GLFW_KEY_RIGHT_SHIFT 344 +#define GLFW_KEY_RIGHT_CONTROL 345 +#define GLFW_KEY_RIGHT_ALT 346 +#define GLFW_KEY_RIGHT_SUPER 347 +#define GLFW_KEY_MENU 348 + +#define GLFW_KEY_LAST GLFW_KEY_MENU + +/*! @} */ + +/*! @defgroup mods Modifier key flags + * @brief Modifier key flags. + * + * See [key input](@ref input_key) for how these are used. + * + * @ingroup input + * @{ */ + +/*! @brief If this bit is set one or more Shift keys were held down. + * + * If this bit is set one or more Shift keys were held down. + */ +#define GLFW_MOD_SHIFT 0x0001 +/*! @brief If this bit is set one or more Control keys were held down. + * + * If this bit is set one or more Control keys were held down. + */ +#define GLFW_MOD_CONTROL 0x0002 +/*! @brief If this bit is set one or more Alt keys were held down. + * + * If this bit is set one or more Alt keys were held down. + */ +#define GLFW_MOD_ALT 0x0004 +/*! @brief If this bit is set one or more Super keys were held down. + * + * If this bit is set one or more Super keys were held down. + */ +#define GLFW_MOD_SUPER 0x0008 +/*! @brief If this bit is set the Caps Lock key is enabled. + * + * If this bit is set the Caps Lock key is enabled and the @ref + * GLFW_LOCK_KEY_MODS input mode is set. + */ +#define GLFW_MOD_CAPS_LOCK 0x0010 +/*! @brief If this bit is set the Num Lock key is enabled. + * + * If this bit is set the Num Lock key is enabled and the @ref + * GLFW_LOCK_KEY_MODS input mode is set. + */ +#define GLFW_MOD_NUM_LOCK 0x0020 + +/*! @} */ + +/*! @defgroup buttons Mouse buttons + * @brief Mouse button IDs. + * + * See [mouse button input](@ref input_mouse_button) for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_MOUSE_BUTTON_1 0 +#define GLFW_MOUSE_BUTTON_2 1 +#define GLFW_MOUSE_BUTTON_3 2 +#define GLFW_MOUSE_BUTTON_4 3 +#define GLFW_MOUSE_BUTTON_5 4 +#define GLFW_MOUSE_BUTTON_6 5 +#define GLFW_MOUSE_BUTTON_7 6 +#define GLFW_MOUSE_BUTTON_8 7 +#define GLFW_MOUSE_BUTTON_LAST GLFW_MOUSE_BUTTON_8 +#define GLFW_MOUSE_BUTTON_LEFT GLFW_MOUSE_BUTTON_1 +#define GLFW_MOUSE_BUTTON_RIGHT GLFW_MOUSE_BUTTON_2 +#define GLFW_MOUSE_BUTTON_MIDDLE GLFW_MOUSE_BUTTON_3 +/*! @} */ + +/*! @defgroup joysticks Joysticks + * @brief Joystick IDs. + * + * See [joystick input](@ref joystick) for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_JOYSTICK_1 0 +#define GLFW_JOYSTICK_2 1 +#define GLFW_JOYSTICK_3 2 +#define GLFW_JOYSTICK_4 3 +#define GLFW_JOYSTICK_5 4 +#define GLFW_JOYSTICK_6 5 +#define GLFW_JOYSTICK_7 6 +#define GLFW_JOYSTICK_8 7 +#define GLFW_JOYSTICK_9 8 +#define GLFW_JOYSTICK_10 9 +#define GLFW_JOYSTICK_11 10 +#define GLFW_JOYSTICK_12 11 +#define GLFW_JOYSTICK_13 12 +#define GLFW_JOYSTICK_14 13 +#define GLFW_JOYSTICK_15 14 +#define GLFW_JOYSTICK_16 15 +#define GLFW_JOYSTICK_LAST GLFW_JOYSTICK_16 +/*! @} */ + +/*! @defgroup gamepad_buttons Gamepad buttons + * @brief Gamepad buttons. + * + * See @ref gamepad for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_GAMEPAD_BUTTON_A 0 +#define GLFW_GAMEPAD_BUTTON_B 1 +#define GLFW_GAMEPAD_BUTTON_X 2 +#define GLFW_GAMEPAD_BUTTON_Y 3 +#define GLFW_GAMEPAD_BUTTON_LEFT_BUMPER 4 +#define GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER 5 +#define GLFW_GAMEPAD_BUTTON_BACK 6 +#define GLFW_GAMEPAD_BUTTON_START 7 +#define GLFW_GAMEPAD_BUTTON_GUIDE 8 +#define GLFW_GAMEPAD_BUTTON_LEFT_THUMB 9 +#define GLFW_GAMEPAD_BUTTON_RIGHT_THUMB 10 +#define GLFW_GAMEPAD_BUTTON_DPAD_UP 11 +#define GLFW_GAMEPAD_BUTTON_DPAD_RIGHT 12 +#define GLFW_GAMEPAD_BUTTON_DPAD_DOWN 13 +#define GLFW_GAMEPAD_BUTTON_DPAD_LEFT 14 +#define GLFW_GAMEPAD_BUTTON_LAST GLFW_GAMEPAD_BUTTON_DPAD_LEFT + +#define GLFW_GAMEPAD_BUTTON_CROSS GLFW_GAMEPAD_BUTTON_A +#define GLFW_GAMEPAD_BUTTON_CIRCLE GLFW_GAMEPAD_BUTTON_B +#define GLFW_GAMEPAD_BUTTON_SQUARE GLFW_GAMEPAD_BUTTON_X +#define GLFW_GAMEPAD_BUTTON_TRIANGLE GLFW_GAMEPAD_BUTTON_Y +/*! @} */ + +/*! @defgroup gamepad_axes Gamepad axes + * @brief Gamepad axes. + * + * See @ref gamepad for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_GAMEPAD_AXIS_LEFT_X 0 +#define GLFW_GAMEPAD_AXIS_LEFT_Y 1 +#define GLFW_GAMEPAD_AXIS_RIGHT_X 2 +#define GLFW_GAMEPAD_AXIS_RIGHT_Y 3 +#define GLFW_GAMEPAD_AXIS_LEFT_TRIGGER 4 +#define GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER 5 +#define GLFW_GAMEPAD_AXIS_LAST GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER +/*! @} */ + +/*! @defgroup errors Error codes + * @brief Error codes. + * + * See [error handling](@ref error_handling) for how these are used. + * + * @ingroup init + * @{ */ +/*! @brief No error has occurred. + * + * No error has occurred. + * + * @analysis Yay. + */ +#define GLFW_NO_ERROR 0 +/*! @brief GLFW has not been initialized. + * + * This occurs if a GLFW function was called that must not be called unless the + * library is [initialized](@ref intro_init). + * + * @analysis Application programmer error. Initialize GLFW before calling any + * function that requires initialization. + */ +#define GLFW_NOT_INITIALIZED 0x00010001 +/*! @brief No context is current for this thread. + * + * This occurs if a GLFW function was called that needs and operates on the + * current OpenGL or OpenGL ES context but no context is current on the calling + * thread. One such function is @ref glfwSwapInterval. + * + * @analysis Application programmer error. Ensure a context is current before + * calling functions that require a current context. + */ +#define GLFW_NO_CURRENT_CONTEXT 0x00010002 +/*! @brief One of the arguments to the function was an invalid enum value. + * + * One of the arguments to the function was an invalid enum value, for example + * requesting @ref GLFW_RED_BITS with @ref glfwGetWindowAttrib. + * + * @analysis Application programmer error. Fix the offending call. + */ +#define GLFW_INVALID_ENUM 0x00010003 +/*! @brief One of the arguments to the function was an invalid value. + * + * One of the arguments to the function was an invalid value, for example + * requesting a non-existent OpenGL or OpenGL ES version like 2.7. + * + * Requesting a valid but unavailable OpenGL or OpenGL ES version will instead + * result in a @ref GLFW_VERSION_UNAVAILABLE error. + * + * @analysis Application programmer error. Fix the offending call. + */ +#define GLFW_INVALID_VALUE 0x00010004 +/*! @brief A memory allocation failed. + * + * A memory allocation failed. + * + * @analysis A bug in GLFW or the underlying operating system. Report the bug + * to our [issue tracker](https://github.com/glfw/glfw/issues). + */ +#define GLFW_OUT_OF_MEMORY 0x00010005 +/*! @brief GLFW could not find support for the requested API on the system. + * + * GLFW could not find support for the requested API on the system. + * + * @analysis The installed graphics driver does not support the requested + * API, or does not support it via the chosen context creation backend. + * Below are a few examples. + * + * @par + * Some pre-installed Windows graphics drivers do not support OpenGL. AMD only + * supports OpenGL ES via EGL, while Nvidia and Intel only support it via + * a WGL or GLX extension. macOS does not provide OpenGL ES at all. The Mesa + * EGL, OpenGL and OpenGL ES libraries do not interface with the Nvidia binary + * driver. Older graphics drivers do not support Vulkan. + */ +#define GLFW_API_UNAVAILABLE 0x00010006 +/*! @brief The requested OpenGL or OpenGL ES version is not available. + * + * The requested OpenGL or OpenGL ES version (including any requested context + * or framebuffer hints) is not available on this machine. + * + * @analysis The machine does not support your requirements. If your + * application is sufficiently flexible, downgrade your requirements and try + * again. Otherwise, inform the user that their machine does not match your + * requirements. + * + * @par + * Future invalid OpenGL and OpenGL ES versions, for example OpenGL 4.8 if 5.0 + * comes out before the 4.x series gets that far, also fail with this error and + * not @ref GLFW_INVALID_VALUE, because GLFW cannot know what future versions + * will exist. + */ +#define GLFW_VERSION_UNAVAILABLE 0x00010007 +/*! @brief A platform-specific error occurred that does not match any of the + * more specific categories. + * + * A platform-specific error occurred that does not match any of the more + * specific categories. + * + * @analysis A bug or configuration error in GLFW, the underlying operating + * system or its drivers, or a lack of required resources. Report the issue to + * our [issue tracker](https://github.com/glfw/glfw/issues). + */ +#define GLFW_PLATFORM_ERROR 0x00010008 +/*! @brief The requested format is not supported or available. + * + * If emitted during window creation, the requested pixel format is not + * supported. + * + * If emitted when querying the clipboard, the contents of the clipboard could + * not be converted to the requested format. + * + * @analysis If emitted during window creation, one or more + * [hard constraints](@ref window_hints_hard) did not match any of the + * available pixel formats. If your application is sufficiently flexible, + * downgrade your requirements and try again. Otherwise, inform the user that + * their machine does not match your requirements. + * + * @par + * If emitted when querying the clipboard, ignore the error or report it to + * the user, as appropriate. + */ +#define GLFW_FORMAT_UNAVAILABLE 0x00010009 +/*! @brief The specified window does not have an OpenGL or OpenGL ES context. + * + * A window that does not have an OpenGL or OpenGL ES context was passed to + * a function that requires it to have one. + * + * @analysis Application programmer error. Fix the offending call. + */ +#define GLFW_NO_WINDOW_CONTEXT 0x0001000A +/*! @brief The specified cursor shape is not available. + * + * The specified standard cursor shape is not available, either because the + * current system cursor theme does not provide it or because it is not + * available on the platform. + * + * @analysis Platform or system settings limitation. Pick another + * [standard cursor shape](@ref shapes) or create a + * [custom cursor](@ref cursor_custom). + */ +#define GLFW_CURSOR_UNAVAILABLE 0x0001000B +/*! @} */ + +/*! @addtogroup window + * @{ */ +/*! @brief Input focus window hint and attribute + * + * Input focus [window hint](@ref GLFW_FOCUSED_hint) or + * [window attribute](@ref GLFW_FOCUSED_attrib). + */ +#define GLFW_FOCUSED 0x00020001 +/*! @brief Window iconification window attribute + * + * Window iconification [window attribute](@ref GLFW_ICONIFIED_attrib). + */ +#define GLFW_ICONIFIED 0x00020002 +/*! @brief Window resize-ability window hint and attribute + * + * Window resize-ability [window hint](@ref GLFW_RESIZABLE_hint) and + * [window attribute](@ref GLFW_RESIZABLE_attrib). + */ +#define GLFW_RESIZABLE 0x00020003 +/*! @brief Window visibility window hint and attribute + * + * Window visibility [window hint](@ref GLFW_VISIBLE_hint) and + * [window attribute](@ref GLFW_VISIBLE_attrib). + */ +#define GLFW_VISIBLE 0x00020004 +/*! @brief Window decoration window hint and attribute + * + * Window decoration [window hint](@ref GLFW_DECORATED_hint) and + * [window attribute](@ref GLFW_DECORATED_attrib). + */ +#define GLFW_DECORATED 0x00020005 +/*! @brief Window auto-iconification window hint and attribute + * + * Window auto-iconification [window hint](@ref GLFW_AUTO_ICONIFY_hint) and + * [window attribute](@ref GLFW_AUTO_ICONIFY_attrib). + */ +#define GLFW_AUTO_ICONIFY 0x00020006 +/*! @brief Window decoration window hint and attribute + * + * Window decoration [window hint](@ref GLFW_FLOATING_hint) and + * [window attribute](@ref GLFW_FLOATING_attrib). + */ +#define GLFW_FLOATING 0x00020007 +/*! @brief Window maximization window hint and attribute + * + * Window maximization [window hint](@ref GLFW_MAXIMIZED_hint) and + * [window attribute](@ref GLFW_MAXIMIZED_attrib). + */ +#define GLFW_MAXIMIZED 0x00020008 +/*! @brief Cursor centering window hint + * + * Cursor centering [window hint](@ref GLFW_CENTER_CURSOR_hint). + */ +#define GLFW_CENTER_CURSOR 0x00020009 +/*! @brief Window framebuffer transparency hint and attribute + * + * Window framebuffer transparency + * [window hint](@ref GLFW_TRANSPARENT_FRAMEBUFFER_hint) and + * [window attribute](@ref GLFW_TRANSPARENT_FRAMEBUFFER_attrib). + */ +#define GLFW_TRANSPARENT_FRAMEBUFFER 0x0002000A +/*! @brief Mouse cursor hover window attribute. + * + * Mouse cursor hover [window attribute](@ref GLFW_HOVERED_attrib). + */ +#define GLFW_HOVERED 0x0002000B +/*! @brief Input focus on calling show window hint and attribute + * + * Input focus [window hint](@ref GLFW_FOCUS_ON_SHOW_hint) or + * [window attribute](@ref GLFW_FOCUS_ON_SHOW_attrib). + */ +#define GLFW_FOCUS_ON_SHOW 0x0002000C + +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_RED_BITS). + */ +#define GLFW_RED_BITS 0x00021001 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_GREEN_BITS). + */ +#define GLFW_GREEN_BITS 0x00021002 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_BLUE_BITS). + */ +#define GLFW_BLUE_BITS 0x00021003 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ALPHA_BITS). + */ +#define GLFW_ALPHA_BITS 0x00021004 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_DEPTH_BITS). + */ +#define GLFW_DEPTH_BITS 0x00021005 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_STENCIL_BITS). + */ +#define GLFW_STENCIL_BITS 0x00021006 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ACCUM_RED_BITS). + */ +#define GLFW_ACCUM_RED_BITS 0x00021007 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ACCUM_GREEN_BITS). + */ +#define GLFW_ACCUM_GREEN_BITS 0x00021008 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ACCUM_BLUE_BITS). + */ +#define GLFW_ACCUM_BLUE_BITS 0x00021009 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ACCUM_ALPHA_BITS). + */ +#define GLFW_ACCUM_ALPHA_BITS 0x0002100A +/*! @brief Framebuffer auxiliary buffer hint. + * + * Framebuffer auxiliary buffer [hint](@ref GLFW_AUX_BUFFERS). + */ +#define GLFW_AUX_BUFFERS 0x0002100B +/*! @brief OpenGL stereoscopic rendering hint. + * + * OpenGL stereoscopic rendering [hint](@ref GLFW_STEREO). + */ +#define GLFW_STEREO 0x0002100C +/*! @brief Framebuffer MSAA samples hint. + * + * Framebuffer MSAA samples [hint](@ref GLFW_SAMPLES). + */ +#define GLFW_SAMPLES 0x0002100D +/*! @brief Framebuffer sRGB hint. + * + * Framebuffer sRGB [hint](@ref GLFW_SRGB_CAPABLE). + */ +#define GLFW_SRGB_CAPABLE 0x0002100E +/*! @brief Monitor refresh rate hint. + * + * Monitor refresh rate [hint](@ref GLFW_REFRESH_RATE). + */ +#define GLFW_REFRESH_RATE 0x0002100F +/*! @brief Framebuffer double buffering hint. + * + * Framebuffer double buffering [hint](@ref GLFW_DOUBLEBUFFER). + */ +#define GLFW_DOUBLEBUFFER 0x00021010 + +/*! @brief Context client API hint and attribute. + * + * Context client API [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ +#define GLFW_CLIENT_API 0x00022001 +/*! @brief Context client API major version hint and attribute. + * + * Context client API major version [hint](@ref GLFW_CONTEXT_VERSION_MAJOR_hint) + * and [attribute](@ref GLFW_CONTEXT_VERSION_MAJOR_attrib). + */ +#define GLFW_CONTEXT_VERSION_MAJOR 0x00022002 +/*! @brief Context client API minor version hint and attribute. + * + * Context client API minor version [hint](@ref GLFW_CONTEXT_VERSION_MINOR_hint) + * and [attribute](@ref GLFW_CONTEXT_VERSION_MINOR_attrib). + */ +#define GLFW_CONTEXT_VERSION_MINOR 0x00022003 +/*! @brief Context client API revision number hint and attribute. + * + * Context client API revision number + * [attribute](@ref GLFW_CONTEXT_REVISION_attrib). + */ +#define GLFW_CONTEXT_REVISION 0x00022004 +/*! @brief Context robustness hint and attribute. + * + * Context client API revision number [hint](@ref GLFW_CONTEXT_ROBUSTNESS_hint) + * and [attribute](@ref GLFW_CONTEXT_ROBUSTNESS_attrib). + */ +#define GLFW_CONTEXT_ROBUSTNESS 0x00022005 +/*! @brief OpenGL forward-compatibility hint and attribute. + * + * OpenGL forward-compatibility [hint](@ref GLFW_OPENGL_FORWARD_COMPAT_hint) + * and [attribute](@ref GLFW_OPENGL_FORWARD_COMPAT_attrib). + */ +#define GLFW_OPENGL_FORWARD_COMPAT 0x00022006 +/*! @brief OpenGL debug context hint and attribute. + * + * OpenGL debug context [hint](@ref GLFW_OPENGL_DEBUG_CONTEXT_hint) and + * [attribute](@ref GLFW_OPENGL_DEBUG_CONTEXT_attrib). + */ +#define GLFW_OPENGL_DEBUG_CONTEXT 0x00022007 +/*! @brief OpenGL profile hint and attribute. + * + * OpenGL profile [hint](@ref GLFW_OPENGL_PROFILE_hint) and + * [attribute](@ref GLFW_OPENGL_PROFILE_attrib). + */ +#define GLFW_OPENGL_PROFILE 0x00022008 +/*! @brief Context flush-on-release hint and attribute. + * + * Context flush-on-release [hint](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_hint) and + * [attribute](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_attrib). + */ +#define GLFW_CONTEXT_RELEASE_BEHAVIOR 0x00022009 +/*! @brief Context error suppression hint and attribute. + * + * Context error suppression [hint](@ref GLFW_CONTEXT_NO_ERROR_hint) and + * [attribute](@ref GLFW_CONTEXT_NO_ERROR_attrib). + */ +#define GLFW_CONTEXT_NO_ERROR 0x0002200A +/*! @brief Context creation API hint and attribute. + * + * Context creation API [hint](@ref GLFW_CONTEXT_CREATION_API_hint) and + * [attribute](@ref GLFW_CONTEXT_CREATION_API_attrib). + */ +#define GLFW_CONTEXT_CREATION_API 0x0002200B +/*! @brief Window content area scaling window + * [window hint](@ref GLFW_SCALE_TO_MONITOR). + */ +#define GLFW_SCALE_TO_MONITOR 0x0002200C +/*! @brief macOS specific + * [window hint](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint). + */ +#define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001 +/*! @brief macOS specific + * [window hint](@ref GLFW_COCOA_FRAME_NAME_hint). + */ +#define GLFW_COCOA_FRAME_NAME 0x00023002 +/*! @brief macOS specific + * [window hint](@ref GLFW_COCOA_GRAPHICS_SWITCHING_hint). + */ +#define GLFW_COCOA_GRAPHICS_SWITCHING 0x00023003 +/*! @brief X11 specific + * [window hint](@ref GLFW_X11_CLASS_NAME_hint). + */ +#define GLFW_X11_CLASS_NAME 0x00024001 +/*! @brief X11 specific + * [window hint](@ref GLFW_X11_CLASS_NAME_hint). + */ +#define GLFW_X11_INSTANCE_NAME 0x00024002 +#define GLFW_WIN32_KEYBOARD_MENU 0x00025001 +/*! @} */ + +#define GLFW_NO_API 0 +#define GLFW_OPENGL_API 0x00030001 +#define GLFW_OPENGL_ES_API 0x00030002 + +#define GLFW_NO_ROBUSTNESS 0 +#define GLFW_NO_RESET_NOTIFICATION 0x00031001 +#define GLFW_LOSE_CONTEXT_ON_RESET 0x00031002 + +#define GLFW_OPENGL_ANY_PROFILE 0 +#define GLFW_OPENGL_CORE_PROFILE 0x00032001 +#define GLFW_OPENGL_COMPAT_PROFILE 0x00032002 + +#define GLFW_CURSOR 0x00033001 +#define GLFW_STICKY_KEYS 0x00033002 +#define GLFW_STICKY_MOUSE_BUTTONS 0x00033003 +#define GLFW_LOCK_KEY_MODS 0x00033004 +#define GLFW_RAW_MOUSE_MOTION 0x00033005 + +#define GLFW_CURSOR_NORMAL 0x00034001 +#define GLFW_CURSOR_HIDDEN 0x00034002 +#define GLFW_CURSOR_DISABLED 0x00034003 + +#define GLFW_ANY_RELEASE_BEHAVIOR 0 +#define GLFW_RELEASE_BEHAVIOR_FLUSH 0x00035001 +#define GLFW_RELEASE_BEHAVIOR_NONE 0x00035002 + +#define GLFW_NATIVE_CONTEXT_API 0x00036001 +#define GLFW_EGL_CONTEXT_API 0x00036002 +#define GLFW_OSMESA_CONTEXT_API 0x00036003 + +/*! @defgroup shapes Standard cursor shapes + * @brief Standard system cursor shapes. + * + * These are the [standard cursor shapes](@ref cursor_standard) that can be + * requested from the window system. + * + * @ingroup input + * @{ */ + +/*! @brief The regular arrow cursor shape. + * + * The regular arrow cursor shape. + */ +#define GLFW_ARROW_CURSOR 0x00036001 +/*! @brief The text input I-beam cursor shape. + * + * The text input I-beam cursor shape. + */ +#define GLFW_IBEAM_CURSOR 0x00036002 +/*! @brief The crosshair cursor shape. + * + * The crosshair cursor shape. + */ +#define GLFW_CROSSHAIR_CURSOR 0x00036003 +/*! @brief The pointing hand cursor shape. + * + * The pointing hand cursor shape. + */ +#define GLFW_POINTING_HAND_CURSOR 0x00036004 +/*! @brief The horizontal resize/move arrow shape. + * + * The horizontal resize/move arrow shape. This is usually a horizontal + * double-headed arrow. + */ +#define GLFW_RESIZE_EW_CURSOR 0x00036005 +/*! @brief The vertical resize/move arrow shape. + * + * The vertical resize/move shape. This is usually a vertical double-headed + * arrow. + */ +#define GLFW_RESIZE_NS_CURSOR 0x00036006 +/*! @brief The top-left to bottom-right diagonal resize/move arrow shape. + * + * The top-left to bottom-right diagonal resize/move shape. This is usually + * a diagonal double-headed arrow. + * + * @note @macos This shape is provided by a private system API and may fail + * with @ref GLFW_CURSOR_UNAVAILABLE in the future. + * + * @note @x11 This shape is provided by a newer standard not supported by all + * cursor themes. + * + * @note @wayland This shape is provided by a newer standard not supported by + * all cursor themes. + */ +#define GLFW_RESIZE_NWSE_CURSOR 0x00036007 +/*! @brief The top-right to bottom-left diagonal resize/move arrow shape. + * + * The top-right to bottom-left diagonal resize/move shape. This is usually + * a diagonal double-headed arrow. + * + * @note @macos This shape is provided by a private system API and may fail + * with @ref GLFW_CURSOR_UNAVAILABLE in the future. + * + * @note @x11 This shape is provided by a newer standard not supported by all + * cursor themes. + * + * @note @wayland This shape is provided by a newer standard not supported by + * all cursor themes. + */ +#define GLFW_RESIZE_NESW_CURSOR 0x00036008 +/*! @brief The omni-directional resize/move cursor shape. + * + * The omni-directional resize cursor/move shape. This is usually either + * a combined horizontal and vertical double-headed arrow or a grabbing hand. + */ +#define GLFW_RESIZE_ALL_CURSOR 0x00036009 +/*! @brief The operation-not-allowed shape. + * + * The operation-not-allowed shape. This is usually a circle with a diagonal + * line through it. + * + * @note @x11 This shape is provided by a newer standard not supported by all + * cursor themes. + * + * @note @wayland This shape is provided by a newer standard not supported by + * all cursor themes. + */ +#define GLFW_NOT_ALLOWED_CURSOR 0x0003600A +/*! @brief Legacy name for compatibility. + * + * This is an alias for compatibility with earlier versions. + */ +#define GLFW_HRESIZE_CURSOR GLFW_RESIZE_EW_CURSOR +/*! @brief Legacy name for compatibility. + * + * This is an alias for compatibility with earlier versions. + */ +#define GLFW_VRESIZE_CURSOR GLFW_RESIZE_NS_CURSOR +/*! @brief Legacy name for compatibility. + * + * This is an alias for compatibility with earlier versions. + */ +#define GLFW_HAND_CURSOR GLFW_POINTING_HAND_CURSOR +/*! @} */ + +#define GLFW_CONNECTED 0x00040001 +#define GLFW_DISCONNECTED 0x00040002 + +/*! @addtogroup init + * @{ */ +/*! @brief Joystick hat buttons init hint. + * + * Joystick hat buttons [init hint](@ref GLFW_JOYSTICK_HAT_BUTTONS). + */ +#define GLFW_JOYSTICK_HAT_BUTTONS 0x00050001 +/*! @brief macOS specific init hint. + * + * macOS specific [init hint](@ref GLFW_COCOA_CHDIR_RESOURCES_hint). + */ +#define GLFW_COCOA_CHDIR_RESOURCES 0x00051001 +/*! @brief macOS specific init hint. + * + * macOS specific [init hint](@ref GLFW_COCOA_MENUBAR_hint). + */ +#define GLFW_COCOA_MENUBAR 0x00051002 +/*! @} */ + +#define GLFW_DONT_CARE -1 + + +/************************************************************************* + * GLFW API types + *************************************************************************/ + +/*! @brief Client API function pointer type. + * + * Generic function pointer used for returning client API function pointers + * without forcing a cast from a regular pointer. + * + * @sa @ref context_glext + * @sa @ref glfwGetProcAddress + * + * @since Added in version 3.0. + * + * @ingroup context + */ +typedef void (*GLFWglproc)(void); + +/*! @brief Vulkan API function pointer type. + * + * Generic function pointer used for returning Vulkan API function pointers + * without forcing a cast from a regular pointer. + * + * @sa @ref vulkan_proc + * @sa @ref glfwGetInstanceProcAddress + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +typedef void (*GLFWvkproc)(void); + +/*! @brief Opaque monitor object. + * + * Opaque monitor object. + * + * @see @ref monitor_object + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +typedef struct GLFWmonitor GLFWmonitor; + +/*! @brief Opaque window object. + * + * Opaque window object. + * + * @see @ref window_object + * + * @since Added in version 3.0. + * + * @ingroup window + */ +typedef struct GLFWwindow GLFWwindow; + +/*! @brief Opaque cursor object. + * + * Opaque cursor object. + * + * @see @ref cursor_object + * + * @since Added in version 3.1. + * + * @ingroup input + */ +typedef struct GLFWcursor GLFWcursor; + +/*! @brief The function pointer type for error callbacks. + * + * This is the function pointer type for error callbacks. An error callback + * function has the following signature: + * @code + * void callback_name(int error_code, const char* description) + * @endcode + * + * @param[in] error_code An [error code](@ref errors). Future releases may add + * more error codes. + * @param[in] description A UTF-8 encoded string describing the error. + * + * @pointer_lifetime The error description string is valid until the callback + * function returns. + * + * @sa @ref error_handling + * @sa @ref glfwSetErrorCallback + * + * @since Added in version 3.0. + * + * @ingroup init + */ +typedef void (* GLFWerrorfun)(int,const char*); + +/*! @brief The function pointer type for window position callbacks. + * + * This is the function pointer type for window position callbacks. A window + * position callback function has the following signature: + * @code + * void callback_name(GLFWwindow* window, int xpos, int ypos) + * @endcode + * + * @param[in] window The window that was moved. + * @param[in] xpos The new x-coordinate, in screen coordinates, of the + * upper-left corner of the content area of the window. + * @param[in] ypos The new y-coordinate, in screen coordinates, of the + * upper-left corner of the content area of the window. + * + * @sa @ref window_pos + * @sa @ref glfwSetWindowPosCallback + * + * @since Added in version 3.0. + * + * @ingroup window + */ +typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int); + +/*! @brief The function pointer type for window size callbacks. + * + * This is the function pointer type for window size callbacks. A window size + * callback function has the following signature: + * @code + * void callback_name(GLFWwindow* window, int width, int height) + * @endcode + * + * @param[in] window The window that was resized. + * @param[in] width The new width, in screen coordinates, of the window. + * @param[in] height The new height, in screen coordinates, of the window. + * + * @sa @ref window_size + * @sa @ref glfwSetWindowSizeCallback + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int); + +/*! @brief The function pointer type for window close callbacks. + * + * This is the function pointer type for window close callbacks. A window + * close callback function has the following signature: + * @code + * void function_name(GLFWwindow* window) + * @endcode + * + * @param[in] window The window that the user attempted to close. + * + * @sa @ref window_close + * @sa @ref glfwSetWindowCloseCallback + * + * @since Added in version 2.5. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +typedef void (* GLFWwindowclosefun)(GLFWwindow*); + +/*! @brief The function pointer type for window content refresh callbacks. + * + * This is the function pointer type for window content refresh callbacks. + * A window content refresh callback function has the following signature: + * @code + * void function_name(GLFWwindow* window); + * @endcode + * + * @param[in] window The window whose content needs to be refreshed. + * + * @sa @ref window_refresh + * @sa @ref glfwSetWindowRefreshCallback + * + * @since Added in version 2.5. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +typedef void (* GLFWwindowrefreshfun)(GLFWwindow*); + +/*! @brief The function pointer type for window focus callbacks. + * + * This is the function pointer type for window focus callbacks. A window + * focus callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int focused) + * @endcode + * + * @param[in] window The window that gained or lost input focus. + * @param[in] focused `GLFW_TRUE` if the window was given input focus, or + * `GLFW_FALSE` if it lost it. + * + * @sa @ref window_focus + * @sa @ref glfwSetWindowFocusCallback + * + * @since Added in version 3.0. + * + * @ingroup window + */ +typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int); + +/*! @brief The function pointer type for window iconify callbacks. + * + * This is the function pointer type for window iconify callbacks. A window + * iconify callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int iconified) + * @endcode + * + * @param[in] window The window that was iconified or restored. + * @param[in] iconified `GLFW_TRUE` if the window was iconified, or + * `GLFW_FALSE` if it was restored. + * + * @sa @ref window_iconify + * @sa @ref glfwSetWindowIconifyCallback + * + * @since Added in version 3.0. + * + * @ingroup window + */ +typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); + +/*! @brief The function pointer type for window maximize callbacks. + * + * This is the function pointer type for window maximize callbacks. A window + * maximize callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int maximized) + * @endcode + * + * @param[in] window The window that was maximized or restored. + * @param[in] iconified `GLFW_TRUE` if the window was maximized, or + * `GLFW_FALSE` if it was restored. + * + * @sa @ref window_maximize + * @sa glfwSetWindowMaximizeCallback + * + * @since Added in version 3.3. + * + * @ingroup window + */ +typedef void (* GLFWwindowmaximizefun)(GLFWwindow*,int); + +/*! @brief The function pointer type for framebuffer size callbacks. + * + * This is the function pointer type for framebuffer size callbacks. + * A framebuffer size callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int width, int height) + * @endcode + * + * @param[in] window The window whose framebuffer was resized. + * @param[in] width The new width, in pixels, of the framebuffer. + * @param[in] height The new height, in pixels, of the framebuffer. + * + * @sa @ref window_fbsize + * @sa @ref glfwSetFramebufferSizeCallback + * + * @since Added in version 3.0. + * + * @ingroup window + */ +typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int); + +/*! @brief The function pointer type for window content scale callbacks. + * + * This is the function pointer type for window content scale callbacks. + * A window content scale callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, float xscale, float yscale) + * @endcode + * + * @param[in] window The window whose content scale changed. + * @param[in] xscale The new x-axis content scale of the window. + * @param[in] yscale The new y-axis content scale of the window. + * + * @sa @ref window_scale + * @sa @ref glfwSetWindowContentScaleCallback + * + * @since Added in version 3.3. + * + * @ingroup window + */ +typedef void (* GLFWwindowcontentscalefun)(GLFWwindow*,float,float); + +/*! @brief The function pointer type for mouse button callbacks. + * + * This is the function pointer type for mouse button callback functions. + * A mouse button callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int button, int action, int mods) + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] button The [mouse button](@ref buttons) that was pressed or + * released. + * @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`. Future releases + * may add more actions. + * @param[in] mods Bit field describing which [modifier keys](@ref mods) were + * held down. + * + * @sa @ref input_mouse_button + * @sa @ref glfwSetMouseButtonCallback + * + * @since Added in version 1.0. + * @glfw3 Added window handle and modifier mask parameters. + * + * @ingroup input + */ +typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int); + +/*! @brief The function pointer type for cursor position callbacks. + * + * This is the function pointer type for cursor position callbacks. A cursor + * position callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, double xpos, double ypos); + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] xpos The new cursor x-coordinate, relative to the left edge of + * the content area. + * @param[in] ypos The new cursor y-coordinate, relative to the top edge of the + * content area. + * + * @sa @ref cursor_pos + * @sa @ref glfwSetCursorPosCallback + * + * @since Added in version 3.0. Replaces `GLFWmouseposfun`. + * + * @ingroup input + */ +typedef void (* GLFWcursorposfun)(GLFWwindow*,double,double); + +/*! @brief The function pointer type for cursor enter/leave callbacks. + * + * This is the function pointer type for cursor enter/leave callbacks. + * A cursor enter/leave callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int entered) + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] entered `GLFW_TRUE` if the cursor entered the window's content + * area, or `GLFW_FALSE` if it left it. + * + * @sa @ref cursor_enter + * @sa @ref glfwSetCursorEnterCallback + * + * @since Added in version 3.0. + * + * @ingroup input + */ +typedef void (* GLFWcursorenterfun)(GLFWwindow*,int); + +/*! @brief The function pointer type for scroll callbacks. + * + * This is the function pointer type for scroll callbacks. A scroll callback + * function has the following signature: + * @code + * void function_name(GLFWwindow* window, double xoffset, double yoffset) + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] xoffset The scroll offset along the x-axis. + * @param[in] yoffset The scroll offset along the y-axis. + * + * @sa @ref scrolling + * @sa @ref glfwSetScrollCallback + * + * @since Added in version 3.0. Replaces `GLFWmousewheelfun`. + * + * @ingroup input + */ +typedef void (* GLFWscrollfun)(GLFWwindow*,double,double); + +/*! @brief The function pointer type for keyboard key callbacks. + * + * This is the function pointer type for keyboard key callbacks. A keyboard + * key callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int key, int scancode, int action, int mods) + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] key The [keyboard key](@ref keys) that was pressed or released. + * @param[in] scancode The system-specific scancode of the key. + * @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`. Future + * releases may add more actions. + * @param[in] mods Bit field describing which [modifier keys](@ref mods) were + * held down. + * + * @sa @ref input_key + * @sa @ref glfwSetKeyCallback + * + * @since Added in version 1.0. + * @glfw3 Added window handle, scancode and modifier mask parameters. + * + * @ingroup input + */ +typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int); + +/*! @brief The function pointer type for Unicode character callbacks. + * + * This is the function pointer type for Unicode character callbacks. + * A Unicode character callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint) + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] codepoint The Unicode code point of the character. + * + * @sa @ref input_char + * @sa @ref glfwSetCharCallback + * + * @since Added in version 2.4. + * @glfw3 Added window handle parameter. + * + * @ingroup input + */ +typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int); + +/*! @brief The function pointer type for Unicode character with modifiers + * callbacks. + * + * This is the function pointer type for Unicode character with modifiers + * callbacks. It is called for each input character, regardless of what + * modifier keys are held down. A Unicode character with modifiers callback + * function has the following signature: + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint, int mods) + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] codepoint The Unicode code point of the character. + * @param[in] mods Bit field describing which [modifier keys](@ref mods) were + * held down. + * + * @sa @ref input_char + * @sa @ref glfwSetCharModsCallback + * + * @deprecated Scheduled for removal in version 4.0. + * + * @since Added in version 3.1. + * + * @ingroup input + */ +typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int); + +/*! @brief The function pointer type for path drop callbacks. + * + * This is the function pointer type for path drop callbacks. A path drop + * callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int path_count, const char* paths[]) + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] path_count The number of dropped paths. + * @param[in] paths The UTF-8 encoded file and/or directory path names. + * + * @pointer_lifetime The path array and its strings are valid until the + * callback function returns. + * + * @sa @ref path_drop + * @sa @ref glfwSetDropCallback + * + * @since Added in version 3.1. + * + * @ingroup input + */ +typedef void (* GLFWdropfun)(GLFWwindow*,int,const char*[]); + +/*! @brief The function pointer type for monitor configuration callbacks. + * + * This is the function pointer type for monitor configuration callbacks. + * A monitor callback function has the following signature: + * @code + * void function_name(GLFWmonitor* monitor, int event) + * @endcode + * + * @param[in] monitor The monitor that was connected or disconnected. + * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Future + * releases may add more events. + * + * @sa @ref monitor_event + * @sa @ref glfwSetMonitorCallback + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); + +/*! @brief The function pointer type for joystick configuration callbacks. + * + * This is the function pointer type for joystick configuration callbacks. + * A joystick configuration callback function has the following signature: + * @code + * void function_name(int jid, int event) + * @endcode + * + * @param[in] jid The joystick that was connected or disconnected. + * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Future + * releases may add more events. + * + * @sa @ref joystick_event + * @sa @ref glfwSetJoystickCallback + * + * @since Added in version 3.2. + * + * @ingroup input + */ +typedef void (* GLFWjoystickfun)(int,int); + +/*! @brief Video mode type. + * + * This describes a single video mode. + * + * @sa @ref monitor_modes + * @sa @ref glfwGetVideoMode + * @sa @ref glfwGetVideoModes + * + * @since Added in version 1.0. + * @glfw3 Added refresh rate member. + * + * @ingroup monitor + */ +typedef struct GLFWvidmode +{ + /*! The width, in screen coordinates, of the video mode. + */ + int width; + /*! The height, in screen coordinates, of the video mode. + */ + int height; + /*! The bit depth of the red channel of the video mode. + */ + int redBits; + /*! The bit depth of the green channel of the video mode. + */ + int greenBits; + /*! The bit depth of the blue channel of the video mode. + */ + int blueBits; + /*! The refresh rate, in Hz, of the video mode. + */ + int refreshRate; +} GLFWvidmode; + +/*! @brief Gamma ramp. + * + * This describes the gamma ramp for a monitor. + * + * @sa @ref monitor_gamma + * @sa @ref glfwGetGammaRamp + * @sa @ref glfwSetGammaRamp + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +typedef struct GLFWgammaramp +{ + /*! An array of value describing the response of the red channel. + */ + unsigned short* red; + /*! An array of value describing the response of the green channel. + */ + unsigned short* green; + /*! An array of value describing the response of the blue channel. + */ + unsigned short* blue; + /*! The number of elements in each array. + */ + unsigned int size; +} GLFWgammaramp; + +/*! @brief Image data. + * + * This describes a single 2D image. See the documentation for each related + * function what the expected pixel format is. + * + * @sa @ref cursor_custom + * @sa @ref window_icon + * + * @since Added in version 2.1. + * @glfw3 Removed format and bytes-per-pixel members. + * + * @ingroup window + */ +typedef struct GLFWimage +{ + /*! The width, in pixels, of this image. + */ + int width; + /*! The height, in pixels, of this image. + */ + int height; + /*! The pixel data of this image, arranged left-to-right, top-to-bottom. + */ + unsigned char* pixels; +} GLFWimage; + +/*! @brief Gamepad input state + * + * This describes the input state of a gamepad. + * + * @sa @ref gamepad + * @sa @ref glfwGetGamepadState + * + * @since Added in version 3.3. + * + * @ingroup input + */ +typedef struct GLFWgamepadstate +{ + /*! The states of each [gamepad button](@ref gamepad_buttons), `GLFW_PRESS` + * or `GLFW_RELEASE`. + */ + unsigned char buttons[15]; + /*! The states of each [gamepad axis](@ref gamepad_axes), in the range -1.0 + * to 1.0 inclusive. + */ + float axes[6]; +} GLFWgamepadstate; + + +/************************************************************************* + * GLFW API functions + *************************************************************************/ + +/*! @brief Initializes the GLFW library. + * + * This function initializes the GLFW library. Before most GLFW functions can + * be used, GLFW must be initialized, and before an application terminates GLFW + * should be terminated in order to free any resources allocated during or + * after initialization. + * + * If this function fails, it calls @ref glfwTerminate before returning. If it + * succeeds, you should call @ref glfwTerminate before the application exits. + * + * Additional calls to this function after successful initialization but before + * termination will return `GLFW_TRUE` immediately. + * + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. + * + * @remark @macos This function will change the current directory of the + * application to the `Contents/Resources` subdirectory of the application's + * bundle, if present. This can be disabled with the @ref + * GLFW_COCOA_CHDIR_RESOURCES init hint. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref intro_init + * @sa @ref glfwTerminate + * + * @since Added in version 1.0. + * + * @ingroup init + */ +GLFWAPI int glfwInit(void); + +/*! @brief Terminates the GLFW library. + * + * This function destroys all remaining windows and cursors, restores any + * modified gamma ramps and frees any other allocated resources. Once this + * function is called, you must again call @ref glfwInit successfully before + * you will be able to use most GLFW functions. + * + * If GLFW has been successfully initialized, this function should be called + * before the application exits. If initialization fails, there is no need to + * call this function, as it is called by @ref glfwInit before it returns + * failure. + * + * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. + * + * @remark This function may be called before @ref glfwInit. + * + * @warning The contexts of any remaining windows must not be current on any + * other thread when this function is called. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref intro_init + * @sa @ref glfwInit + * + * @since Added in version 1.0. + * + * @ingroup init + */ +GLFWAPI void glfwTerminate(void); + +/*! @brief Sets the specified init hint to the desired value. + * + * This function sets hints for the next initialization of GLFW. + * + * The values you set hints to are never reset by GLFW, but they only take + * effect during initialization. Once GLFW has been initialized, any values + * you set will be ignored until the library is terminated and initialized + * again. + * + * 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. + * + * @param[in] hint The [init hint](@ref init_hints) to set. + * @param[in] value The new value of the init hint. + * + * @errors Possible errors include @ref GLFW_INVALID_ENUM and @ref + * GLFW_INVALID_VALUE. + * + * @remarks This function may be called before @ref glfwInit. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa init_hints + * @sa glfwInit + * + * @since Added in version 3.3. + * + * @ingroup init + */ +GLFWAPI void glfwInitHint(int hint, int value); + +/*! @brief Retrieves the version of the GLFW library. + * + * This function retrieves the major, minor and revision numbers of the GLFW + * library. It is intended for when you are using GLFW as a shared library and + * want to ensure that you are using the minimum required version. + * + * Any or all of the version arguments may be `NULL`. + * + * @param[out] major Where to store the major version number, or `NULL`. + * @param[out] minor Where to store the minor version number, or `NULL`. + * @param[out] rev Where to store the revision number, or `NULL`. + * + * @errors None. + * + * @remark This function may be called before @ref glfwInit. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref intro_version + * @sa @ref glfwGetVersionString + * + * @since Added in version 1.0. + * + * @ingroup init + */ +GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev); + +/*! @brief Returns a string describing the compile-time configuration. + * + * This function returns the compile-time generated + * [version string](@ref intro_version_string) of the GLFW library binary. It + * describes the version, platform, compiler and any platform-specific + * compile-time options. It should not be confused with the OpenGL or OpenGL + * ES version string, queried with `glGetString`. + * + * __Do not use the version string__ to parse the GLFW library version. The + * @ref glfwGetVersion function provides the version of the running library + * binary in numerical format. + * + * @return The ASCII encoded GLFW version string. + * + * @errors None. + * + * @remark This function may be called before @ref glfwInit. + * + * @pointer_lifetime The returned string is static and compile-time generated. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref intro_version + * @sa @ref glfwGetVersion + * + * @since Added in version 3.0. + * + * @ingroup init + */ +GLFWAPI const char* glfwGetVersionString(void); + +/*! @brief Returns and clears the last error for the calling thread. + * + * This function returns and clears the [error code](@ref errors) of the last + * error that occurred on the calling thread, and optionally a UTF-8 encoded + * human-readable description of it. If no error has occurred since the last + * call, it returns @ref GLFW_NO_ERROR (zero) and the description pointer is + * set to `NULL`. + * + * @param[in] description Where to store the error description pointer, or `NULL`. + * @return The last error code for the calling thread, or @ref GLFW_NO_ERROR + * (zero). + * + * @errors None. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is guaranteed to be valid only until the + * next error occurs or the library is terminated. + * + * @remark This function may be called before @ref glfwInit. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref error_handling + * @sa @ref glfwSetErrorCallback + * + * @since Added in version 3.3. + * + * @ingroup init + */ +GLFWAPI int glfwGetError(const char** description); + +/*! @brief Sets the error callback. + * + * This function sets the error callback, which is called with an error code + * and a human-readable description each time a GLFW error occurs. + * + * The error code is set before the callback is called. Calling @ref + * glfwGetError from the error callback will return the same value as the error + * code argument. + * + * The error callback is called on the thread where the error occurred. If you + * are using GLFW from multiple threads, your error callback needs to be + * written accordingly. + * + * Because the description string may have been generated specifically for that + * error, it is not guaranteed to be valid after the callback has returned. If + * you wish to use it after the callback returns, you need to make a copy. + * + * Once set, the error callback remains set even after the library has been + * terminated. + * + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set. + * + * @callback_signature + * @code + * void callback_name(int error_code, const char* description) + * @endcode + * For more information about the callback parameters, see the + * [callback pointer type](@ref GLFWerrorfun). + * + * @errors None. + * + * @remark This function may be called before @ref glfwInit. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref error_handling + * @sa @ref glfwGetError + * + * @since Added in version 3.0. + * + * @ingroup init + */ +GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun callback); + +/*! @brief Returns the currently connected monitors. + * + * This function returns an array of handles for all currently connected + * monitors. The primary monitor is always first in the returned array. If no + * monitors were found, this function returns `NULL`. + * + * @param[out] count Where to store the number of monitors in the returned + * array. This is set to zero if an error occurred. + * @return An array of monitor handles, or `NULL` if no monitors were found or + * if an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is guaranteed to be valid only until the + * monitor configuration changes or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_monitors + * @sa @ref monitor_event + * @sa @ref glfwGetPrimaryMonitor + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI GLFWmonitor** glfwGetMonitors(int* count); + +/*! @brief Returns the primary monitor. + * + * This function returns the primary monitor. This is usually the monitor + * where elements like the task bar or global menu bar are located. + * + * @return The primary monitor, or `NULL` if no monitors were found or if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @remark The primary monitor is always first in the array returned by @ref + * glfwGetMonitors. + * + * @sa @ref monitor_monitors + * @sa @ref glfwGetMonitors + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void); + +/*! @brief Returns the position of the monitor's viewport on the virtual screen. + * + * This function returns the position, in screen coordinates, of the upper-left + * corner of the specified monitor. + * + * Any or all of the position arguments may be `NULL`. If an error occurs, all + * non-`NULL` position arguments will be set to zero. + * + * @param[in] monitor The monitor to query. + * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`. + * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_properties + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); + +/*! @brief Retrieves the work area of the monitor. + * + * This function returns the position, in screen coordinates, of the upper-left + * corner of the work area of the specified monitor along with the work area + * size in screen coordinates. The work area is defined as the area of the + * monitor not occluded by the operating system task bar where present. If no + * task bar exists then the work area is the monitor resolution in screen + * coordinates. + * + * Any or all of the position and size arguments may be `NULL`. If an error + * occurs, all non-`NULL` position and size arguments will be set to zero. + * + * @param[in] monitor The monitor to query. + * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`. + * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`. + * @param[out] width Where to store the monitor width, or `NULL`. + * @param[out] height Where to store the monitor height, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_workarea + * + * @since Added in version 3.3. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height); + +/*! @brief Returns the physical size of the monitor. + * + * This function returns the size, in millimetres, of the display area of the + * specified monitor. + * + * Some systems do not provide accurate monitor size information, either + * because the monitor + * [EDID](https://en.wikipedia.org/wiki/Extended_display_identification_data) + * data is incorrect or because the driver does not report it accurately. + * + * Any or all of the size arguments may be `NULL`. If an error occurs, all + * non-`NULL` size arguments will be set to zero. + * + * @param[in] monitor The monitor to query. + * @param[out] widthMM Where to store the width, in millimetres, of the + * monitor's display area, or `NULL`. + * @param[out] heightMM Where to store the height, in millimetres, of the + * monitor's display area, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark @win32 calculates the returned physical size from the + * current resolution and system DPI instead of querying the monitor EDID data. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_properties + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* widthMM, int* heightMM); + +/*! @brief Retrieves the content scale for the specified monitor. + * + * This function retrieves the content scale for the specified monitor. The + * content scale is the ratio between the current DPI and the platform's + * default DPI. This is especially important for text and any UI elements. If + * the pixel dimensions of your UI scaled by this look appropriate on your + * machine then it should appear at a reasonable size on other machines + * regardless of their DPI and scaling settings. This relies on the system DPI + * and scaling settings being somewhat correct. + * + * 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. + * + * @param[in] monitor The monitor to query. + * @param[out] xscale Where to store the x-axis content scale, or `NULL`. + * @param[out] yscale Where to store the y-axis content scale, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_scale + * @sa @ref glfwGetWindowContentScale + * + * @since Added in version 3.3. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* monitor, float* xscale, float* yscale); + +/*! @brief Returns the name of the specified monitor. + * + * This function returns a human-readable name, encoded as UTF-8, of the + * specified monitor. The name typically reflects the make and model of the + * monitor and is not guaranteed to be unique among the connected monitors. + * + * @param[in] monitor The monitor to query. + * @return The UTF-8 encoded name of the monitor, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified monitor is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_properties + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* monitor); + +/*! @brief Sets the user pointer of the specified monitor. + * + * This function sets the user-defined pointer of the specified monitor. The + * current value is retained until the monitor is disconnected. The initial + * value is `NULL`. + * + * This function may be called from the monitor callback, even for a monitor + * that is being disconnected. + * + * @param[in] monitor The monitor whose pointer to set. + * @param[in] pointer The new value. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref monitor_userptr + * @sa @ref glfwGetMonitorUserPointer + * + * @since Added in version 3.3. + * + * @ingroup monitor + */ +GLFWAPI void glfwSetMonitorUserPointer(GLFWmonitor* monitor, void* pointer); + +/*! @brief Returns the user pointer of the specified monitor. + * + * This function returns the current value of the user-defined pointer of the + * specified monitor. The initial value is `NULL`. + * + * This function may be called from the monitor callback, even for a monitor + * that is being disconnected. + * + * @param[in] monitor The monitor whose pointer to return. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref monitor_userptr + * @sa @ref glfwSetMonitorUserPointer + * + * @since Added in version 3.3. + * + * @ingroup monitor + */ +GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* monitor); + +/*! @brief Sets the monitor configuration callback. + * + * This function sets the monitor configuration callback, or removes the + * currently set callback. This is called when a monitor is connected to or + * disconnected from the system. + * + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWmonitor* monitor, int event) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWmonitorfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_event + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun callback); + +/*! @brief Returns the available video modes for the specified monitor. + * + * This function returns an array of all video modes supported by the specified + * monitor. The returned array is sorted in ascending order, first by color + * bit depth (the sum of all channel depths) and then by resolution area (the + * product of width and height). + * + * @param[in] monitor The monitor to query. + * @param[out] count Where to store the number of video modes in the returned + * array. This is set to zero if an error occurred. + * @return An array of video modes, 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 array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified monitor is + * disconnected, this function is called again for that monitor or the library + * is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_modes + * @sa @ref glfwGetVideoMode + * + * @since Added in version 1.0. + * @glfw3 Changed to return an array of modes for a specific monitor. + * + * @ingroup monitor + */ +GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* monitor, int* count); + +/*! @brief Returns the current mode of the specified monitor. + * + * This function returns the current video mode of the specified monitor. If + * you have created a full screen window for that monitor, the return value + * will depend on whether that window is iconified. + * + * @param[in] monitor The monitor to query. + * @return The current mode of the monitor, 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 array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified monitor is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_modes + * @sa @ref glfwGetVideoModes + * + * @since Added in version 3.0. Replaces `glfwGetDesktopMode`. + * + * @ingroup monitor + */ +GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor); + +/*! @brief Generates a gamma ramp and sets it for the specified monitor. + * + * This function generates an appropriately sized gamma ramp from the specified + * exponent and then calls @ref glfwSetGammaRamp with it. The value must be + * a finite number greater than zero. + * + * The software controlled gamma ramp is applied _in addition_ to the hardware + * gamma correction, which today is usually an approximation of sRGB gamma. + * This means that setting a perfectly linear ramp, or gamma 1.0, will produce + * the default (usually sRGB-like) behavior. + * + * For gamma correct rendering with OpenGL or OpenGL ES, see the @ref + * GLFW_SRGB_CAPABLE hint. + * + * @param[in] monitor The monitor whose gamma ramp to set. + * @param[in] gamma The desired exponent. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @remark @wayland Gamma handling is a privileged protocol, this function + * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_gamma + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI void glfwSetGamma(GLFWmonitor* monitor, float gamma); + +/*! @brief Returns the current gamma ramp for the specified monitor. + * + * This function returns the current gamma ramp of the specified monitor. + * + * @param[in] monitor The monitor to query. + * @return The current gamma ramp, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland Gamma handling is a privileged protocol, this function + * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR while + * returning `NULL`. + * + * @pointer_lifetime The returned structure and its arrays are allocated and + * freed by GLFW. You should not free them yourself. They are valid until the + * specified monitor is disconnected, this function is called again for that + * monitor or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_gamma + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor); + +/*! @brief Sets the current gamma ramp for the specified monitor. + * + * This function sets the current gamma ramp for the specified monitor. The + * original gamma ramp for that monitor is saved by GLFW the first time this + * function is called and is restored by @ref glfwTerminate. + * + * The software controlled gamma ramp is applied _in addition_ to the hardware + * gamma correction, which today is usually an approximation of sRGB gamma. + * This means that setting a perfectly linear ramp, or gamma 1.0, will produce + * the default (usually sRGB-like) behavior. + * + * For gamma correct rendering with OpenGL or OpenGL ES, see the @ref + * GLFW_SRGB_CAPABLE hint. + * + * @param[in] monitor The monitor whose gamma ramp to set. + * @param[in] ramp The gamma ramp to use. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark The size of the specified gamma ramp should match the size of the + * current ramp for that monitor. + * + * @remark @win32 The gamma ramp size must be 256. + * + * @remark @wayland Gamma handling is a privileged protocol, this function + * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The specified gamma ramp is copied before this function + * returns. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_gamma + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI void glfwSetGammaRamp(GLFWmonitor* monitor, const GLFWgammaramp* ramp); + +/*! @brief Resets all window hints to their default values. + * + * This function resets all window hints to their + * [default values](@ref window_hints_values). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_hints + * @sa @ref glfwWindowHint + * @sa @ref glfwWindowHintString + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwDefaultWindowHints(void); + +/*! @brief Sets the specified window hint to the desired value. + * + * This function sets hints for the next call to @ref glfwCreateWindow. The + * hints, once set, retain their values until changed by a call to this + * function or @ref glfwDefaultWindowHints, or until the library is terminated. + * + * Only integer value hints can be set with this function. String value hints + * are set with @ref glfwWindowHintString. + * + * This function does not check whether the specified hint values are valid. + * If you set hints to invalid values this will instead be reported by the next + * call to @ref glfwCreateWindow. + * + * 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. + * + * @param[in] hint The [window hint](@ref window_hints) to set. + * @param[in] value The new value of the window hint. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_hints + * @sa @ref glfwWindowHintString + * @sa @ref glfwDefaultWindowHints + * + * @since Added in version 3.0. Replaces `glfwOpenWindowHint`. + * + * @ingroup window + */ +GLFWAPI void glfwWindowHint(int hint, int value); + +/*! @brief Sets the specified window hint to the desired value. + * + * This function sets hints for the next call to @ref glfwCreateWindow. The + * hints, once set, retain their values until changed by a call to this + * function or @ref glfwDefaultWindowHints, or until the library is terminated. + * + * Only string type hints can be set with this function. Integer value hints + * are set with @ref glfwWindowHint. + * + * This function does not check whether the specified hint values are valid. + * If you set hints to invalid values this will instead be reported by the next + * call to @ref glfwCreateWindow. + * + * 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. + * + * @param[in] hint The [window hint](@ref window_hints) to set. + * @param[in] value The new value of the window hint. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @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 window_hints + * @sa @ref glfwWindowHint + * @sa @ref glfwDefaultWindowHints + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwWindowHintString(int hint, const char* value); + +/*! @brief Creates a window and its associated context. + * + * This function creates a window and its associated OpenGL or OpenGL ES + * context. Most of the options controlling how the window and its context + * should be created are specified with [window hints](@ref window_hints). + * + * Successful creation does not change which context is current. Before you + * can use the newly created context, you need to + * [make it current](@ref context_current). For information about the `share` + * parameter, see @ref context_sharing. + * + * The created window, framebuffer and context may differ from what you + * requested, as not all parameters and hints are + * [hard constraints](@ref window_hints_hard). This includes the size of the + * window, especially for full screen windows. To query the actual attributes + * of the created window, framebuffer and context, see @ref + * glfwGetWindowAttrib, @ref glfwGetWindowSize and @ref glfwGetFramebufferSize. + * + * To create a full screen window, you need to specify the monitor the window + * will cover. If no monitor is specified, the window will be windowed mode. + * Unless you have a way for the user to choose a specific monitor, it is + * recommended that you pick the primary monitor. For more information on how + * to query connected monitors, see @ref monitor_monitors. + * + * For full screen windows, the specified size becomes the resolution of the + * window's _desired video mode_. As long as a full screen window is not + * iconified, the supported video mode most closely matching the desired video + * mode is set for the specified monitor. For more information about full + * screen windows, including the creation of so called _windowed full screen_ + * or _borderless full screen_ windows, see @ref window_windowed_full_screen. + * + * Once you have created the window, you can switch it between windowed and + * full screen mode with @ref glfwSetWindowMonitor. This will not affect its + * OpenGL or OpenGL ES context. + * + * By default, newly created windows use the placement recommended by the + * window system. To create the window at a specific position, make it + * initially invisible using the [GLFW_VISIBLE](@ref GLFW_VISIBLE_hint) window + * hint, set its [position](@ref window_pos) and then [show](@ref window_hide) + * it. + * + * As long as at least one full screen window is not iconified, the screensaver + * is prohibited from starting. + * + * Window systems put limits on window sizes. Very large or very small window + * dimensions may be overridden by the window system on creation. Check the + * actual [size](@ref window_size) after creation. + * + * The [swap interval](@ref buffer_swap) is not set during window creation and + * the initial value may vary depending on driver settings and defaults. + * + * @param[in] width The desired width, in screen coordinates, of the window. + * This must be greater than zero. + * @param[in] height The desired height, in screen coordinates, of the window. + * This must be greater than zero. + * @param[in] title The initial, UTF-8 encoded window title. + * @param[in] monitor The monitor to use for full screen mode, or `NULL` for + * windowed mode. + * @param[in] share The window whose context to share resources with, or `NULL` + * to not share resources. + * @return The handle of the created window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE, @ref GLFW_API_UNAVAILABLE, @ref + * GLFW_VERSION_UNAVAILABLE, @ref GLFW_FORMAT_UNAVAILABLE and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @win32 Window creation will fail if the Microsoft GDI software + * OpenGL implementation is the only one available. + * + * @remark @win32 If the executable has an icon resource named `GLFW_ICON,` it + * will be set as the initial icon for the window. If no such icon is present, + * the `IDI_APPLICATION` icon will be used instead. To set a different icon, + * see @ref glfwSetWindowIcon. + * + * @remark @win32 The context to share resources with must not be current on + * any other thread. + * + * @remark @macos The OS only supports core profile contexts for OpenGL + * versions 3.2 and later. Before creating an OpenGL context of version 3.2 or + * later you must set the [GLFW_OPENGL_PROFILE](@ref GLFW_OPENGL_PROFILE_hint) + * hint accordingly. OpenGL 3.0 and 3.1 contexts are not supported at all + * on macOS. + * + * @remark @macos The GLFW window has no icon, as it is not a document + * window, but the dock icon will be the same as the application bundle's icon. + * For more information on bundles, see the + * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) + * in the Mac Developer Library. + * + * @remark @macos The first time a window is created the menu bar is created. + * If GLFW finds a `MainMenu.nib` it is loaded and assumed to contain a menu + * bar. Otherwise a minimal menu bar is created manually with common commands + * like Hide, Quit and About. The About entry opens a minimal about dialog + * with information from the application's bundle. Menu bar creation can be + * disabled entirely with the @ref GLFW_COCOA_MENUBAR init hint. + * + * @remark @macos On OS X 10.10 and later the window frame will not be rendered + * at full resolution on Retina displays unless the + * [GLFW_COCOA_RETINA_FRAMEBUFFER](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint) + * hint is `GLFW_TRUE` and the `NSHighResolutionCapable` key is enabled in the + * application bundle's `Info.plist`. For more information, see + * [High Resolution Guidelines for OS X](https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html) + * in the Mac Developer Library. The GLFW test and example programs use + * a custom `Info.plist` template for this, which can be found as + * `CMake/Info.plist.in` in the source tree. + * + * @remark @macos When activating frame autosaving with + * [GLFW_COCOA_FRAME_NAME](@ref GLFW_COCOA_FRAME_NAME_hint), the specified + * window size and position may be overridden by previously saved values. + * + * @remark @x11 Some window managers will not respect the placement of + * initially hidden windows. + * + * @remark @x11 Due to the asynchronous nature of X11, it may take a moment for + * a window to reach its requested state. This means you may not be able to + * query the final size, position or other attributes directly after window + * creation. + * + * @remark @x11 The class part of the `WM_CLASS` window property will by + * default be set to the window title passed to this function. The instance + * part will use the contents of the `RESOURCE_NAME` environment variable, if + * present and not empty, or fall back to the window title. Set the + * [GLFW_X11_CLASS_NAME](@ref GLFW_X11_CLASS_NAME_hint) and + * [GLFW_X11_INSTANCE_NAME](@ref GLFW_X11_INSTANCE_NAME_hint) window hints to + * override this. + * + * @remark @wayland Compositors should implement the xdg-decoration protocol + * for GLFW to decorate the window properly. If this protocol isn't + * supported, or if the compositor prefers client-side decorations, a very + * simple fallback frame will be drawn using the wp_viewporter protocol. A + * compositor can still emit close, maximize or fullscreen events, using for + * instance a keybind mechanism. If neither of these protocols is supported, + * the window won't be decorated. + * + * @remark @wayland A full screen window will not attempt to change the mode, + * no matter what the requested size or refresh rate. + * + * @remark @wayland Screensaver inhibition requires the idle-inhibit protocol + * to be implemented in the user's compositor. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_creation + * @sa @ref glfwDestroyWindow + * + * @since Added in version 3.0. Replaces `glfwOpenWindow`. + * + * @ingroup window + */ +GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share); + +/*! @brief Destroys the specified window and its context. + * + * This function destroys the specified window and its context. On calling + * this function, no further callbacks will be called for that window. + * + * If the context of the specified window is current on the main thread, it is + * detached before being destroyed. + * + * @param[in] window The window to destroy. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @note The context of the specified window must not be current on any other + * thread when this function is called. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_creation + * @sa @ref glfwCreateWindow + * + * @since Added in version 3.0. Replaces `glfwCloseWindow`. + * + * @ingroup window + */ +GLFWAPI void glfwDestroyWindow(GLFWwindow* window); + +/*! @brief Checks the close flag of the specified window. + * + * This function returns the value of the close flag of the specified window. + * + * @param[in] window The window to query. + * @return The value of the close flag. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref window_close + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI int glfwWindowShouldClose(GLFWwindow* window); + +/*! @brief Sets the close flag of the specified window. + * + * This function sets the value of the close flag of the specified window. + * This can be used to override the user's attempt to close the window, or + * to signal that it should be closed. + * + * @param[in] window The window whose flag to change. + * @param[in] value The new value. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref window_close + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value); + +/*! @brief Sets the title of the specified window. + * + * This function sets the window title, encoded as UTF-8, of the specified + * window. + * + * @param[in] window The window whose title to change. + * @param[in] title The UTF-8 encoded window title. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @macos The window title will not be updated until the next time you + * process events. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_title + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); + +/*! @brief Sets the icon for the specified window. + * + * This function sets the icon of the specified window. If passed an array of + * candidate images, those of or closest to the sizes desired by the system are + * selected. If no images are specified, the window reverts to its default + * icon. + * + * The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight + * bits per channel with the red channel first. They are arranged canonically + * as packed sequential rows, starting from the top-left corner. + * + * The desired image sizes varies depending on platform and system settings. + * The selected images will be rescaled as needed. Good sizes include 16x16, + * 32x32 and 48x48. + * + * @param[in] window The window whose icon to set. + * @param[in] count The number of images in the specified array, or zero to + * revert to the default window icon. + * @param[in] images The images to create the icon from. This is ignored if + * count is zero. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The specified image data is copied before this function + * returns. + * + * @remark @macos The GLFW window has no icon, as it is not a document + * window, so this function does nothing. The dock icon will be the same as + * the application bundle's icon. For more information on bundles, see the + * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) + * in the Mac Developer Library. + * + * @remark @wayland There is no existing protocol to change an icon, the + * window will thus inherit the one defined in the application's desktop file. + * This function always emits @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_icon + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* images); + +/*! @brief Retrieves the position of the content area of the specified window. + * + * This function retrieves the position, in screen coordinates, of the + * upper-left corner of the content area of the specified window. + * + * Any or all of the position arguments may be `NULL`. If an error occurs, all + * non-`NULL` position arguments will be set to zero. + * + * @param[in] window The window to query. + * @param[out] xpos Where to store the x-coordinate of the upper-left corner of + * the content area, or `NULL`. + * @param[out] ypos Where to store the y-coordinate of the upper-left corner of + * the content area, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland There is no way for an application to retrieve the global + * position of its windows, this function will always emit @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_pos + * @sa @ref glfwSetWindowPos + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); + +/*! @brief Sets the position of the content area of the specified window. + * + * This function sets the position, in screen coordinates, of the upper-left + * corner of the content area of the specified windowed mode window. If the + * window is a full screen window, this function does nothing. + * + * __Do not use this function__ to move an already visible window unless you + * have very good reasons for doing so, as it will confuse and annoy the user. + * + * The window manager may put limits on what positions are allowed. GLFW + * cannot and should not override these limits. + * + * @param[in] window The window to query. + * @param[in] xpos The x-coordinate of the upper-left corner of the content area. + * @param[in] ypos The y-coordinate of the upper-left corner of the content area. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland There is no way for an application to set the global + * position of its windows, this function will always emit @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_pos + * @sa @ref glfwGetWindowPos + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos); + +/*! @brief Retrieves the size of the content area of the specified window. + * + * This function retrieves the size, in screen coordinates, of the content area + * of the specified window. If you wish to retrieve the size of the + * framebuffer of the window in pixels, see @ref glfwGetFramebufferSize. + * + * Any or all of the size arguments may be `NULL`. If an error occurs, all + * non-`NULL` size arguments will be set to zero. + * + * @param[in] window The window whose size to retrieve. + * @param[out] width Where to store the width, in screen coordinates, of the + * content area, or `NULL`. + * @param[out] height Where to store the height, in screen coordinates, of the + * content area, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_size + * @sa @ref glfwSetWindowSize + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); + +/*! @brief Sets the size limits of the specified window. + * + * This function sets the size limits of the content area of the specified + * window. If the window is full screen, the size limits only take effect + * once it is made windowed. If the window is not resizable, this function + * does nothing. + * + * The size limits are applied immediately to a windowed mode window and may + * cause it to be resized. + * + * The maximum dimensions must be greater than or equal to the minimum + * dimensions and all must be greater than or equal to zero. + * + * @param[in] window The window to set limits for. + * @param[in] minwidth The minimum width, in screen coordinates, of the content + * area, or `GLFW_DONT_CARE`. + * @param[in] minheight The minimum height, in screen coordinates, of the + * content area, or `GLFW_DONT_CARE`. + * @param[in] maxwidth The maximum width, in screen coordinates, of the content + * area, or `GLFW_DONT_CARE`. + * @param[in] maxheight The maximum height, in screen coordinates, of the + * content area, or `GLFW_DONT_CARE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @remark If you set size limits and an aspect ratio that conflict, the + * results are undefined. + * + * @remark @wayland The size limits will not be applied until the window is + * actually resized, either by the user or by the compositor. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_sizelimits + * @sa @ref glfwSetWindowAspectRatio + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight); + +/*! @brief Sets the aspect ratio of the specified window. + * + * This function sets the required aspect ratio of the content area of the + * specified window. If the window is full screen, the aspect ratio only takes + * effect once it is made windowed. If the window is not resizable, this + * function does nothing. + * + * The aspect ratio is specified as a numerator and a denominator and both + * values must be greater than zero. For example, the common 16:9 aspect ratio + * is specified as 16 and 9, respectively. + * + * If the numerator and denominator is set to `GLFW_DONT_CARE` then the aspect + * ratio limit is disabled. + * + * The aspect ratio is applied immediately to a windowed mode window and may + * cause it to be resized. + * + * @param[in] window The window to set limits for. + * @param[in] numer The numerator of the desired aspect ratio, or + * `GLFW_DONT_CARE`. + * @param[in] denom The denominator of the desired aspect ratio, or + * `GLFW_DONT_CARE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @remark If you set size limits and an aspect ratio that conflict, the + * results are undefined. + * + * @remark @wayland The aspect ratio will not be applied until the window is + * actually resized, either by the user or by the compositor. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_sizelimits + * @sa @ref glfwSetWindowSizeLimits + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom); + +/*! @brief Sets the size of the content area of the specified window. + * + * This function sets the size, in screen coordinates, of the content area of + * the specified window. + * + * For full screen windows, this function updates the resolution of its desired + * video mode and switches to the video mode closest to it, without affecting + * the window's context. As the context is unaffected, the bit depths of the + * framebuffer remain unchanged. + * + * If you wish to update the refresh rate of the desired video mode in addition + * to its resolution, see @ref glfwSetWindowMonitor. + * + * The window manager may put limits on what sizes are allowed. GLFW cannot + * and should not override these limits. + * + * @param[in] window The window to resize. + * @param[in] width The desired width, in screen coordinates, of the window + * content area. + * @param[in] height The desired height, in screen coordinates, of the window + * content area. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland A full screen window will not attempt to change the mode, + * no matter what the requested size. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_size + * @sa @ref glfwGetWindowSize + * @sa @ref glfwSetWindowMonitor + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowSize(GLFWwindow* window, int width, int height); + +/*! @brief Retrieves the size of the framebuffer of the specified window. + * + * This function retrieves the size, in pixels, of the framebuffer of the + * specified window. If you wish to retrieve the size of the window in screen + * coordinates, see @ref glfwGetWindowSize. + * + * Any or all of the size arguments may be `NULL`. If an error occurs, all + * non-`NULL` size arguments will be set to zero. + * + * @param[in] window The window whose framebuffer to query. + * @param[out] width Where to store the width, in pixels, of the framebuffer, + * or `NULL`. + * @param[out] height Where to store the height, in pixels, of the framebuffer, + * or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_fbsize + * @sa @ref glfwSetFramebufferSizeCallback + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height); + +/*! @brief Retrieves the size of the frame of the window. + * + * This function retrieves the size, in screen coordinates, of each edge of the + * frame of the specified window. This size includes the title bar, if the + * window has one. The size of the frame may vary depending on the + * [window-related hints](@ref window_hints_wnd) used to create it. + * + * Because this function retrieves the size of each window frame edge and not + * the offset along a particular coordinate axis, the retrieved values will + * always be zero or positive. + * + * Any or all of the size arguments may be `NULL`. If an error occurs, all + * non-`NULL` size arguments will be set to zero. + * + * @param[in] window The window whose frame size to query. + * @param[out] left Where to store the size, in screen coordinates, of the left + * edge of the window frame, or `NULL`. + * @param[out] top Where to store the size, in screen coordinates, of the top + * edge of the window frame, or `NULL`. + * @param[out] right Where to store the size, in screen coordinates, of the + * right edge of the window frame, or `NULL`. + * @param[out] bottom Where to store the size, in screen coordinates, of the + * bottom edge of the window frame, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_size + * + * @since Added in version 3.1. + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int* right, int* bottom); + +/*! @brief Retrieves the content scale for the specified window. + * + * This function retrieves the content scale for the specified window. The + * content scale is the ratio between the current DPI and the platform's + * default DPI. This is especially important for text and any UI elements. If + * the pixel dimensions of your UI scaled by this look appropriate on your + * machine then it should appear at a reasonable size on other machines + * regardless of their DPI and scaling settings. This relies on the system DPI + * and scaling settings being somewhat correct. + * + * 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. + * + * @param[in] window The window to query. + * @param[out] xscale Where to store the x-axis content scale, or `NULL`. + * @param[out] yscale Where to store the y-axis content scale, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_scale + * @sa @ref glfwSetWindowContentScaleCallback + * @sa @ref glfwGetMonitorContentScale + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowContentScale(GLFWwindow* window, float* xscale, float* yscale); + +/*! @brief Returns the opacity of the whole window. + * + * This function returns the opacity of the window, including any decorations. + * + * The opacity (or alpha) value is a positive finite number between zero and + * one, where zero is fully transparent and one is fully opaque. If the system + * does not support whole window transparency, this function always returns one. + * + * The initial opacity value for newly created windows is one. + * + * @param[in] window The window to query. + * @return The opacity value of the specified window. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_transparency + * @sa @ref glfwSetWindowOpacity + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI float glfwGetWindowOpacity(GLFWwindow* window); + +/*! @brief Sets the opacity of the whole window. + * + * This function sets the opacity of the window, including any decorations. + * + * The opacity (or alpha) value is a positive finite number between zero and + * one, where zero is fully transparent and one is fully opaque. + * + * The initial opacity value for newly created windows is one. + * + * A window created with framebuffer transparency may not use whole window + * transparency. The results of doing this are undefined. + * + * @param[in] window The window to set the opacity for. + * @param[in] opacity The desired opacity of the specified window. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_transparency + * @sa @ref glfwGetWindowOpacity + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowOpacity(GLFWwindow* window, float opacity); + +/*! @brief Iconifies the specified window. + * + * This function iconifies (minimizes) the specified window if it was + * previously restored. If the window is already iconified, this function does + * nothing. + * + * If the specified window is a full screen window, the original monitor + * resolution is restored until the window is restored. + * + * @param[in] window The window to iconify. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland Once a window is iconified, @ref glfwRestoreWindow won’t + * be able to restore it. This is a design decision of the xdg-shell + * protocol. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_iconify + * @sa @ref glfwRestoreWindow + * @sa @ref glfwMaximizeWindow + * + * @since Added in version 2.1. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwIconifyWindow(GLFWwindow* window); + +/*! @brief Restores the specified window. + * + * This function restores the specified window if it was previously iconified + * (minimized) or maximized. If the window is already restored, this function + * does nothing. + * + * If the specified window is a full screen window, the resolution chosen for + * the window is restored on the selected monitor. + * + * @param[in] window The window to restore. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_iconify + * @sa @ref glfwIconifyWindow + * @sa @ref glfwMaximizeWindow + * + * @since Added in version 2.1. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwRestoreWindow(GLFWwindow* window); + +/*! @brief Maximizes the specified window. + * + * This function maximizes the specified window if it was previously not + * maximized. If the window is already maximized, this function does nothing. + * + * If the specified window is a full screen window, this function does nothing. + * + * @param[in] window The window to maximize. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @par Thread Safety + * This function may only be called from the main thread. + * + * @sa @ref window_iconify + * @sa @ref glfwIconifyWindow + * @sa @ref glfwRestoreWindow + * + * @since Added in GLFW 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwMaximizeWindow(GLFWwindow* window); + +/*! @brief Makes the specified window visible. + * + * This function makes the specified window visible if it was previously + * hidden. If the window is already visible or is in full screen mode, this + * function does nothing. + * + * By default, windowed mode windows are focused when shown + * Set the [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_hint) window hint + * to change this behavior for all newly created windows, or change the + * behavior for an existing window with @ref glfwSetWindowAttrib. + * + * @param[in] window The window to make visible. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_hide + * @sa @ref glfwHideWindow + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwShowWindow(GLFWwindow* window); + +/*! @brief Hides the specified window. + * + * This function hides the specified window if it was previously visible. If + * the window is already hidden or is in full screen mode, this function does + * nothing. + * + * @param[in] window The window to hide. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_hide + * @sa @ref glfwShowWindow + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwHideWindow(GLFWwindow* window); + +/*! @brief Brings the specified window to front and sets input focus. + * + * This function brings the specified window to front and sets input focus. + * The window should already be visible and not iconified. + * + * By default, both windowed and full screen mode windows are focused when + * initially created. Set the [GLFW_FOCUSED](@ref GLFW_FOCUSED_hint) to + * disable this behavior. + * + * Also by default, windowed mode windows are focused when shown + * with @ref glfwShowWindow. Set the + * [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_hint) to disable this behavior. + * + * __Do not use this function__ to steal focus from other applications unless + * you are certain that is what the user wants. Focus stealing can be + * extremely disruptive. + * + * For a less disruptive way of getting the user's attention, see + * [attention requests](@ref window_attention). + * + * @param[in] window The window to give input focus. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland It is not possible for an application to bring its windows + * to front, this function will always emit @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_focus + * @sa @ref window_attention + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwFocusWindow(GLFWwindow* window); + +/*! @brief Requests user attention to the specified window. + * + * This function requests user attention to the specified window. On + * platforms where this is not supported, attention is requested to the + * application as a whole. + * + * Once the user has given attention, usually by focusing the window or + * application, the system will end the request automatically. + * + * @param[in] window The window to request attention to. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @macos Attention is requested to the application as a whole, not the + * specific window. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_attention + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwRequestWindowAttention(GLFWwindow* window); + +/*! @brief Returns the monitor that the window uses for full screen mode. + * + * This function returns the handle of the monitor that the specified window is + * in full screen on. + * + * @param[in] window The window to query. + * @return The monitor, or `NULL` if the window is in windowed mode or an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_monitor + * @sa @ref glfwSetWindowMonitor + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); + +/*! @brief Sets the mode, monitor, video mode and placement of a window. + * + * This function sets the monitor that the window uses for full screen mode or, + * if the monitor is `NULL`, makes it windowed mode. + * + * When setting a monitor, this function updates the width, height and refresh + * rate of the desired video mode and switches to the video mode closest to it. + * The window position is ignored when setting a monitor. + * + * When the monitor is `NULL`, the position, width and height are used to + * place the window content area. The refresh rate is ignored when no monitor + * is specified. + * + * If you only wish to update the resolution of a full screen window or the + * size of a windowed mode window, see @ref glfwSetWindowSize. + * + * When a window transitions from full screen to windowed mode, this function + * restores any previous window settings such as whether it is decorated, + * floating, resizable, has size or aspect ratio limits, etc. + * + * @param[in] window The window whose monitor, size or video mode to set. + * @param[in] monitor The desired monitor, or `NULL` to set windowed mode. + * @param[in] xpos The desired x-coordinate of the upper-left corner of the + * content area. + * @param[in] ypos The desired y-coordinate of the upper-left corner of the + * content area. + * @param[in] width The desired with, in screen coordinates, of the content + * area or video mode. + * @param[in] height The desired height, in screen coordinates, of the content + * area or video mode. + * @param[in] refreshRate The desired refresh rate, in Hz, of the video mode, + * or `GLFW_DONT_CARE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark The OpenGL or OpenGL ES context will not be destroyed or otherwise + * affected by any resizing or mode switching, although you may need to update + * your viewport if the framebuffer size has changed. + * + * @remark @wayland The desired window position is ignored, as there is no way + * for an application to set this property. + * + * @remark @wayland Setting the window to full screen will not attempt to + * change the mode, no matter what the requested size or refresh rate. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_monitor + * @sa @ref window_full_screen + * @sa @ref glfwGetWindowMonitor + * @sa @ref glfwSetWindowSize + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); + +/*! @brief Returns an attribute of the specified window. + * + * This function returns the value of an attribute of the specified window or + * its OpenGL or OpenGL ES context. + * + * @param[in] window The window to query. + * @param[in] attrib The [window attribute](@ref window_attribs) whose value to + * return. + * @return The value of the attribute, or zero if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @remark Framebuffer related hints are not window attributes. See @ref + * window_attribs_fb for more information. + * + * @remark Zero is a valid value for many window and context related + * attributes so you cannot use a return value of zero as an indication of + * errors. However, this function should not fail as long as it is passed + * valid arguments and the library has been [initialized](@ref intro_init). + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_attribs + * @sa @ref glfwSetWindowAttrib + * + * @since Added in version 3.0. Replaces `glfwGetWindowParam` and + * `glfwGetGLVersion`. + * + * @ingroup window + */ +GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib); + +/*! @brief Sets an attribute of the specified window. + * + * This function sets the value of an attribute of the specified window. + * + * The supported attributes are [GLFW_DECORATED](@ref GLFW_DECORATED_attrib), + * [GLFW_RESIZABLE](@ref GLFW_RESIZABLE_attrib), + * [GLFW_FLOATING](@ref GLFW_FLOATING_attrib), + * [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib) and + * [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_attrib). + * + * Some of these attributes are ignored for full screen windows. The new + * value will take effect if the window is later made windowed. + * + * Some of these attributes are ignored for windowed mode windows. The new + * value will take effect if the window is later made full screen. + * + * @param[in] window The window to set the attribute for. + * @param[in] attrib A supported window attribute. + * @param[in] value `GLFW_TRUE` or `GLFW_FALSE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @remark Calling @ref glfwGetWindowAttrib will always return the latest + * value, even if that value is ignored by the current mode of the window. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_attribs + * @sa @ref glfwGetWindowAttrib + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowAttrib(GLFWwindow* window, int attrib, int value); + +/*! @brief Sets the user pointer of the specified window. + * + * This function sets the user-defined pointer of the specified window. The + * current value is retained until the window is destroyed. The initial value + * is `NULL`. + * + * @param[in] window The window whose pointer to set. + * @param[in] pointer The new value. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref window_userptr + * @sa @ref glfwGetWindowUserPointer + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* window, void* pointer); + +/*! @brief Returns the user pointer of the specified window. + * + * This function returns the current value of the user-defined pointer of the + * specified window. The initial value is `NULL`. + * + * @param[in] window The window whose pointer to return. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref window_userptr + * @sa @ref glfwSetWindowUserPointer + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); + +/*! @brief Sets the position callback for the specified window. + * + * This function sets the position callback of the specified window, which is + * called when the window is moved. The callback is provided with the + * position, in screen coordinates, of the upper-left corner of the content + * area of the window. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int xpos, int ypos) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowposfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark @wayland This callback will never be called, as there is no way for + * an application to know its global position. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_pos + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindowposfun callback); + +/*! @brief Sets the size callback for the specified window. + * + * This function sets the size callback of the specified window, which is + * called when the window is resized. The callback is provided with the size, + * in screen coordinates, of the content area of the window. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int width, int height) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowsizefun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_size + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup window + */ +GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwindowsizefun callback); + +/*! @brief Sets the close callback for the specified window. + * + * This function sets the close callback of the specified window, which is + * called when the user attempts to close the window, for example by clicking + * the close widget in the title bar. + * + * The close flag is set before this callback is called, but you can modify it + * at any time with @ref glfwSetWindowShouldClose. + * + * The close callback is not triggered by @ref glfwDestroyWindow. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowclosefun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark @macos Selecting Quit from the application menu will trigger the + * close callback for all windows. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_close + * + * @since Added in version 2.5. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup window + */ +GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwindowclosefun callback); + +/*! @brief Sets the refresh callback for the specified window. + * + * This function sets the refresh callback of the specified window, which is + * called when the content area of the window needs to be redrawn, for example + * if the window has been exposed after having been covered by another window. + * + * On compositing window systems such as Aero, Compiz, Aqua or Wayland, where + * the window contents are saved off-screen, this callback may be called only + * very infrequently or never at all. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window); + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowrefreshfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_refresh + * + * @since Added in version 2.5. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup window + */ +GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GLFWwindowrefreshfun callback); + +/*! @brief Sets the focus callback for the specified window. + * + * This function sets the focus callback of the specified window, which is + * called when the window gains or loses input focus. + * + * After the focus callback is called for a window that lost input focus, + * synthetic key and mouse button release events will be generated for all such + * that had been pressed. For more information, see @ref glfwSetKeyCallback + * and @ref glfwSetMouseButtonCallback. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int focused) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowfocusfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_focus + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwindowfocusfun callback); + +/*! @brief Sets the iconify callback for the specified window. + * + * This function sets the iconification callback of the specified window, which + * is called when the window is iconified or restored. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int iconified) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowiconifyfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_iconify + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GLFWwindowiconifyfun callback); + +/*! @brief Sets the maximize callback for the specified window. + * + * This function sets the maximization callback of the specified window, which + * is called when the window is maximized or restored. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int maximized) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowmaximizefun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_maximize + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, GLFWwindowmaximizefun callback); + +/*! @brief Sets the framebuffer resize callback for the specified window. + * + * This function sets the framebuffer resize callback of the specified window, + * which is called when the framebuffer of the specified window is resized. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int width, int height) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWframebuffersizefun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_fbsize + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window, GLFWframebuffersizefun callback); + +/*! @brief Sets the window content scale callback for the specified window. + * + * This function sets the window content scale callback of the specified window, + * which is called when the content scale of the specified window changes. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, float xscale, float yscale) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowcontentscalefun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_scale + * @sa @ref glfwGetWindowContentScale + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* window, GLFWwindowcontentscalefun callback); + +/*! @brief Processes all pending events. + * + * This function processes only those events that are already in the event + * queue and then returns immediately. Processing events will cause the window + * and input callbacks associated with those events to be called. + * + * On some platforms, a window move, resize or menu operation will cause event + * processing to block. This is due to how event processing is designed on + * those platforms. You can use the + * [window refresh callback](@ref window_refresh) to redraw the contents of + * your window when necessary during such operations. + * + * Do not assume that callbacks you set will _only_ be called in response to + * event processing functions like this one. While it is necessary to poll for + * events, window systems that require GLFW to register callbacks of its own + * can pass events to GLFW in response to many window system function calls. + * GLFW will pass those events on to the application callbacks before + * returning. + * + * Event processing is not required for joystick input to work. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref events + * @sa @ref glfwWaitEvents + * @sa @ref glfwWaitEventsTimeout + * + * @since Added in version 1.0. + * + * @ingroup window + */ +GLFWAPI void glfwPollEvents(void); + +/*! @brief Waits until events are queued and processes them. + * + * This function puts the calling thread to sleep until at least one event is + * available in the event queue. Once one or more events are available, + * it behaves exactly like @ref glfwPollEvents, i.e. the events in the queue + * are processed and the function then returns immediately. Processing events + * will cause the window and input callbacks associated with those events to be + * called. + * + * Since not all events are associated with callbacks, this function may return + * without a callback having been called even if you are monitoring all + * callbacks. + * + * On some platforms, a window move, resize or menu operation will cause event + * processing to block. This is due to how event processing is designed on + * those platforms. You can use the + * [window refresh callback](@ref window_refresh) to redraw the contents of + * your window when necessary during such operations. + * + * Do not assume that callbacks you set will _only_ be called in response to + * event processing functions like this one. While it is necessary to poll for + * events, window systems that require GLFW to register callbacks of its own + * can pass events to GLFW in response to many window system function calls. + * GLFW will pass those events on to the application callbacks before + * returning. + * + * Event processing is not required for joystick input to work. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref events + * @sa @ref glfwPollEvents + * @sa @ref glfwWaitEventsTimeout + * + * @since Added in version 2.5. + * + * @ingroup window + */ +GLFWAPI void glfwWaitEvents(void); + +/*! @brief Waits with timeout until events are queued and processes them. + * + * This function puts the calling thread to sleep until at least one event is + * available in the event queue, or until the specified timeout is reached. If + * one or more events are available, it behaves exactly like @ref + * glfwPollEvents, i.e. the events in the queue are processed and the function + * then returns immediately. Processing events will cause the window and input + * callbacks associated with those events to be called. + * + * The timeout value must be a positive finite number. + * + * Since not all events are associated with callbacks, this function may return + * without a callback having been called even if you are monitoring all + * callbacks. + * + * On some platforms, a window move, resize or menu operation will cause event + * processing to block. This is due to how event processing is designed on + * those platforms. You can use the + * [window refresh callback](@ref window_refresh) to redraw the contents of + * your window when necessary during such operations. + * + * Do not assume that callbacks you set will _only_ be called in response to + * event processing functions like this one. While it is necessary to poll for + * events, window systems that require GLFW to register callbacks of its own + * can pass events to GLFW in response to many window system function calls. + * GLFW will pass those events on to the application callbacks before + * returning. + * + * Event processing is not required for joystick input to work. + * + * @param[in] timeout The maximum amount of time, in seconds, to wait. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref events + * @sa @ref glfwPollEvents + * @sa @ref glfwWaitEvents + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwWaitEventsTimeout(double timeout); + +/*! @brief Posts an empty event to the event queue. + * + * This function posts an empty event from the current thread to the event + * queue, causing @ref glfwWaitEvents or @ref glfwWaitEventsTimeout to return. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref events + * @sa @ref glfwWaitEvents + * @sa @ref glfwWaitEventsTimeout + * + * @since Added in version 3.1. + * + * @ingroup window + */ +GLFWAPI void glfwPostEmptyEvent(void); + +/*! @brief Returns the value of an input option for the specified window. + * + * This function returns the value of an input option for the specified window. + * The mode must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS, + * @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or + * @ref GLFW_RAW_MOUSE_MOTION. + * + * @param[in] window The window to query. + * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`, + * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or + * `GLFW_RAW_MOUSE_MOTION`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref glfwSetInputMode + * + * @since Added in version 3.0. + * + * @ingroup input + */ +GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); + +/*! @brief Sets an input option for the specified window. + * + * This function sets an input mode option for the specified window. The mode + * must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS, + * @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or + * @ref GLFW_RAW_MOUSE_MOTION. + * + * If the mode is `GLFW_CURSOR`, the value must be one of the following cursor + * modes: + * - `GLFW_CURSOR_NORMAL` makes the cursor visible and behaving normally. + * - `GLFW_CURSOR_HIDDEN` makes the cursor invisible when it is over the + * content area of the window but does not restrict the cursor from leaving. + * - `GLFW_CURSOR_DISABLED` hides and grabs the cursor, providing virtual + * and unlimited cursor movement. This is useful for implementing for + * example 3D camera controls. + * + * If the mode is `GLFW_STICKY_KEYS`, the value must be either `GLFW_TRUE` to + * enable sticky keys, or `GLFW_FALSE` to disable it. If sticky keys are + * enabled, a key press will ensure that @ref glfwGetKey returns `GLFW_PRESS` + * the next time it is called even if the key had been released before the + * call. This is useful when you are only interested in whether keys have been + * pressed but not when or in which order. + * + * If the mode is `GLFW_STICKY_MOUSE_BUTTONS`, the value must be either + * `GLFW_TRUE` to enable sticky mouse buttons, or `GLFW_FALSE` to disable it. + * If sticky mouse buttons are enabled, a mouse button press will ensure that + * @ref glfwGetMouseButton returns `GLFW_PRESS` the next time it is called even + * if the mouse button had been released before the call. This is useful when + * you are only interested in whether mouse buttons have been pressed but not + * when or in which order. + * + * If the mode is `GLFW_LOCK_KEY_MODS`, the value must be either `GLFW_TRUE` to + * enable lock key modifier bits, or `GLFW_FALSE` to disable them. If enabled, + * callbacks that receive modifier bits will also have the @ref + * GLFW_MOD_CAPS_LOCK bit set when the event was generated with Caps Lock on, + * and the @ref GLFW_MOD_NUM_LOCK bit when Num Lock was on. + * + * If the mode is `GLFW_RAW_MOUSE_MOTION`, the value must be either `GLFW_TRUE` + * to enable raw (unscaled and unaccelerated) mouse motion when the cursor is + * disabled, or `GLFW_FALSE` to disable it. If raw motion is not supported, + * attempting to set this will emit @ref GLFW_PLATFORM_ERROR. Call @ref + * glfwRawMouseMotionSupported to check for support. + * + * @param[in] window The window whose input mode to set. + * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`, + * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or + * `GLFW_RAW_MOUSE_MOTION`. + * @param[in] value The new value of the specified input mode. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref glfwGetInputMode + * + * @since Added in version 3.0. Replaces `glfwEnable` and `glfwDisable`. + * + * @ingroup input + */ +GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); + +/*! @brief Returns whether raw mouse motion is supported. + * + * This function returns whether raw mouse motion is supported on the current + * system. This status does not change after GLFW has been initialized so you + * only need to check this once. If you attempt to enable raw motion on + * a system that does not support it, @ref GLFW_PLATFORM_ERROR will be emitted. + * + * Raw mouse motion is closer to the actual motion of the mouse across + * a surface. It is not affected by the scaling and acceleration applied to + * the motion of the desktop cursor. That processing is suitable for a cursor + * while raw motion is better for controlling for example a 3D camera. Because + * of this, raw mouse motion is only provided when the cursor is disabled. + * + * @return `GLFW_TRUE` if raw mouse motion is supported on the current machine, + * or `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref raw_mouse_motion + * @sa @ref glfwSetInputMode + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwRawMouseMotionSupported(void); + +/*! @brief Returns the layout-specific name of the specified printable key. + * + * This function returns the name of the specified printable key, encoded as + * UTF-8. This is typically the character that key would produce without any + * modifier keys, intended for displaying key bindings to the user. For dead + * keys, it is typically the diacritic it would add to a character. + * + * __Do not use this function__ for [text input](@ref input_char). You will + * break text input for many languages even if it happens to work for yours. + * + * If the key is `GLFW_KEY_UNKNOWN`, the scancode is used to identify the key, + * otherwise the scancode is ignored. If you specify a non-printable key, or + * `GLFW_KEY_UNKNOWN` and a scancode that maps to a non-printable key, this + * function returns `NULL` but does not emit an error. + * + * This behavior allows you to always pass in the arguments in the + * [key callback](@ref input_key) without modification. + * + * The printable keys are: + * - `GLFW_KEY_APOSTROPHE` + * - `GLFW_KEY_COMMA` + * - `GLFW_KEY_MINUS` + * - `GLFW_KEY_PERIOD` + * - `GLFW_KEY_SLASH` + * - `GLFW_KEY_SEMICOLON` + * - `GLFW_KEY_EQUAL` + * - `GLFW_KEY_LEFT_BRACKET` + * - `GLFW_KEY_RIGHT_BRACKET` + * - `GLFW_KEY_BACKSLASH` + * - `GLFW_KEY_WORLD_1` + * - `GLFW_KEY_WORLD_2` + * - `GLFW_KEY_0` to `GLFW_KEY_9` + * - `GLFW_KEY_A` to `GLFW_KEY_Z` + * - `GLFW_KEY_KP_0` to `GLFW_KEY_KP_9` + * - `GLFW_KEY_KP_DECIMAL` + * - `GLFW_KEY_KP_DIVIDE` + * - `GLFW_KEY_KP_MULTIPLY` + * - `GLFW_KEY_KP_SUBTRACT` + * - `GLFW_KEY_KP_ADD` + * - `GLFW_KEY_KP_EQUAL` + * + * Names for printable keys depend on keyboard layout, while names for + * non-printable keys are the same across layouts but depend on the application + * language and should be localized along with other user interface text. + * + * @param[in] key The key to query, or `GLFW_KEY_UNKNOWN`. + * @param[in] scancode The scancode of the key to query. + * @return The UTF-8 encoded, layout-specific name of the key, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark The contents of the returned string may change when a keyboard + * layout change event is received. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_key_name + * + * @since Added in version 3.2. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetKeyName(int key, int scancode); + +/*! @brief Returns the platform-specific scancode of the specified key. + * + * This function returns the platform-specific scancode of the specified key. + * + * If the key is `GLFW_KEY_UNKNOWN` or does not exist on the keyboard this + * method will return `-1`. + * + * @param[in] key Any [named key](@ref keys). + * @return The platform-specific scancode for the key, or `-1` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref input_key + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwGetKeyScancode(int key); + +/*! @brief Returns the last reported state of a keyboard key for the specified + * window. + * + * This function returns the last state reported for the specified key to the + * specified window. The returned state is one of `GLFW_PRESS` or + * `GLFW_RELEASE`. The higher-level action `GLFW_REPEAT` is only reported to + * the key callback. + * + * If the @ref GLFW_STICKY_KEYS input mode is enabled, this function returns + * `GLFW_PRESS` the first time you call it for a key that was pressed, even if + * that key has already been released. + * + * The key functions deal with physical keys, with [key tokens](@ref keys) + * named after their use on the standard US keyboard layout. If you want to + * input text, use the Unicode character callback instead. + * + * The [modifier key bit masks](@ref mods) are not key tokens and cannot be + * used with this function. + * + * __Do not use this function__ to implement [text input](@ref input_char). + * + * @param[in] window The desired window. + * @param[in] key The desired [keyboard key](@ref keys). `GLFW_KEY_UNKNOWN` is + * not a valid key for this function. + * @return One of `GLFW_PRESS` or `GLFW_RELEASE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_key + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup input + */ +GLFWAPI int glfwGetKey(GLFWwindow* window, int key); + +/*! @brief Returns the last reported state of a mouse button for the specified + * window. + * + * This function returns the last state reported for the specified mouse button + * to the specified window. The returned state is one of `GLFW_PRESS` or + * `GLFW_RELEASE`. + * + * If the @ref GLFW_STICKY_MOUSE_BUTTONS input mode is enabled, this function + * returns `GLFW_PRESS` the first time you call it for a mouse button that was + * pressed, even if that mouse button has already been released. + * + * @param[in] window The desired window. + * @param[in] button The desired [mouse button](@ref buttons). + * @return One of `GLFW_PRESS` or `GLFW_RELEASE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_mouse_button + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup input + */ +GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); + +/*! @brief Retrieves the position of the cursor relative to the content area of + * the window. + * + * This function returns the position of the cursor, in screen coordinates, + * relative to the upper-left corner of the content area of the specified + * window. + * + * If the cursor is disabled (with `GLFW_CURSOR_DISABLED`) then the cursor + * position is unbounded and limited only by the minimum and maximum values of + * a `double`. + * + * The coordinate can be converted to their integer equivalents with the + * `floor` function. Casting directly to an integer type works for positive + * coordinates, but fails for negative ones. + * + * Any or all of the position arguments may be `NULL`. If an error occurs, all + * non-`NULL` position arguments will be set to zero. + * + * @param[in] window The desired window. + * @param[out] xpos Where to store the cursor x-coordinate, relative to the + * left edge of the content area, or `NULL`. + * @param[out] ypos Where to store the cursor y-coordinate, relative to the to + * top edge of the content area, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_pos + * @sa @ref glfwSetCursorPos + * + * @since Added in version 3.0. Replaces `glfwGetMousePos`. + * + * @ingroup input + */ +GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos); + +/*! @brief Sets the position of the cursor, relative to the content area of the + * window. + * + * This function sets the position, in screen coordinates, of the cursor + * relative to the upper-left corner of the content area of the specified + * window. The window must have input focus. If the window does not have + * input focus when this function is called, it fails silently. + * + * __Do not use this function__ to implement things like camera controls. GLFW + * already provides the `GLFW_CURSOR_DISABLED` cursor mode that hides the + * cursor, transparently re-centers it and provides unconstrained cursor + * motion. See @ref glfwSetInputMode for more information. + * + * If the cursor mode is `GLFW_CURSOR_DISABLED` then the cursor position is + * unconstrained and limited only by the minimum and maximum values of + * a `double`. + * + * @param[in] window The desired window. + * @param[in] xpos The desired x-coordinate, relative to the left edge of the + * content area. + * @param[in] ypos The desired y-coordinate, relative to the top edge of the + * content area. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland This function will only work when the cursor mode is + * `GLFW_CURSOR_DISABLED`, otherwise it will do nothing. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_pos + * @sa @ref glfwGetCursorPos + * + * @since Added in version 3.0. Replaces `glfwSetMousePos`. + * + * @ingroup input + */ +GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); + +/*! @brief Creates a custom cursor. + * + * Creates a new custom cursor image that can be set for a window with @ref + * glfwSetCursor. The cursor can be destroyed with @ref glfwDestroyCursor. + * Any remaining cursors are destroyed by @ref glfwTerminate. + * + * The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight + * bits per channel with the red channel first. They are arranged canonically + * as packed sequential rows, starting from the top-left corner. + * + * The cursor hotspot is specified in pixels, relative to the upper-left corner + * of the cursor image. Like all other coordinate systems in GLFW, the X-axis + * points to the right and the Y-axis points down. + * + * @param[in] image The desired cursor image. + * @param[in] xhot The desired x-coordinate, in pixels, of the cursor hotspot. + * @param[in] yhot The desired y-coordinate, in pixels, of the cursor hotspot. + * @return The handle of the created cursor, 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 specified image data is copied before this function + * returns. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_object + * @sa @ref glfwDestroyCursor + * @sa @ref glfwCreateStandardCursor + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot); + +/*! @brief Creates a cursor with a standard shape. + * + * Returns a cursor with a standard shape, that can be set for a window with + * @ref glfwSetCursor. The images for these cursors come from the system + * cursor theme and their exact appearance will vary between platforms. + * + * Most of these shapes are guaranteed to exist on every supported platform but + * a few may not be present. See the table below for details. + * + * Cursor shape | Windows | macOS | X11 | Wayland + * ------------------------------ | ------- | ----- | ------ | ------- + * @ref GLFW_ARROW_CURSOR | Yes | Yes | Yes | Yes + * @ref GLFW_IBEAM_CURSOR | Yes | Yes | Yes | Yes + * @ref GLFW_CROSSHAIR_CURSOR | Yes | Yes | Yes | Yes + * @ref GLFW_POINTING_HAND_CURSOR | Yes | Yes | Yes | Yes + * @ref GLFW_RESIZE_EW_CURSOR | Yes | Yes | Yes | Yes + * @ref GLFW_RESIZE_NS_CURSOR | Yes | Yes | Yes | Yes + * @ref GLFW_RESIZE_NWSE_CURSOR | Yes | Yes1 | Maybe2 | Maybe2 + * @ref GLFW_RESIZE_NESW_CURSOR | Yes | Yes1 | Maybe2 | Maybe2 + * @ref GLFW_RESIZE_ALL_CURSOR | Yes | Yes | Yes | Yes + * @ref GLFW_NOT_ALLOWED_CURSOR | Yes | Yes | Maybe2 | Maybe2 + * + * 1) This uses a private system API and may fail in the future. + * + * 2) This uses a newer standard that not all cursor themes support. + * + * If the requested shape is not available, this function emits a @ref + * GLFW_CURSOR_UNAVAILABLE error and returns `NULL`. + * + * @param[in] shape One of the [standard shapes](@ref shapes). + * @return A new cursor ready to use or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM, @ref GLFW_CURSOR_UNAVAILABLE and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_standard + * @sa @ref glfwCreateCursor + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape); + +/*! @brief Destroys a cursor. + * + * This function destroys a cursor previously created with @ref + * glfwCreateCursor. Any remaining cursors will be destroyed by @ref + * glfwTerminate. + * + * If the specified cursor is current for any window, that window will be + * reverted to the default cursor. This does not affect the cursor mode. + * + * @param[in] cursor The cursor object to destroy. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_object + * @sa @ref glfwCreateCursor + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI void glfwDestroyCursor(GLFWcursor* cursor); + +/*! @brief Sets the cursor for the window. + * + * This function sets the cursor image to be used when the cursor is over the + * content area of the specified window. The set cursor will only be visible + * when the [cursor mode](@ref cursor_mode) of the window is + * `GLFW_CURSOR_NORMAL`. + * + * On some platforms, the set cursor may not be visible unless the window also + * has input focus. + * + * @param[in] window The window to set the cursor for. + * @param[in] cursor The cursor to set, or `NULL` to switch back to the default + * arrow cursor. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_object + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor); + +/*! @brief Sets the key callback. + * + * This function sets the key callback of the specified window, which is called + * when a key is pressed, repeated or released. + * + * The key functions deal with physical keys, with layout independent + * [key tokens](@ref keys) named after their values in the standard US keyboard + * layout. If you want to input text, use the + * [character callback](@ref glfwSetCharCallback) instead. + * + * When a window loses input focus, it will generate synthetic key release + * events for all pressed keys. You can tell these events from user-generated + * events by the fact that the synthetic ones are generated after the focus + * loss event has been processed, i.e. after the + * [window focus callback](@ref glfwSetWindowFocusCallback) has been called. + * + * The scancode of a key is specific to that platform or sometimes even to that + * machine. Scancodes are intended to allow users to bind keys that don't have + * a GLFW key token. Such keys have `key` set to `GLFW_KEY_UNKNOWN`, their + * state is not saved and so it cannot be queried with @ref glfwGetKey. + * + * Sometimes GLFW needs to generate synthetic key events, in which case the + * scancode may be zero. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new key callback, or `NULL` to remove the currently + * set callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int key, int scancode, int action, int mods) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWkeyfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_key + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup input + */ +GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun callback); + +/*! @brief Sets the Unicode character callback. + * + * This function sets the character callback of the specified window, which is + * called when a Unicode character is input. + * + * The character callback is intended for Unicode text input. As it deals with + * characters, it is keyboard layout dependent, whereas the + * [key callback](@ref glfwSetKeyCallback) is not. Characters do not map 1:1 + * to physical keys, as a key may produce zero, one or more characters. If you + * want to know whether a specific physical key was pressed or released, see + * the key callback instead. + * + * The character callback behaves as system text input normally does and will + * not be called if modifier keys are held down that would prevent normal text + * input on that platform, for example a Super (Command) key on macOS or Alt key + * on Windows. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcharfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_char + * + * @since Added in version 2.4. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup input + */ +GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun callback); + +/*! @brief Sets the Unicode character with modifiers callback. + * + * This function sets the character with modifiers callback of the specified + * window, which is called when a Unicode character is input regardless of what + * modifier keys are used. + * + * The character with modifiers callback is intended for implementing custom + * Unicode character input. For regular Unicode text input, see the + * [character callback](@ref glfwSetCharCallback). Like the character + * callback, the character with modifiers callback deals with characters and is + * keyboard layout dependent. Characters do not map 1:1 to physical keys, as + * a key may produce zero, one or more characters. If you want to know whether + * a specific physical key was pressed or released, see the + * [key callback](@ref glfwSetKeyCallback) instead. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or an + * [error](@ref error_handling) occurred. + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint, int mods) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcharmodsfun). + * + * @deprecated Scheduled for removal in version 4.0. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_char + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmodsfun callback); + +/*! @brief Sets the mouse button callback. + * + * This function sets the mouse button callback of the specified window, which + * is called when a mouse button is pressed or released. + * + * When a window loses input focus, it will generate synthetic mouse button + * release events for all pressed mouse buttons. You can tell these events + * from user-generated events by the fact that the synthetic ones are generated + * after the focus loss event has been processed, i.e. after the + * [window focus callback](@ref glfwSetWindowFocusCallback) has been called. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int button, int action, int mods) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWmousebuttonfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_mouse_button + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup input + */ +GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmousebuttonfun callback); + +/*! @brief Sets the cursor position callback. + * + * This function sets the cursor position callback of the specified window, + * which is called when the cursor is moved. The callback is provided with the + * position, in screen coordinates, relative to the upper-left corner of the + * content area of the window. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, double xpos, double ypos); + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcursorposfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_pos + * + * @since Added in version 3.0. Replaces `glfwSetMousePosCallback`. + * + * @ingroup input + */ +GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursorposfun callback); + +/*! @brief Sets the cursor enter/leave callback. + * + * This function sets the cursor boundary crossing callback of the specified + * window, which is called when the cursor enters or leaves the content area of + * the window. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int entered) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcursorenterfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_enter + * + * @since Added in version 3.0. + * + * @ingroup input + */ +GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcursorenterfun callback); + +/*! @brief Sets the scroll callback. + * + * This function sets the scroll callback of the specified window, which is + * called when a scrolling device is used, such as a mouse wheel or scrolling + * area of a touchpad. + * + * The scroll callback receives all scrolling input, like that from a mouse + * wheel or a touchpad scrolling area. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new scroll callback, or `NULL` to remove the + * currently set callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, double xoffset, double yoffset) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWscrollfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref scrolling + * + * @since Added in version 3.0. Replaces `glfwSetMouseWheelCallback`. + * + * @ingroup input + */ +GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun callback); + +/*! @brief Sets the path drop callback. + * + * This function sets the path drop callback of the specified window, which is + * called when one or more dragged paths are dropped on the window. + * + * Because the path array and its strings may have been generated specifically + * for that event, they are not guaranteed to be valid after the callback has + * returned. If you wish to use them after the callback returns, you need to + * make a deep copy. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new file drop callback, or `NULL` to remove the + * currently set callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int path_count, const char* paths[]) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWdropfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark @wayland File drop is currently unimplemented. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref path_drop + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun callback); + +/*! @brief Returns whether the specified joystick is present. + * + * This function returns whether the specified joystick is present. + * + * There is no need to call this function before other functions that accept + * a joystick ID, as they all check for presence before performing any other + * work. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return `GLFW_TRUE` if the joystick is present, or `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick + * + * @since Added in version 3.0. Replaces `glfwGetJoystickParam`. + * + * @ingroup input + */ +GLFWAPI int glfwJoystickPresent(int jid); + +/*! @brief Returns the values of all axes of the specified joystick. + * + * This function returns the values of all axes of the specified joystick. + * Each element in the array is a value between -1.0 and 1.0. + * + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @param[out] count Where to store the number of axis values in the returned + * array. This is set to zero if the joystick is not present or an error + * occurred. + * @return An array of axis values, or `NULL` if the joystick is not present or + * an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_axis + * + * @since Added in version 3.0. Replaces `glfwGetJoystickPos`. + * + * @ingroup input + */ +GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count); + +/*! @brief Returns the state of all buttons of the specified joystick. + * + * This function returns the state of all buttons of the specified joystick. + * Each element in the array is either `GLFW_PRESS` or `GLFW_RELEASE`. + * + * For backward compatibility with earlier versions that did not have @ref + * glfwGetJoystickHats, the button array also includes all hats, each + * represented as four buttons. The hats are in the same order as returned by + * __glfwGetJoystickHats__ and are in the order _up_, _right_, _down_ and + * _left_. To disable these extra buttons, set the @ref + * GLFW_JOYSTICK_HAT_BUTTONS init hint before initialization. + * + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @param[out] count Where to store the number of button states in the returned + * array. This is set to zero if the joystick is not present or an error + * occurred. + * @return An array of button states, or `NULL` if the joystick is not present + * or an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_button + * + * @since Added in version 2.2. + * @glfw3 Changed to return a dynamic array. + * + * @ingroup input + */ +GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count); + +/*! @brief Returns the state of all hats of the specified joystick. + * + * This function returns the state of all hats of the specified joystick. + * Each element in the array is one of the following values: + * + * Name | Value + * ---- | ----- + * `GLFW_HAT_CENTERED` | 0 + * `GLFW_HAT_UP` | 1 + * `GLFW_HAT_RIGHT` | 2 + * `GLFW_HAT_DOWN` | 4 + * `GLFW_HAT_LEFT` | 8 + * `GLFW_HAT_RIGHT_UP` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_UP` + * `GLFW_HAT_RIGHT_DOWN` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_DOWN` + * `GLFW_HAT_LEFT_UP` | `GLFW_HAT_LEFT` \| `GLFW_HAT_UP` + * `GLFW_HAT_LEFT_DOWN` | `GLFW_HAT_LEFT` \| `GLFW_HAT_DOWN` + * + * The diagonal directions are bitwise combinations of the primary (up, right, + * down and left) directions and you can test for these individually by ANDing + * it with the corresponding direction. + * + * @code + * if (hats[2] & GLFW_HAT_RIGHT) + * { + * // State of hat 2 could be right-up, right or right-down + * } + * @endcode + * + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @param[out] count Where to store the number of hat states in the returned + * array. This is set to zero if the joystick is not present or an error + * occurred. + * @return An array of hat states, or `NULL` if the joystick is not present + * or an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected, this function is called again for that joystick or the library + * is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_hat + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count); + +/*! @brief Returns the name of the specified joystick. + * + * This function returns the name, encoded as UTF-8, of the specified joystick. + * The returned string is allocated and freed by GLFW. You should not free it + * yourself. + * + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return The UTF-8 encoded name of the joystick, or `NULL` if the joystick + * is not present or an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM 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 specified joystick is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_name + * + * @since Added in version 3.0. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetJoystickName(int jid); + +/*! @brief Returns the SDL compatible GUID of the specified joystick. + * + * This function returns the SDL compatible GUID, as a UTF-8 encoded + * hexadecimal string, of the specified joystick. The returned string is + * allocated and freed by GLFW. You should not free it yourself. + * + * The GUID is what connects a joystick to a gamepad mapping. A connected + * joystick will always have a GUID even if there is no gamepad mapping + * assigned to it. + * + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. + * + * The GUID uses the format introduced in SDL 2.0.5. This GUID tries to + * uniquely identify the make and model of a joystick but does not identify + * a specific unit, e.g. all wired Xbox 360 controllers will have the same + * GUID on that platform. The GUID for a unit may vary between platforms + * depending on what hardware information the platform specific APIs provide. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return The UTF-8 encoded GUID of the joystick, or `NULL` if the joystick + * is not present or an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM 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 specified joystick is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetJoystickGUID(int jid); + +/*! @brief Sets the user pointer of the specified joystick. + * + * This function sets the user-defined pointer of the specified joystick. The + * current value is retained until the joystick is disconnected. The initial + * value is `NULL`. + * + * This function may be called from the joystick callback, even for a joystick + * that is being disconnected. + * + * @param[in] jid The joystick whose pointer to set. + * @param[in] pointer The new value. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref joystick_userptr + * @sa @ref glfwGetJoystickUserPointer + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI void glfwSetJoystickUserPointer(int jid, void* pointer); + +/*! @brief Returns the user pointer of the specified joystick. + * + * This function returns the current value of the user-defined pointer of the + * specified joystick. The initial value is `NULL`. + * + * This function may be called from the joystick callback, even for a joystick + * that is being disconnected. + * + * @param[in] jid The joystick whose pointer to return. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref joystick_userptr + * @sa @ref glfwSetJoystickUserPointer + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI void* glfwGetJoystickUserPointer(int jid); + +/*! @brief Returns whether the specified joystick has a gamepad mapping. + * + * This function returns whether the specified joystick is both present and has + * a gamepad mapping. + * + * If the specified joystick is present but does not have a gamepad mapping + * this function will return `GLFW_FALSE` but will not generate an error. Call + * @ref glfwJoystickPresent to check if a joystick is present regardless of + * whether it has a mapping. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return `GLFW_TRUE` if a joystick is both present and has a gamepad mapping, + * or `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * @sa @ref glfwGetGamepadState + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwJoystickIsGamepad(int jid); + +/*! @brief Sets the joystick configuration callback. + * + * This function sets the joystick configuration callback, or removes the + * currently set callback. This is called when a joystick is connected to or + * disconnected from the system. + * + * For joystick connection and disconnection events to be delivered on all + * platforms, you need to call one of the [event processing](@ref events) + * functions. Joystick disconnection may also be detected and the callback + * called by joystick functions. The function will then return whatever it + * returns if the joystick is not present. + * + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(int jid, int event) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWjoystickfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_event + * + * @since Added in version 3.2. + * + * @ingroup input + */ +GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun callback); + +/*! @brief Adds the specified SDL_GameControllerDB gamepad mappings. + * + * This function parses the specified ASCII encoded string and updates the + * internal list with any gamepad mappings it finds. This string may + * contain either a single gamepad mapping or many mappings separated by + * newlines. The parser supports the full format of the `gamecontrollerdb.txt` + * source file including empty lines and comments. + * + * See @ref gamepad_mapping for a description of the format. + * + * If there is already a gamepad mapping for a given GUID in the internal list, + * it will be replaced by the one passed to this function. If the library is + * terminated and re-initialized the internal list will revert to the built-in + * default. + * + * @param[in] string The string containing the gamepad mappings. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_VALUE. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * @sa @ref glfwJoystickIsGamepad + * @sa @ref glfwGetGamepadName + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwUpdateGamepadMappings(const char* string); + +/*! @brief Returns the human-readable gamepad name for the specified joystick. + * + * This function returns the human-readable name of the gamepad from the + * gamepad mapping assigned to the specified joystick. + * + * If the specified joystick is not present or does not have a gamepad mapping + * this function will return `NULL` but will not generate an error. Call + * @ref glfwJoystickPresent to check whether it is present regardless of + * whether it has a mapping. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return The UTF-8 encoded name of the gamepad, or `NULL` if the + * joystick is not present, does not have a mapping or an + * [error](@ref error_handling) occurred. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected, the gamepad mappings are updated or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * @sa @ref glfwJoystickIsGamepad + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetGamepadName(int jid); + +/*! @brief Retrieves the state of the specified joystick remapped as a gamepad. + * + * This function retrieves the state of the specified joystick remapped to + * an Xbox-like gamepad. + * + * If the specified joystick is not present or does not have a gamepad mapping + * this function will return `GLFW_FALSE` but will not generate an error. Call + * @ref glfwJoystickPresent to check whether it is present regardless of + * whether it has a mapping. + * + * The Guide button may not be available for input as it is often hooked by the + * system or the Steam client. + * + * Not all devices have all the buttons or axes provided by @ref + * GLFWgamepadstate. Unavailable buttons and axes will always report + * `GLFW_RELEASE` and 0.0 respectively. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @param[out] state The gamepad input state of the joystick. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if no joystick is + * connected, it has no gamepad mapping or an [error](@ref error_handling) + * occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * @sa @ref glfwUpdateGamepadMappings + * @sa @ref glfwJoystickIsGamepad + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state); + +/*! @brief Sets the clipboard to the specified string. + * + * This function sets the system clipboard to the specified, UTF-8 encoded + * string. + * + * @param[in] window Deprecated. Any valid window or `NULL`. + * @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 @ref glfwGetClipboardString + * + * @since Added in version 3.0. + * + * @ingroup input + */ +GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); + +/*! @brief Returns the contents of the clipboard as a string. + * + * This function returns the contents of the system clipboard, if it contains + * or is convertible to a UTF-8 encoded string. If the clipboard is empty or + * if its contents cannot be converted, `NULL` is returned and a @ref + * GLFW_FORMAT_UNAVAILABLE error is generated. + * + * @param[in] window Deprecated. Any valid window or `NULL`. + * @return The contents of the clipboard 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 + * glfwGetClipboardString or @ref glfwSetClipboardString, or until the library + * is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref clipboard + * @sa @ref glfwSetClipboardString + * + * @since Added in version 3.0. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); + +/*! @brief Returns the GLFW time. + * + * This function returns the current GLFW time, in seconds. Unless the time + * has been set using @ref glfwSetTime it measures time elapsed since GLFW was + * initialized. + * + * This function and @ref glfwSetTime are helper functions on top of @ref + * glfwGetTimerFrequency and @ref glfwGetTimerValue. + * + * The resolution of the timer is system dependent, but is usually on the order + * of a few micro- or nanoseconds. It uses the highest-resolution monotonic + * time source on each supported platform. + * + * @return The current time, in seconds, or zero if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Reading and + * writing of the internal base time is not atomic, so it needs to be + * externally synchronized with calls to @ref glfwSetTime. + * + * @sa @ref time + * + * @since Added in version 1.0. + * + * @ingroup input + */ +GLFWAPI double glfwGetTime(void); + +/*! @brief Sets the GLFW time. + * + * This function sets the current GLFW time, in seconds. The value must be + * a positive finite number less than or equal to 18446744073.0, which is + * approximately 584.5 years. + * + * This function and @ref glfwGetTime are helper functions on top of @ref + * glfwGetTimerFrequency and @ref glfwGetTimerValue. + * + * @param[in] time The new value, in seconds. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_VALUE. + * + * @remark The upper limit of GLFW time is calculated as + * floor((264 - 1) / 109) and is due to implementations + * storing nanoseconds in 64 bits. The limit may be increased in the future. + * + * @thread_safety This function may be called from any thread. Reading and + * writing of the internal base time is not atomic, so it needs to be + * externally synchronized with calls to @ref glfwGetTime. + * + * @sa @ref time + * + * @since Added in version 2.2. + * + * @ingroup input + */ +GLFWAPI void glfwSetTime(double time); + +/*! @brief Returns the current value of the raw timer. + * + * This function returns the current value of the raw timer, measured in + * 1 / frequency seconds. To get the frequency, call @ref + * glfwGetTimerFrequency. + * + * @return The value of the timer, or zero if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref time + * @sa @ref glfwGetTimerFrequency + * + * @since Added in version 3.2. + * + * @ingroup input + */ +GLFWAPI uint64_t glfwGetTimerValue(void); + +/*! @brief Returns the frequency, in Hz, of the raw timer. + * + * This function returns the frequency, in Hz, of the raw timer. + * + * @return The frequency of the timer, in Hz, or zero if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref time + * @sa @ref glfwGetTimerValue + * + * @since Added in version 3.2. + * + * @ingroup input + */ +GLFWAPI uint64_t glfwGetTimerFrequency(void); + +/*! @brief Makes the context of the specified window current for the calling + * thread. + * + * This function makes the OpenGL or OpenGL ES context of the specified window + * current on the calling thread. A context must only be made current on + * a single thread at a time and each thread can have only a single current + * context 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. + * + * By default, making a context non-current implicitly forces a pipeline flush. + * On machines that support `GL_KHR_context_flush_control`, you can control + * whether a context performs this flush by setting the + * [GLFW_CONTEXT_RELEASE_BEHAVIOR](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_hint) + * hint. + * + * The specified window must have an OpenGL or OpenGL ES context. Specifying + * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT + * error. + * + * @param[in] window The window whose context to make current, or `NULL` to + * detach the current context. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref context_current + * @sa @ref glfwGetCurrentContext + * + * @since Added in version 3.0. + * + * @ingroup context + */ +GLFWAPI void glfwMakeContextCurrent(GLFWwindow* window); + +/*! @brief Returns the window whose context is current on the calling thread. + * + * This function returns the window whose OpenGL or OpenGL ES context is + * current on the calling thread. + * + * @return The window whose context is current, or `NULL` if no window's + * context is current. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref context_current + * @sa @ref glfwMakeContextCurrent + * + * @since Added in version 3.0. + * + * @ingroup context + */ +GLFWAPI GLFWwindow* glfwGetCurrentContext(void); + +/*! @brief Swaps the front and back buffers of the specified window. + * + * This function swaps the front and back buffers of the specified window when + * rendering with OpenGL or OpenGL ES. If the swap interval is greater than + * zero, the GPU driver waits the specified number of screen updates before + * swapping the buffers. + * + * The specified window must have an OpenGL or OpenGL ES context. Specifying + * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT + * error. + * + * This function does not apply to Vulkan. If you are rendering with Vulkan, + * see `vkQueuePresentKHR` instead. + * + * @param[in] window The window whose buffers to swap. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR. + * + * @remark __EGL:__ The context of the specified window must be current on the + * calling thread. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref buffer_swap + * @sa @ref glfwSwapInterval + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwSwapBuffers(GLFWwindow* window); + +/*! @brief Sets the swap interval for the current context. + * + * This function sets the swap interval for the current OpenGL or OpenGL ES + * context, i.e. the number of screen updates to wait from the time @ref + * glfwSwapBuffers was called before swapping the buffers and returning. This + * is sometimes called _vertical synchronization_, _vertical retrace + * synchronization_ or just _vsync_. + * + * A context that supports either of the `WGL_EXT_swap_control_tear` and + * `GLX_EXT_swap_control_tear` extensions also accepts _negative_ swap + * intervals, which allows the driver to swap immediately even if a frame + * arrives a little bit late. You can check for these extensions with @ref + * glfwExtensionSupported. + * + * A context must be current on the calling thread. Calling this function + * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. + * + * This function does not apply to Vulkan. If you are rendering with Vulkan, + * see the present mode of your swapchain instead. + * + * @param[in] interval The minimum number of screen updates to wait for + * until the buffers are swapped by @ref glfwSwapBuffers. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR. + * + * @remark This function is not called during context creation, leaving the + * swap interval set to whatever is the default on that platform. This is done + * because some swap interval extensions used by GLFW do not allow the swap + * interval to be reset to zero once it has been set to a non-zero value. + * + * @remark Some GPU drivers do not honor the requested swap interval, either + * because of a user setting that overrides the application's request or due to + * bugs in the driver. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref buffer_swap + * @sa @ref glfwSwapBuffers + * + * @since Added in version 1.0. + * + * @ingroup context + */ +GLFWAPI void glfwSwapInterval(int interval); + +/*! @brief Returns whether the specified extension is available. + * + * This function returns whether the specified + * [API extension](@ref context_glext) is supported by the current OpenGL or + * OpenGL ES context. It searches both for client API extension and context + * creation API extensions. + * + * A context must be current on the calling thread. Calling this function + * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. + * + * As this functions retrieves and searches one or more extension strings each + * call, it is recommended that you cache its results if it is going to be used + * frequently. The extension strings will not change during the lifetime of + * a context, so there is no danger in doing this. + * + * This function does not apply to Vulkan. If you are using Vulkan, see @ref + * glfwGetRequiredInstanceExtensions, `vkEnumerateInstanceExtensionProperties` + * and `vkEnumerateDeviceExtensionProperties` instead. + * + * @param[in] extension The ASCII encoded name of the extension. + * @return `GLFW_TRUE` if the extension is available, or `GLFW_FALSE` + * otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_CURRENT_CONTEXT, @ref GLFW_INVALID_VALUE and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref context_glext + * @sa @ref glfwGetProcAddress + * + * @since Added in version 1.0. + * + * @ingroup context + */ +GLFWAPI int glfwExtensionSupported(const char* extension); + +/*! @brief Returns the address of the specified function for the current + * context. + * + * This function returns the address of the specified OpenGL or OpenGL ES + * [core or extension function](@ref context_glext), if it is supported + * by the current context. + * + * A context must be current on the calling thread. Calling this function + * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. + * + * This function does not apply to Vulkan. If you are rendering with Vulkan, + * see @ref glfwGetInstanceProcAddress, `vkGetInstanceProcAddr` and + * `vkGetDeviceProcAddr` instead. + * + * @param[in] procname The ASCII encoded name of the function. + * @return The address of the function, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR. + * + * @remark The address of a given function is not guaranteed to be the same + * between contexts. + * + * @remark This function may return a non-`NULL` address despite the + * associated version or extension not being available. Always check the + * context version or extension string first. + * + * @pointer_lifetime The returned function pointer is valid until the context + * is destroyed or the library is terminated. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref context_glext + * @sa @ref glfwExtensionSupported + * + * @since Added in version 1.0. + * + * @ingroup context + */ +GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname); + +/*! @brief Returns whether the Vulkan loader and an ICD have been found. + * + * This function returns whether the Vulkan loader and any minimally functional + * ICD have been found. + * + * The availability of a Vulkan loader and even an ICD does not by itself + * guarantee that surface creation or even instance creation is possible. + * For example, on Fermi systems Nvidia will install an ICD that provides no + * actual Vulkan support. Call @ref glfwGetRequiredInstanceExtensions to check + * whether the extensions necessary for Vulkan surface creation are available + * and @ref glfwGetPhysicalDevicePresentationSupport to check whether a queue + * family of a physical device supports image presentation. + * + * @return `GLFW_TRUE` if Vulkan is minimally available, or `GLFW_FALSE` + * otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref vulkan_support + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI int glfwVulkanSupported(void); + +/*! @brief Returns the Vulkan instance extensions required by GLFW. + * + * This function returns an array of names of Vulkan instance extensions required + * by GLFW for creating Vulkan surfaces for GLFW windows. If successful, the + * list will always contain `VK_KHR_surface`, so if you don't require any + * additional extensions you can pass this list directly to the + * `VkInstanceCreateInfo` struct. + * + * If Vulkan is not available on the machine, this function returns `NULL` and + * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported + * to check whether Vulkan is at least minimally available. + * + * If Vulkan is available but no set of extensions allowing window surface + * creation was found, this function returns `NULL`. You may still use Vulkan + * for off-screen rendering and compute work. + * + * @param[out] count Where to store the number of extensions in the returned + * array. This is set to zero if an error occurred. + * @return An array of ASCII encoded extension names, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_API_UNAVAILABLE. + * + * @remark Additional extensions may be required by future versions of GLFW. + * You should check if any extensions you wish to enable are already in the + * returned array, as it is an error to specify an extension more than once in + * the `VkInstanceCreateInfo` struct. + * + * @remark @macos This function currently supports either the + * `VK_MVK_macos_surface` extension from MoltenVK or `VK_EXT_metal_surface` + * extension. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is guaranteed to be valid only until the + * library is terminated. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref vulkan_ext + * @sa @ref glfwCreateWindowSurface + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count); + +#if defined(VK_VERSION_1_0) + +/*! @brief Returns the address of the specified Vulkan instance function. + * + * This function returns the address of the specified Vulkan core or extension + * function for the specified instance. If instance is set to `NULL` it can + * return any function exported from the Vulkan loader, including at least the + * following functions: + * + * - `vkEnumerateInstanceExtensionProperties` + * - `vkEnumerateInstanceLayerProperties` + * - `vkCreateInstance` + * - `vkGetInstanceProcAddr` + * + * If Vulkan is not available on the machine, this function returns `NULL` and + * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported + * to check whether Vulkan is at least minimally available. + * + * This function is equivalent to calling `vkGetInstanceProcAddr` with + * a platform-specific query of the Vulkan loader as a fallback. + * + * @param[in] instance The Vulkan instance to query, or `NULL` to retrieve + * functions related to instance creation. + * @param[in] procname The ASCII encoded name of the function. + * @return The address of the function, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_API_UNAVAILABLE. + * + * @pointer_lifetime The returned function pointer is valid until the library + * is terminated. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref vulkan_proc + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char* procname); + +/*! @brief Returns whether the specified queue family can present images. + * + * This function returns whether the specified queue family of the specified + * physical device supports presentation to the platform GLFW was built for. + * + * If Vulkan or the required window surface creation instance extensions are + * not available on the machine, or if the specified instance was not created + * with the required extensions, this function returns `GLFW_FALSE` and + * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported + * to check whether Vulkan is at least minimally available and @ref + * glfwGetRequiredInstanceExtensions to check what instance extensions are + * required. + * + * @param[in] instance The instance that the physical device belongs to. + * @param[in] device The physical device that the queue family belongs to. + * @param[in] queuefamily The index of the queue family to query. + * @return `GLFW_TRUE` if the queue family supports presentation, or + * `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. + * + * @remark @macos This function currently always returns `GLFW_TRUE`, as the + * `VK_MVK_macos_surface` extension does not provide + * a `vkGetPhysicalDevice*PresentationSupport` type function. + * + * @thread_safety This function may be called from any thread. For + * synchronization details of Vulkan objects, see the Vulkan specification. + * + * @sa @ref vulkan_present + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); + +/*! @brief Creates a Vulkan surface for the specified window. + * + * This function creates a Vulkan surface for the specified window. + * + * If the Vulkan loader or at least one minimally functional ICD were not found, + * this function returns `VK_ERROR_INITIALIZATION_FAILED` and generates a @ref + * GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported to check whether + * Vulkan is at least minimally available. + * + * If the required window surface creation instance extensions are not + * available or if the specified instance was not created with these extensions + * enabled, this function returns `VK_ERROR_EXTENSION_NOT_PRESENT` and + * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref + * glfwGetRequiredInstanceExtensions to check what instance extensions are + * required. + * + * The window surface cannot be shared with another API so the window must + * have been created with the [client api hint](@ref GLFW_CLIENT_API_attrib) + * set to `GLFW_NO_API` otherwise it generates a @ref GLFW_INVALID_VALUE error + * and returns `VK_ERROR_NATIVE_WINDOW_IN_USE_KHR`. + * + * The window surface must be destroyed before the specified Vulkan instance. + * It is the responsibility of the caller to destroy the window surface. GLFW + * does not destroy it for you. Call `vkDestroySurfaceKHR` to destroy the + * surface. + * + * @param[in] instance The Vulkan instance to create the surface in. + * @param[in] window The window to create the surface for. + * @param[in] allocator The allocator to use, or `NULL` to use the default + * allocator. + * @param[out] surface Where to store the handle of the surface. This is set + * to `VK_NULL_HANDLE` if an error occurred. + * @return `VK_SUCCESS` if successful, or a Vulkan error code if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_API_UNAVAILABLE, @ref GLFW_PLATFORM_ERROR and @ref GLFW_INVALID_VALUE + * + * @remark If an error occurs before the creation call is made, GLFW returns + * the Vulkan error code most appropriate for the error. Appropriate use of + * @ref glfwVulkanSupported and @ref glfwGetRequiredInstanceExtensions should + * eliminate almost all occurrences of these errors. + * + * @remark @macos This function currently only supports the + * `VK_MVK_macos_surface` extension from MoltenVK. + * + * @remark @macos This function creates and sets a `CAMetalLayer` instance for + * the window content view, which is required for MoltenVK to function. + * + * @thread_safety This function may be called from any thread. For + * synchronization details of Vulkan objects, see the Vulkan specification. + * + * @sa @ref vulkan_surface + * @sa @ref glfwGetRequiredInstanceExtensions + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); + +#endif /*VK_VERSION_1_0*/ + + +/************************************************************************* + * Global definition cleanup + *************************************************************************/ + +/* ------------------- BEGIN SYSTEM/COMPILER SPECIFIC -------------------- */ + +#ifdef GLFW_WINGDIAPI_DEFINED + #undef WINGDIAPI + #undef GLFW_WINGDIAPI_DEFINED +#endif + +#ifdef GLFW_CALLBACK_DEFINED + #undef CALLBACK + #undef GLFW_CALLBACK_DEFINED +#endif + +/* Some OpenGL related headers need GLAPIENTRY, but it is unconditionally + * defined by some gl.h variants (OpenBSD) so define it after if needed. + */ +#ifndef GLAPIENTRY + #define GLAPIENTRY APIENTRY +#endif + +/* -------------------- END SYSTEM/COMPILER SPECIFIC --------------------- */ + + +#ifdef __cplusplus +} +#endif + +#endif /* _glfw3_h_ */ + diff --git a/source/MaterialXGraphEditor/External/Glfw/include/GLFW/glfw3native.h b/source/MaterialXGraphEditor/External/Glfw/include/GLFW/glfw3native.h new file mode 100644 index 0000000000..e680c1e345 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/include/GLFW/glfw3native.h @@ -0,0 +1,525 @@ +/************************************************************************* + * GLFW 3.4 - www.glfw.org + * A library for OpenGL, window and input + *------------------------------------------------------------------------ + * Copyright (c) 2002-2006 Marcus Geelnard + * Copyright (c) 2006-2018 Camilla Löwy + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would + * be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + *************************************************************************/ + +#ifndef _glfw3_native_h_ +#define _glfw3_native_h_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/************************************************************************* + * Doxygen documentation + *************************************************************************/ + +/*! @file glfw3native.h + * @brief The header of the native access functions. + * + * This is the header file of the native access functions. See @ref native for + * more information. + */ +/*! @defgroup native Native access + * @brief Functions related to accessing native handles. + * + * **By using the native access functions you assert that you know what you're + * doing and how to fix problems caused by using them. If you don't, you + * shouldn't be using them.** + * + * Before the inclusion of @ref glfw3native.h, you may define zero or more + * window system API macro and zero or more context creation API macros. + * + * The chosen backends must match those the library was compiled for. Failure + * to do this will cause a link-time error. + * + * The available window API macros are: + * * `GLFW_EXPOSE_NATIVE_WIN32` + * * `GLFW_EXPOSE_NATIVE_COCOA` + * * `GLFW_EXPOSE_NATIVE_X11` + * * `GLFW_EXPOSE_NATIVE_WAYLAND` + * + * The available context API macros are: + * * `GLFW_EXPOSE_NATIVE_WGL` + * * `GLFW_EXPOSE_NATIVE_NSGL` + * * `GLFW_EXPOSE_NATIVE_GLX` + * * `GLFW_EXPOSE_NATIVE_EGL` + * * `GLFW_EXPOSE_NATIVE_OSMESA` + * + * These macros select which of the native access functions that are declared + * and which platform-specific headers to include. It is then up your (by + * definition platform-specific) code to handle which of these should be + * defined. + */ + + +/************************************************************************* + * System headers and types + *************************************************************************/ + +#if defined(GLFW_EXPOSE_NATIVE_WIN32) || defined(GLFW_EXPOSE_NATIVE_WGL) + // 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 + // callback) but windows.h assumes no one will define APIENTRY before it does + #if defined(GLFW_APIENTRY_DEFINED) + #undef APIENTRY + #undef GLFW_APIENTRY_DEFINED + #endif + #include +#elif defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL) + #if defined(__OBJC__) + #import + #else + #include + typedef void* id; + #endif +#elif defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX) + #include + #include +#elif defined(GLFW_EXPOSE_NATIVE_WAYLAND) + #include +#endif + +#if defined(GLFW_EXPOSE_NATIVE_WGL) + /* WGL is declared by windows.h */ +#endif +#if defined(GLFW_EXPOSE_NATIVE_NSGL) + /* NSGL is declared by Cocoa.h */ +#endif +#if defined(GLFW_EXPOSE_NATIVE_GLX) + #include +#endif +#if defined(GLFW_EXPOSE_NATIVE_EGL) + #include +#endif +#if defined(GLFW_EXPOSE_NATIVE_OSMESA) + #include +#endif + + +/************************************************************************* + * Functions + *************************************************************************/ + +#if defined(GLFW_EXPOSE_NATIVE_WIN32) +/*! @brief Returns the adapter device name of the specified monitor. + * + * @return The UTF-8 encoded adapter device name (for example `\\.\DISPLAY1`) + * of the specified monitor, or `NULL` if an [error](@ref error_handling) + * occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.1. + * + * @ingroup native + */ +GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* monitor); + +/*! @brief Returns the display device name of the specified monitor. + * + * @return The UTF-8 encoded display device name (for example + * `\\.\DISPLAY1\Monitor0`) of the specified monitor, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.1. + * + * @ingroup native + */ +GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* monitor); + +/*! @brief Returns the `HWND` of the specified window. + * + * @return The `HWND` of the specified window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_WGL) +/*! @brief Returns the `HGLRC` of the specified window. + * + * @return The `HGLRC` of the specified window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_COCOA) +/*! @brief Returns the `CGDirectDisplayID` of the specified monitor. + * + * @return The `CGDirectDisplayID` of the specified monitor, or + * `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.1. + * + * @ingroup native + */ +GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor); + +/*! @brief Returns the `NSWindow` of the specified window. + * + * @return The `NSWindow` of the specified window, or `nil` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_NSGL) +/*! @brief Returns the `NSOpenGLContext` of the specified window. + * + * @return The `NSOpenGLContext` of the specified window, or `nil` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI id glfwGetNSGLContext(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_X11) +/*! @brief Returns the `Display` used by GLFW. + * + * @return The `Display` used by GLFW, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI Display* glfwGetX11Display(void); + +/*! @brief Returns the `RRCrtc` of the specified monitor. + * + * @return The `RRCrtc` of the specified monitor, or `None` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.1. + * + * @ingroup native + */ +GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor); + +/*! @brief Returns the `RROutput` of the specified monitor. + * + * @return The `RROutput` of the specified monitor, or `None` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.1. + * + * @ingroup native + */ +GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor); + +/*! @brief Returns the `Window` of the specified window. + * + * @return The `Window` of the specified window, or `None` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +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 + +#if defined(GLFW_EXPOSE_NATIVE_GLX) +/*! @brief Returns the `GLXContext` of the specified window. + * + * @return The `GLXContext` of the specified window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window); + +/*! @brief Returns the `GLXWindow` of the specified window. + * + * @return The `GLXWindow` of the specified window, or `None` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_WAYLAND) +/*! @brief Returns the `struct wl_display*` used by GLFW. + * + * @return The `struct wl_display*` used by GLFW, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI struct wl_display* glfwGetWaylandDisplay(void); + +/*! @brief Returns the `struct wl_output*` of the specified monitor. + * + * @return The `struct wl_output*` of the specified monitor, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor); + +/*! @brief Returns the main `struct wl_surface*` of the specified window. + * + * @return The main `struct wl_surface*` of the specified window, or `NULL` if + * an [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_EGL) +/*! @brief Returns the `EGLDisplay` used by GLFW. + * + * @return The `EGLDisplay` used by GLFW, or `EGL_NO_DISPLAY` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI EGLDisplay glfwGetEGLDisplay(void); + +/*! @brief Returns the `EGLContext` of the specified window. + * + * @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window); + +/*! @brief Returns the `EGLSurface` of the specified window. + * + * @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_OSMESA) +/*! @brief Retrieves the color buffer associated with the specified window. + * + * @param[in] window The window whose color buffer to retrieve. + * @param[out] width Where to store the width of the color buffer, or `NULL`. + * @param[out] height Where to store the height of the color buffer, or `NULL`. + * @param[out] format Where to store the OSMesa pixel format of the color + * buffer, or `NULL`. + * @param[out] buffer Where to store the address of the color buffer, or + * `NULL`. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, int* height, int* format, void** buffer); + +/*! @brief Retrieves the depth buffer associated with the specified window. + * + * @param[in] window The window whose depth buffer to retrieve. + * @param[out] width Where to store the width of the depth buffer, or `NULL`. + * @param[out] height Where to store the height of the depth buffer, or `NULL`. + * @param[out] bytesPerValue Where to store the number of bytes per depth + * buffer element, or `NULL`. + * @param[out] buffer Where to store the address of the depth buffer, or + * `NULL`. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, int* height, int* bytesPerValue, void** buffer); + +/*! @brief Returns the `OSMesaContext` of the specified window. + * + * @return The `OSMesaContext` of the specified window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* window); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _glfw3_native_h_ */ + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/CMakeLists.txt b/source/MaterialXGraphEditor/External/Glfw/src/CMakeLists.txt new file mode 100644 index 0000000000..396e863907 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/CMakeLists.txt @@ -0,0 +1,179 @@ + +add_library(glfw_minimal "${GLFW_SOURCE_DIR}/include/GLFW/glfw3.h" + "${GLFW_SOURCE_DIR}/include/GLFW/glfw3native.h" + internal.h mappings.h context.c init.c input.c monitor.c + vulkan.c window.c) + +if (_GLFW_COCOA) + target_sources(glfw_minimal PRIVATE cocoa_platform.h cocoa_joystick.h posix_thread.h + nsgl_context.h egl_context.h osmesa_context.h + cocoa_init.m cocoa_joystick.m cocoa_monitor.m + cocoa_window.m cocoa_time.c posix_thread.c + nsgl_context.m egl_context.c osmesa_context.c) +elseif (_GLFW_WIN32) + target_sources(glfw_minimal PRIVATE win32_platform.h win32_joystick.h wgl_context.h + egl_context.h osmesa_context.h win32_init.c + win32_joystick.c win32_monitor.c win32_time.c + win32_thread.c win32_window.c wgl_context.c + egl_context.c osmesa_context.c) +elseif (_GLFW_X11) + target_sources(glfw_minimal PRIVATE x11_platform.h xkb_unicode.h posix_time.h + posix_thread.h glx_context.h egl_context.h + osmesa_context.h x11_init.c x11_monitor.c + x11_window.c xkb_unicode.c posix_time.c + posix_thread.c glx_context.c egl_context.c + osmesa_context.c) +elseif (_GLFW_WAYLAND) + target_sources(glfw_minimal PRIVATE wl_platform.h posix_time.h posix_thread.h + xkb_unicode.h egl_context.h osmesa_context.h + wl_init.c wl_monitor.c wl_window.c posix_time.c + posix_thread.c xkb_unicode.c egl_context.c + osmesa_context.c) +elseif (_GLFW_OSMESA) + target_sources(glfw_minimal PRIVATE null_platform.h null_joystick.h posix_time.h + posix_thread.h osmesa_context.h null_init.c + null_monitor.c null_window.c null_joystick.c + posix_time.c posix_thread.c osmesa_context.c) +endif() + +if (_GLFW_X11 OR _GLFW_WAYLAND) + if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + target_sources(glfw_minimal PRIVATE linux_joystick.h linux_joystick.c) + else() + target_sources(glfw_minimal PRIVATE null_joystick.h null_joystick.c) + endif() +endif() + +if (_GLFW_WAYLAND) + ecm_add_wayland_client_protocol(GLFW_WAYLAND_PROTOCOL_SOURCES + PROTOCOL + "${WAYLAND_PROTOCOLS_PKGDATADIR}/stable/xdg-shell/xdg-shell.xml" + BASENAME xdg-shell) + ecm_add_wayland_client_protocol(GLFW_WAYLAND_PROTOCOL_SOURCES + PROTOCOL + "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" + BASENAME xdg-decoration) + ecm_add_wayland_client_protocol(GLFW_WAYLAND_PROTOCOL_SOURCES + PROTOCOL + "${WAYLAND_PROTOCOLS_PKGDATADIR}/stable/viewporter/viewporter.xml" + BASENAME viewporter) + ecm_add_wayland_client_protocol(GLFW_WAYLAND_PROTOCOL_SOURCES + PROTOCOL + "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/relative-pointer/relative-pointer-unstable-v1.xml" + BASENAME relative-pointer-unstable-v1) + ecm_add_wayland_client_protocol(GLFW_WAYLAND_PROTOCOL_SOURCES + PROTOCOL + "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml" + BASENAME pointer-constraints-unstable-v1) + ecm_add_wayland_client_protocol(GLFW_WAYLAND_PROTOCOL_SOURCES + PROTOCOL + "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml" + BASENAME idle-inhibit-unstable-v1) + target_sources(glfw_minimal PRIVATE ${GLFW_WAYLAND_PROTOCOL_SOURCES}) +endif() + +if (WIN32 AND BUILD_SHARED_LIBS) + configure_file(glfw.rc.in glfw.rc @ONLY) + target_sources(glfw_minimal PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/glfw.rc") +endif() + +configure_file(glfw_config.h.in glfw_config.h @ONLY) +target_compile_definitions(glfw_minimal PRIVATE _GLFW_USE_CONFIG_H) +target_sources(glfw_minimal PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/glfw_config.h") + +set_target_properties(glfw_minimal PROPERTIES + OUTPUT_NAME ${GLFW_LIB_NAME} + VERSION ${GLFW_VERSION_MAJOR}.${GLFW_VERSION_MINOR} + SOVERSION ${GLFW_VERSION_MAJOR} + POSITION_INDEPENDENT_CODE ON + C_STANDARD 99 + C_EXTENSIONS OFF + DEFINE_SYMBOL _GLFW_BUILD_DLL + FOLDER "GLFW3") + +target_include_directories(glfw_minimal PUBLIC + "$" + "$") +target_include_directories(glfw_minimal PRIVATE + "${GLFW_SOURCE_DIR}/src" + "${GLFW_BINARY_DIR}/src" + ${glfw_INCLUDE_DIRS}) +target_link_libraries(glfw_minimal PRIVATE Threads::Threads ${glfw_LIBRARIES}) + +if (APPLE) + # For some reason CMake didn't know about .m until version 3.16 + set_source_files_properties(cocoa_init.m cocoa_joystick.m cocoa_monitor.m + cocoa_window.m nsgl_context.m PROPERTIES + LANGUAGE C) +endif() + +if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR + "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR + "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang") + + # Make GCC and Clang warn about declarations that VS 2010 and 2012 won't + # accept for all source files that VS will build + set_source_files_properties(context.c init.c input.c monitor.c vulkan.c + window.c win32_init.c win32_joystick.c + win32_monitor.c win32_time.c win32_thread.c + win32_window.c wgl_context.c egl_context.c + osmesa_context.c PROPERTIES + COMPILE_FLAGS -Wdeclaration-after-statement) + + # Enable a reasonable set of warnings (no, -Wextra is not reasonable) + target_compile_options(glfw_minimal PRIVATE "-Wall") +endif() + +if (WIN32) + target_compile_definitions(glfw_minimal PRIVATE _UNICODE) +endif() + +# HACK: When building on MinGW, WINVER and UNICODE need to be defined before +# the inclusion of stddef.h (by glfw3.h), which is itself included before +# win32_platform.h. We define them here until a saner solution can be found +# NOTE: MinGW-w64 and Visual C++ do /not/ need this hack. +if (MINGW) + target_compile_definitions(glfw_minimal PRIVATE UNICODE WINVER=0x0501) +endif() + +if (BUILD_SHARED_LIBS) + if (WIN32) + if (MINGW) + # Remove the dependency on the shared version of libgcc + # NOTE: MinGW-w64 has the correct default but MinGW needs this + target_link_libraries(glfw_minimal PRIVATE "-static-libgcc") + + # Remove the lib prefix on the DLL (but not the import library) + set_target_properties(glfw_minimal PROPERTIES PREFIX "") + + # Add a suffix to the import library to avoid naming conflicts + set_target_properties(glfw_minimal PROPERTIES IMPORT_SUFFIX "dll.a") + else() + # Add a suffix to the import library to avoid naming conflicts + set_target_properties(glfw_minimal PROPERTIES IMPORT_SUFFIX "dll.lib") + endif() + + target_compile_definitions(glfw_minimal INTERFACE GLFW_DLL) + elseif (APPLE) + set_target_properties(glfw_minimal PROPERTIES + INSTALL_NAME_DIR "${CMAKE_INSTALL_LIBDIR}") + endif() + + if (UNIX) + # Hide symbols not explicitly tagged for export from the shared library + target_compile_options(glfw_minimal PRIVATE "-fvisibility=hidden") + endif() +endif() + +if (MSVC) + target_compile_definitions(glfw_minimal PRIVATE _CRT_SECURE_NO_WARNINGS) +endif() + +if (GLFW_INSTALL) + install(TARGETS glfw_minimal + EXPORT glfwTargets + RUNTIME DESTINATION "bin" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") +endif() + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/cocoa_init.m b/source/MaterialXGraphEditor/External/Glfw/src/cocoa_init.m new file mode 100644 index 0000000000..cbc9462ff5 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/cocoa_init.m @@ -0,0 +1,621 @@ +//======================================================================== +// GLFW 3.4 macOS - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2009-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#include "internal.h" +#include // For MAXPATHLEN + +// Needed for _NSGetProgname +#include + +// Change to our application bundle's resources directory, if present +// +static void changeToResourcesDirectory(void) +{ + char resourcesPath[MAXPATHLEN]; + + CFBundleRef bundle = CFBundleGetMainBundle(); + if (!bundle) + return; + + CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle); + + CFStringRef last = CFURLCopyLastPathComponent(resourcesURL); + if (CFStringCompare(CFSTR("Resources"), last, 0) != kCFCompareEqualTo) + { + CFRelease(last); + CFRelease(resourcesURL); + return; + } + + CFRelease(last); + + if (!CFURLGetFileSystemRepresentation(resourcesURL, + true, + (UInt8*) resourcesPath, + MAXPATHLEN)) + { + CFRelease(resourcesURL); + return; + } + + CFRelease(resourcesURL); + + chdir(resourcesPath); +} + +// Set up the menu bar (manually) +// This is nasty, nasty stuff -- calls to undocumented semi-private APIs that +// could go away at any moment, lots of stuff that really should be +// localize(d|able), etc. Add a nib to save us this horror. +// +static void createMenuBar(void) +{ + size_t i; + NSString* appName = nil; + NSDictionary* bundleInfo = [[NSBundle mainBundle] infoDictionary]; + NSString* nameKeys[] = + { + @"CFBundleDisplayName", + @"CFBundleName", + @"CFBundleExecutable", + }; + + // Try to figure out what the calling application is called + + for (i = 0; i < sizeof(nameKeys) / sizeof(nameKeys[0]); i++) + { + id name = bundleInfo[nameKeys[i]]; + if (name && + [name isKindOfClass:[NSString class]] && + ![name isEqualToString:@""]) + { + appName = name; + break; + } + } + + if (!appName) + { + char** progname = _NSGetProgname(); + if (progname && *progname) + appName = @(*progname); + else + appName = @"GLFW Application"; + } + + NSMenu* bar = [[NSMenu alloc] init]; + [NSApp setMainMenu:bar]; + + NSMenuItem* appMenuItem = + [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""]; + NSMenu* appMenu = [[NSMenu alloc] init]; + [appMenuItem setSubmenu:appMenu]; + + [appMenu addItemWithTitle:[NSString stringWithFormat:@"About %@", appName] + action:@selector(orderFrontStandardAboutPanel:) + keyEquivalent:@""]; + [appMenu addItem:[NSMenuItem separatorItem]]; + NSMenu* servicesMenu = [[NSMenu alloc] init]; + [NSApp setServicesMenu:servicesMenu]; + [[appMenu addItemWithTitle:@"Services" + action:NULL + keyEquivalent:@""] setSubmenu:servicesMenu]; + [servicesMenu release]; + [appMenu addItem:[NSMenuItem separatorItem]]; + [appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", appName] + action:@selector(hide:) + keyEquivalent:@"h"]; + [[appMenu addItemWithTitle:@"Hide Others" + action:@selector(hideOtherApplications:) + keyEquivalent:@"h"] + setKeyEquivalentModifierMask:NSEventModifierFlagOption | NSEventModifierFlagCommand]; + [appMenu addItemWithTitle:@"Show All" + action:@selector(unhideAllApplications:) + keyEquivalent:@""]; + [appMenu addItem:[NSMenuItem separatorItem]]; + [appMenu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", appName] + action:@selector(terminate:) + keyEquivalent:@"q"]; + + NSMenuItem* windowMenuItem = + [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""]; + [bar release]; + NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; + [NSApp setWindowsMenu:windowMenu]; + [windowMenuItem setSubmenu:windowMenu]; + + [windowMenu addItemWithTitle:@"Minimize" + action:@selector(performMiniaturize:) + keyEquivalent:@"m"]; + [windowMenu addItemWithTitle:@"Zoom" + action:@selector(performZoom:) + keyEquivalent:@""]; + [windowMenu addItem:[NSMenuItem separatorItem]]; + [windowMenu addItemWithTitle:@"Bring All to Front" + action:@selector(arrangeInFront:) + keyEquivalent:@""]; + + // TODO: Make this appear at the bottom of the menu (for consistency) + [windowMenu addItem:[NSMenuItem separatorItem]]; + [[windowMenu addItemWithTitle:@"Enter Full Screen" + action:@selector(toggleFullScreen:) + keyEquivalent:@"f"] + setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand]; + + // Prior to Snow Leopard, we need to use this oddly-named semi-private API + // to get the application menu working properly. + SEL setAppleMenuSelector = NSSelectorFromString(@"setAppleMenu:"); + [NSApp performSelector:setAppleMenuSelector withObject:appMenu]; +} + +// Create key code translation tables +// +static void createKeyTables(void) +{ + int scancode; + + memset(_glfw.ns.keycodes, -1, sizeof(_glfw.ns.keycodes)); + memset(_glfw.ns.scancodes, -1, sizeof(_glfw.ns.scancodes)); + + _glfw.ns.keycodes[0x1D] = GLFW_KEY_0; + _glfw.ns.keycodes[0x12] = GLFW_KEY_1; + _glfw.ns.keycodes[0x13] = GLFW_KEY_2; + _glfw.ns.keycodes[0x14] = GLFW_KEY_3; + _glfw.ns.keycodes[0x15] = GLFW_KEY_4; + _glfw.ns.keycodes[0x17] = GLFW_KEY_5; + _glfw.ns.keycodes[0x16] = GLFW_KEY_6; + _glfw.ns.keycodes[0x1A] = GLFW_KEY_7; + _glfw.ns.keycodes[0x1C] = GLFW_KEY_8; + _glfw.ns.keycodes[0x19] = GLFW_KEY_9; + _glfw.ns.keycodes[0x00] = GLFW_KEY_A; + _glfw.ns.keycodes[0x0B] = GLFW_KEY_B; + _glfw.ns.keycodes[0x08] = GLFW_KEY_C; + _glfw.ns.keycodes[0x02] = GLFW_KEY_D; + _glfw.ns.keycodes[0x0E] = GLFW_KEY_E; + _glfw.ns.keycodes[0x03] = GLFW_KEY_F; + _glfw.ns.keycodes[0x05] = GLFW_KEY_G; + _glfw.ns.keycodes[0x04] = GLFW_KEY_H; + _glfw.ns.keycodes[0x22] = GLFW_KEY_I; + _glfw.ns.keycodes[0x26] = GLFW_KEY_J; + _glfw.ns.keycodes[0x28] = GLFW_KEY_K; + _glfw.ns.keycodes[0x25] = GLFW_KEY_L; + _glfw.ns.keycodes[0x2E] = GLFW_KEY_M; + _glfw.ns.keycodes[0x2D] = GLFW_KEY_N; + _glfw.ns.keycodes[0x1F] = GLFW_KEY_O; + _glfw.ns.keycodes[0x23] = GLFW_KEY_P; + _glfw.ns.keycodes[0x0C] = GLFW_KEY_Q; + _glfw.ns.keycodes[0x0F] = GLFW_KEY_R; + _glfw.ns.keycodes[0x01] = GLFW_KEY_S; + _glfw.ns.keycodes[0x11] = GLFW_KEY_T; + _glfw.ns.keycodes[0x20] = GLFW_KEY_U; + _glfw.ns.keycodes[0x09] = GLFW_KEY_V; + _glfw.ns.keycodes[0x0D] = GLFW_KEY_W; + _glfw.ns.keycodes[0x07] = GLFW_KEY_X; + _glfw.ns.keycodes[0x10] = GLFW_KEY_Y; + _glfw.ns.keycodes[0x06] = GLFW_KEY_Z; + + _glfw.ns.keycodes[0x27] = GLFW_KEY_APOSTROPHE; + _glfw.ns.keycodes[0x2A] = GLFW_KEY_BACKSLASH; + _glfw.ns.keycodes[0x2B] = GLFW_KEY_COMMA; + _glfw.ns.keycodes[0x18] = GLFW_KEY_EQUAL; + _glfw.ns.keycodes[0x32] = GLFW_KEY_GRAVE_ACCENT; + _glfw.ns.keycodes[0x21] = GLFW_KEY_LEFT_BRACKET; + _glfw.ns.keycodes[0x1B] = GLFW_KEY_MINUS; + _glfw.ns.keycodes[0x2F] = GLFW_KEY_PERIOD; + _glfw.ns.keycodes[0x1E] = GLFW_KEY_RIGHT_BRACKET; + _glfw.ns.keycodes[0x29] = GLFW_KEY_SEMICOLON; + _glfw.ns.keycodes[0x2C] = GLFW_KEY_SLASH; + _glfw.ns.keycodes[0x0A] = GLFW_KEY_WORLD_1; + + _glfw.ns.keycodes[0x33] = GLFW_KEY_BACKSPACE; + _glfw.ns.keycodes[0x39] = GLFW_KEY_CAPS_LOCK; + _glfw.ns.keycodes[0x75] = GLFW_KEY_DELETE; + _glfw.ns.keycodes[0x7D] = GLFW_KEY_DOWN; + _glfw.ns.keycodes[0x77] = GLFW_KEY_END; + _glfw.ns.keycodes[0x24] = GLFW_KEY_ENTER; + _glfw.ns.keycodes[0x35] = GLFW_KEY_ESCAPE; + _glfw.ns.keycodes[0x7A] = GLFW_KEY_F1; + _glfw.ns.keycodes[0x78] = GLFW_KEY_F2; + _glfw.ns.keycodes[0x63] = GLFW_KEY_F3; + _glfw.ns.keycodes[0x76] = GLFW_KEY_F4; + _glfw.ns.keycodes[0x60] = GLFW_KEY_F5; + _glfw.ns.keycodes[0x61] = GLFW_KEY_F6; + _glfw.ns.keycodes[0x62] = GLFW_KEY_F7; + _glfw.ns.keycodes[0x64] = GLFW_KEY_F8; + _glfw.ns.keycodes[0x65] = GLFW_KEY_F9; + _glfw.ns.keycodes[0x6D] = GLFW_KEY_F10; + _glfw.ns.keycodes[0x67] = GLFW_KEY_F11; + _glfw.ns.keycodes[0x6F] = GLFW_KEY_F12; + _glfw.ns.keycodes[0x69] = GLFW_KEY_F13; + _glfw.ns.keycodes[0x6B] = GLFW_KEY_F14; + _glfw.ns.keycodes[0x71] = GLFW_KEY_F15; + _glfw.ns.keycodes[0x6A] = GLFW_KEY_F16; + _glfw.ns.keycodes[0x40] = GLFW_KEY_F17; + _glfw.ns.keycodes[0x4F] = GLFW_KEY_F18; + _glfw.ns.keycodes[0x50] = GLFW_KEY_F19; + _glfw.ns.keycodes[0x5A] = GLFW_KEY_F20; + _glfw.ns.keycodes[0x73] = GLFW_KEY_HOME; + _glfw.ns.keycodes[0x72] = GLFW_KEY_INSERT; + _glfw.ns.keycodes[0x7B] = GLFW_KEY_LEFT; + _glfw.ns.keycodes[0x3A] = GLFW_KEY_LEFT_ALT; + _glfw.ns.keycodes[0x3B] = GLFW_KEY_LEFT_CONTROL; + _glfw.ns.keycodes[0x38] = GLFW_KEY_LEFT_SHIFT; + _glfw.ns.keycodes[0x37] = GLFW_KEY_LEFT_SUPER; + _glfw.ns.keycodes[0x6E] = GLFW_KEY_MENU; + _glfw.ns.keycodes[0x47] = GLFW_KEY_NUM_LOCK; + _glfw.ns.keycodes[0x79] = GLFW_KEY_PAGE_DOWN; + _glfw.ns.keycodes[0x74] = GLFW_KEY_PAGE_UP; + _glfw.ns.keycodes[0x7C] = GLFW_KEY_RIGHT; + _glfw.ns.keycodes[0x3D] = GLFW_KEY_RIGHT_ALT; + _glfw.ns.keycodes[0x3E] = GLFW_KEY_RIGHT_CONTROL; + _glfw.ns.keycodes[0x3C] = GLFW_KEY_RIGHT_SHIFT; + _glfw.ns.keycodes[0x36] = GLFW_KEY_RIGHT_SUPER; + _glfw.ns.keycodes[0x31] = GLFW_KEY_SPACE; + _glfw.ns.keycodes[0x30] = GLFW_KEY_TAB; + _glfw.ns.keycodes[0x7E] = GLFW_KEY_UP; + + _glfw.ns.keycodes[0x52] = GLFW_KEY_KP_0; + _glfw.ns.keycodes[0x53] = GLFW_KEY_KP_1; + _glfw.ns.keycodes[0x54] = GLFW_KEY_KP_2; + _glfw.ns.keycodes[0x55] = GLFW_KEY_KP_3; + _glfw.ns.keycodes[0x56] = GLFW_KEY_KP_4; + _glfw.ns.keycodes[0x57] = GLFW_KEY_KP_5; + _glfw.ns.keycodes[0x58] = GLFW_KEY_KP_6; + _glfw.ns.keycodes[0x59] = GLFW_KEY_KP_7; + _glfw.ns.keycodes[0x5B] = GLFW_KEY_KP_8; + _glfw.ns.keycodes[0x5C] = GLFW_KEY_KP_9; + _glfw.ns.keycodes[0x45] = GLFW_KEY_KP_ADD; + _glfw.ns.keycodes[0x41] = GLFW_KEY_KP_DECIMAL; + _glfw.ns.keycodes[0x4B] = GLFW_KEY_KP_DIVIDE; + _glfw.ns.keycodes[0x4C] = GLFW_KEY_KP_ENTER; + _glfw.ns.keycodes[0x51] = GLFW_KEY_KP_EQUAL; + _glfw.ns.keycodes[0x43] = GLFW_KEY_KP_MULTIPLY; + _glfw.ns.keycodes[0x4E] = GLFW_KEY_KP_SUBTRACT; + + for (scancode = 0; scancode < 256; scancode++) + { + // Store the reverse translation for faster key name lookup + if (_glfw.ns.keycodes[scancode] >= 0) + _glfw.ns.scancodes[_glfw.ns.keycodes[scancode]] = scancode; + } +} + +// Retrieve Unicode data for the current keyboard layout +// +static GLFWbool updateUnicodeDataNS(void) +{ + if (_glfw.ns.inputSource) + { + CFRelease(_glfw.ns.inputSource); + _glfw.ns.inputSource = NULL; + _glfw.ns.unicodeData = nil; + } + + _glfw.ns.inputSource = TISCopyCurrentKeyboardLayoutInputSource(); + if (!_glfw.ns.inputSource) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to retrieve keyboard layout input source"); + return GLFW_FALSE; + } + + _glfw.ns.unicodeData = + TISGetInputSourceProperty(_glfw.ns.inputSource, + kTISPropertyUnicodeKeyLayoutData); + if (!_glfw.ns.unicodeData) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to retrieve keyboard layout Unicode data"); + return GLFW_FALSE; + } + + return GLFW_TRUE; +} + +// Load HIToolbox.framework and the TIS symbols we need from it +// +static GLFWbool initializeTIS(void) +{ + // This works only because Cocoa has already loaded it properly + _glfw.ns.tis.bundle = + CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HIToolbox")); + if (!_glfw.ns.tis.bundle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to load HIToolbox.framework"); + return GLFW_FALSE; + } + + CFStringRef* kPropertyUnicodeKeyLayoutData = + CFBundleGetDataPointerForName(_glfw.ns.tis.bundle, + CFSTR("kTISPropertyUnicodeKeyLayoutData")); + _glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource = + CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle, + CFSTR("TISCopyCurrentKeyboardLayoutInputSource")); + _glfw.ns.tis.GetInputSourceProperty = + CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle, + CFSTR("TISGetInputSourceProperty")); + _glfw.ns.tis.GetKbdType = + CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle, + CFSTR("LMGetKbdType")); + + if (!kPropertyUnicodeKeyLayoutData || + !TISCopyCurrentKeyboardLayoutInputSource || + !TISGetInputSourceProperty || + !LMGetKbdType) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to load TIS API symbols"); + return GLFW_FALSE; + } + + _glfw.ns.tis.kPropertyUnicodeKeyLayoutData = + *kPropertyUnicodeKeyLayoutData; + + return updateUnicodeDataNS(); +} + +@interface GLFWHelper : NSObject +@end + +@implementation GLFWHelper + +- (void)selectedKeyboardInputSourceChanged:(NSObject* )object +{ + updateUnicodeDataNS(); +} + +- (void)doNothing:(id)object +{ +} + +@end // GLFWHelper + +@interface GLFWApplicationDelegate : NSObject +@end + +@implementation GLFWApplicationDelegate + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender +{ + _GLFWwindow* window; + + for (window = _glfw.windowListHead; window; window = window->next) + _glfwInputWindowCloseRequest(window); + + return NSTerminateCancel; +} + +- (void)applicationDidChangeScreenParameters:(NSNotification *) notification +{ + _GLFWwindow* window; + + for (window = _glfw.windowListHead; window; window = window->next) + { + if (window->context.client != GLFW_NO_API) + [window->context.nsgl.object update]; + } + + _glfwPollMonitorsNS(); +} + +- (void)applicationWillFinishLaunching:(NSNotification *)notification +{ + if (_glfw.hints.init.ns.menubar) + { + // In case we are unbundled, make us a proper UI application + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + + // Menu bar setup must go between sharedApplication and finishLaunching + // in order to properly emulate the behavior of NSApplicationMain + + if ([[NSBundle mainBundle] pathForResource:@"MainMenu" ofType:@"nib"]) + { + [[NSBundle mainBundle] loadNibNamed:@"MainMenu" + owner:NSApp + topLevelObjects:&_glfw.ns.nibObjects]; + } + else + createMenuBar(); + } +} + +- (void)applicationDidFinishLaunching:(NSNotification *)notification +{ + _glfw.ns.finishedLaunching = GLFW_TRUE; + _glfwPlatformPostEmptyEvent(); + [NSApp stop:nil]; +} + +- (void)applicationDidHide:(NSNotification *)notification +{ + int i; + + for (i = 0; i < _glfw.monitorCount; i++) + _glfwRestoreVideoModeNS(_glfw.monitors[i]); +} + +@end // GLFWApplicationDelegate + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +void* _glfwLoadLocalVulkanLoaderNS(void) +{ + CFBundleRef bundle = CFBundleGetMainBundle(); + if (!bundle) + return NULL; + + CFURLRef url = + CFBundleCopyAuxiliaryExecutableURL(bundle, CFSTR("libvulkan.1.dylib")); + if (!url) + return NULL; + + char path[PATH_MAX]; + void* handle = NULL; + + if (CFURLGetFileSystemRepresentation(url, true, (UInt8*) path, sizeof(path) - 1)) + handle = _glfw_dlopen(path); + + CFRelease(url); + return handle; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformInit(void) +{ + @autoreleasepool { + + _glfw.ns.helper = [[GLFWHelper alloc] init]; + + [NSThread detachNewThreadSelector:@selector(doNothing:) + toTarget:_glfw.ns.helper + withObject:nil]; + + if (NSApp) + _glfw.ns.finishedLaunching = GLFW_TRUE; + + [NSApplication sharedApplication]; + + _glfw.ns.delegate = [[GLFWApplicationDelegate alloc] init]; + if (_glfw.ns.delegate == nil) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to create application delegate"); + return GLFW_FALSE; + } + + [NSApp setDelegate:_glfw.ns.delegate]; + + NSEvent* (^block)(NSEvent*) = ^ NSEvent* (NSEvent* event) + { + if ([event modifierFlags] & NSEventModifierFlagCommand) + [[NSApp keyWindow] sendEvent:event]; + + return event; + }; + + _glfw.ns.keyUpMonitor = + [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyUp + handler:block]; + + if (_glfw.hints.init.ns.chdir) + changeToResourcesDirectory(); + + // Press and Hold prevents some keys from emitting repeated characters + NSDictionary* defaults = @{@"ApplePressAndHoldEnabled":@NO}; + [[NSUserDefaults standardUserDefaults] registerDefaults:defaults]; + + [[NSNotificationCenter defaultCenter] + addObserver:_glfw.ns.helper + selector:@selector(selectedKeyboardInputSourceChanged:) + name:NSTextInputContextKeyboardSelectionDidChangeNotification + object:nil]; + + createKeyTables(); + + _glfw.ns.eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); + if (!_glfw.ns.eventSource) + return GLFW_FALSE; + + CGEventSourceSetLocalEventsSuppressionInterval(_glfw.ns.eventSource, 0.0); + + if (!initializeTIS()) + return GLFW_FALSE; + + _glfwInitTimerNS(); + _glfwInitJoysticksNS(); + + _glfwPollMonitorsNS(); + return GLFW_TRUE; + + } // autoreleasepool +} + +void _glfwPlatformTerminate(void) +{ + @autoreleasepool { + + if (_glfw.ns.inputSource) + { + CFRelease(_glfw.ns.inputSource); + _glfw.ns.inputSource = NULL; + _glfw.ns.unicodeData = nil; + } + + if (_glfw.ns.eventSource) + { + CFRelease(_glfw.ns.eventSource); + _glfw.ns.eventSource = NULL; + } + + if (_glfw.ns.delegate) + { + [NSApp setDelegate:nil]; + [_glfw.ns.delegate release]; + _glfw.ns.delegate = nil; + } + + if (_glfw.ns.helper) + { + [[NSNotificationCenter defaultCenter] + removeObserver:_glfw.ns.helper + name:NSTextInputContextKeyboardSelectionDidChangeNotification + object:nil]; + [[NSNotificationCenter defaultCenter] + removeObserver:_glfw.ns.helper]; + [_glfw.ns.helper release]; + _glfw.ns.helper = nil; + } + + if (_glfw.ns.keyUpMonitor) + [NSEvent removeMonitor:_glfw.ns.keyUpMonitor]; + + free(_glfw.ns.clipboardString); + + _glfwTerminateNSGL(); + _glfwTerminateJoysticksNS(); + + } // autoreleasepool +} + +const char* _glfwPlatformGetVersionString(void) +{ + return _GLFW_VERSION_NUMBER " Cocoa NSGL EGL OSMesa" +#if defined(_GLFW_BUILD_DLL) + " dynamic" +#endif + ; +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/cocoa_joystick.h b/source/MaterialXGraphEditor/External/Glfw/src/cocoa_joystick.h new file mode 100644 index 0000000000..9bf5cde4a2 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/cocoa_joystick.h @@ -0,0 +1,50 @@ +//======================================================================== +// GLFW 3.4 Cocoa - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2006-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include +#include +#include +#include + +#define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickNS ns +#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE struct { int dummyJoystick; } + +#define _GLFW_PLATFORM_MAPPING_NAME "Mac OS X" + +// Cocoa-specific per-joystick data +// +typedef struct _GLFWjoystickNS +{ + IOHIDDeviceRef device; + CFMutableArrayRef axes; + CFMutableArrayRef buttons; + CFMutableArrayRef hats; +} _GLFWjoystickNS; + + +void _glfwInitJoysticksNS(void); +void _glfwTerminateJoysticksNS(void); + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/cocoa_joystick.m b/source/MaterialXGraphEditor/External/Glfw/src/cocoa_joystick.m new file mode 100644 index 0000000000..88636a87b5 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/cocoa_joystick.m @@ -0,0 +1,487 @@ +//======================================================================== +// GLFW 3.4 Cocoa - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2009-2019 Camilla Löwy +// Copyright (c) 2012 Torsten Walluhn +// +// 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. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#include "internal.h" + +#include +#include +#include + +#include +#include + +#include +#include + + +// Joystick element information +// +typedef struct _GLFWjoyelementNS +{ + IOHIDElementRef native; + uint32_t usage; + int index; + long minimum; + long maximum; + +} _GLFWjoyelementNS; + + +// Returns the value of the specified element of the specified joystick +// +static long getElementValue(_GLFWjoystick* js, _GLFWjoyelementNS* element) +{ + IOHIDValueRef valueRef; + long value = 0; + + if (js->ns.device) + { + if (IOHIDDeviceGetValue(js->ns.device, + element->native, + &valueRef) == kIOReturnSuccess) + { + value = IOHIDValueGetIntegerValue(valueRef); + } + } + + 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 +// +static void closeJoystick(_GLFWjoystick* js) +{ + int i; + + if (!js->present) + return; + + for (i = 0; i < CFArrayGetCount(js->ns.axes); i++) + free((void*) CFArrayGetValueAtIndex(js->ns.axes, i)); + CFRelease(js->ns.axes); + + for (i = 0; i < CFArrayGetCount(js->ns.buttons); i++) + free((void*) CFArrayGetValueAtIndex(js->ns.buttons, i)); + CFRelease(js->ns.buttons); + + for (i = 0; i < CFArrayGetCount(js->ns.hats); i++) + free((void*) CFArrayGetValueAtIndex(js->ns.hats, i)); + CFRelease(js->ns.hats); + + _glfwFreeJoystick(js); + _glfwInputJoystick(js, GLFW_DISCONNECTED); +} + +// Callback for user-initiated joystick addition +// +static void matchCallback(void* context, + IOReturn result, + void* sender, + IOHIDDeviceRef device) +{ + int jid; + char name[256]; + char guid[33]; + CFIndex i; + CFTypeRef property; + uint32_t vendor = 0, product = 0, version = 0; + _GLFWjoystick* js; + CFMutableArrayRef axes, buttons, hats; + + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (_glfw.joysticks[jid].ns.device == device) + return; + } + + axes = CFArrayCreateMutable(NULL, 0, NULL); + buttons = CFArrayCreateMutable(NULL, 0, NULL); + hats = CFArrayCreateMutable(NULL, 0, NULL); + + property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); + if (property) + { + CFStringGetCString(property, + name, + sizeof(name), + kCFStringEncodingUTF8); + } + else + 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 = + IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone); + + for (i = 0; i < CFArrayGetCount(elements); i++) + { + IOHIDElementRef native = (IOHIDElementRef) + CFArrayGetValueAtIndex(elements, i); + if (CFGetTypeID(native) != IOHIDElementGetTypeID()) + continue; + + const IOHIDElementType type = IOHIDElementGetType(native); + if ((type != kIOHIDElementTypeInput_Axis) && + (type != kIOHIDElementTypeInput_Button) && + (type != kIOHIDElementTypeInput_Misc)) + { + continue; + } + + CFMutableArrayRef target = NULL; + + const uint32_t usage = IOHIDElementGetUsage(native); + const uint32_t page = IOHIDElementGetUsagePage(native); + if (page == kHIDPage_GenericDesktop) + { + switch (usage) + { + case kHIDUsage_GD_X: + case kHIDUsage_GD_Y: + case kHIDUsage_GD_Z: + case kHIDUsage_GD_Rx: + case kHIDUsage_GD_Ry: + case kHIDUsage_GD_Rz: + case kHIDUsage_GD_Slider: + case kHIDUsage_GD_Dial: + case kHIDUsage_GD_Wheel: + target = axes; + break; + case kHIDUsage_GD_Hatswitch: + target = hats; + break; + case kHIDUsage_GD_DPadUp: + case kHIDUsage_GD_DPadRight: + case kHIDUsage_GD_DPadDown: + case kHIDUsage_GD_DPadLeft: + case kHIDUsage_GD_SystemMainMenu: + case kHIDUsage_GD_Select: + case kHIDUsage_GD_Start: + target = buttons; + break; + } + } + else if (page == kHIDPage_Simulation) + { + switch (usage) + { + case kHIDUsage_Sim_Accelerator: + case kHIDUsage_Sim_Brake: + case kHIDUsage_Sim_Throttle: + case kHIDUsage_Sim_Rudder: + case kHIDUsage_Sim_Steering: + target = axes; + break; + } + } + else if (page == kHIDPage_Button || page == kHIDPage_Consumer) + target = buttons; + + if (target) + { + _GLFWjoyelementNS* element = calloc(1, sizeof(_GLFWjoyelementNS)); + element->native = native; + element->usage = usage; + element->index = (int) CFArrayGetCount(target); + element->minimum = IOHIDElementGetLogicalMin(native); + element->maximum = IOHIDElementGetLogicalMax(native); + CFArrayAppendValue(target, element); + } + } + + CFRelease(elements); + + CFArraySortValues(axes, CFRangeMake(0, CFArrayGetCount(axes)), + compareElements, NULL); + CFArraySortValues(buttons, CFRangeMake(0, CFArrayGetCount(buttons)), + 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.axes = axes; + js->ns.buttons = buttons; + js->ns.hats = hats; + + _glfwInputJoystick(js, GLFW_CONNECTED); +} + +// Callback for user-initiated joystick removal +// +static void removeCallback(void* context, + IOReturn result, + void* sender, + IOHIDDeviceRef device) +{ + int jid; + + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (_glfw.joysticks[jid].ns.device == device) + { + closeJoystick(_glfw.joysticks + jid); + break; + } + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialize joystick interface +// +void _glfwInitJoysticksNS(void) +{ + CFMutableArrayRef matching; + const long usages[] = + { + kHIDUsage_GD_Joystick, + kHIDUsage_GD_GamePad, + kHIDUsage_GD_MultiAxisController + }; + + _glfw.ns.hidManager = IOHIDManagerCreate(kCFAllocatorDefault, + kIOHIDOptionsTypeNone); + + matching = CFArrayCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeArrayCallBacks); + if (!matching) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create array"); + return; + } + + for (size_t i = 0; i < sizeof(usages) / sizeof(long); i++) + { + const long page = kHIDPage_GenericDesktop; + + CFMutableDictionaryRef dict = + CFDictionaryCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (!dict) + continue; + + CFNumberRef pageRef = CFNumberCreate(kCFAllocatorDefault, + kCFNumberLongType, + &page); + CFNumberRef usageRef = CFNumberCreate(kCFAllocatorDefault, + kCFNumberLongType, + &usages[i]); + if (pageRef && usageRef) + { + CFDictionarySetValue(dict, + CFSTR(kIOHIDDeviceUsagePageKey), + pageRef); + CFDictionarySetValue(dict, + CFSTR(kIOHIDDeviceUsageKey), + usageRef); + CFArrayAppendValue(matching, dict); + } + + if (pageRef) + CFRelease(pageRef); + if (usageRef) + CFRelease(usageRef); + + CFRelease(dict); + } + + IOHIDManagerSetDeviceMatchingMultiple(_glfw.ns.hidManager, matching); + CFRelease(matching); + + IOHIDManagerRegisterDeviceMatchingCallback(_glfw.ns.hidManager, + &matchCallback, NULL); + IOHIDManagerRegisterDeviceRemovalCallback(_glfw.ns.hidManager, + &removeCallback, NULL); + IOHIDManagerScheduleWithRunLoop(_glfw.ns.hidManager, + CFRunLoopGetMain(), + kCFRunLoopDefaultMode); + IOHIDManagerOpen(_glfw.ns.hidManager, kIOHIDOptionsTypeNone); + + // Execute the run loop once in order to register any initially-attached + // joysticks + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false); +} + +// Close all opened joystick handles +// +void _glfwTerminateJoysticksNS(void) +{ + int jid; + + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + closeJoystick(_glfw.joysticks + jid); + + CFRelease(_glfw.ns.hidManager); + _glfw.ns.hidManager = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) +{ + if (mode & _GLFW_POLL_AXES) + { + CFIndex i; + + for (i = 0; i < CFArrayGetCount(js->ns.axes); i++) + { + _GLFWjoyelementNS* axis = (_GLFWjoyelementNS*) + CFArrayGetValueAtIndex(js->ns.axes, i); + + const long raw = getElementValue(js, axis); + // Perform auto calibration + if (raw < axis->minimum) + axis->minimum = raw; + if (raw > axis->maximum) + axis->maximum = raw; + + const long size = axis->maximum - axis->minimum; + if (size == 0) + _glfwInputJoystickAxis(js, (int) i, 0.f); + else + { + const float value = (2.f * (raw - axis->minimum) / size) - 1.f; + _glfwInputJoystickAxis(js, (int) i, value); + } + } + } + + if (mode & _GLFW_POLL_BUTTONS) + { + CFIndex i; + + for (i = 0; i < CFArrayGetCount(js->ns.buttons); i++) + { + _GLFWjoyelementNS* button = (_GLFWjoyelementNS*) + CFArrayGetValueAtIndex(js->ns.buttons, i); + const char value = getElementValue(js, button) - button->minimum; + const int state = (value > 0) ? GLFW_PRESS : GLFW_RELEASE; + _glfwInputJoystickButton(js, (int) i, state); + } + + for (i = 0; i < CFArrayGetCount(js->ns.hats); i++) + { + const int states[9] = + { + GLFW_HAT_UP, + GLFW_HAT_RIGHT_UP, + GLFW_HAT_RIGHT, + GLFW_HAT_RIGHT_DOWN, + GLFW_HAT_DOWN, + GLFW_HAT_LEFT_DOWN, + GLFW_HAT_LEFT, + GLFW_HAT_LEFT_UP, + GLFW_HAT_CENTERED + }; + + _GLFWjoyelementNS* hat = (_GLFWjoyelementNS*) + CFArrayGetValueAtIndex(js->ns.hats, i); + long state = getElementValue(js, hat) - hat->minimum; + if (state < 0 || state > 8) + state = 8; + + _glfwInputJoystickHat(js, (int) i, states[state]); + } + } + + return js->present; +} + +void _glfwPlatformUpdateGamepadGUID(char* guid) +{ + if ((strncmp(guid + 4, "000000000000", 12) == 0) && + (strncmp(guid + 20, "000000000000", 12) == 0)) + { + char original[33]; + strncpy(original, guid, sizeof(original) - 1); + sprintf(guid, "03000000%.4s0000%.4s000000000000", + original, original + 16); + } +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/cocoa_monitor.m b/source/MaterialXGraphEditor/External/Glfw/src/cocoa_monitor.m new file mode 100644 index 0000000000..42f2dce285 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/cocoa_monitor.m @@ -0,0 +1,612 @@ +//======================================================================== +// GLFW 3.4 macOS - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#include "internal.h" + +#include +#include +#include + +#include +#include + + +// Get the name of the specified display, or NULL +// +static char* getDisplayName(CGDirectDisplayID displayID) +{ + io_iterator_t it; + io_service_t service; + CFDictionaryRef info; + + if (IOServiceGetMatchingServices(kIOMasterPortDefault, + IOServiceMatching("IODisplayConnect"), + &it) != 0) + { + // This may happen if a desktop Mac is running headless + return NULL; + } + + while ((service = IOIteratorNext(it)) != 0) + { + info = IODisplayCreateInfoDictionary(service, + kIODisplayOnlyPreferredName); + + CFNumberRef vendorIDRef = + CFDictionaryGetValue(info, CFSTR(kDisplayVendorID)); + CFNumberRef productIDRef = + CFDictionaryGetValue(info, CFSTR(kDisplayProductID)); + if (!vendorIDRef || !productIDRef) + { + CFRelease(info); + continue; + } + + unsigned int vendorID, productID; + CFNumberGetValue(vendorIDRef, kCFNumberIntType, &vendorID); + CFNumberGetValue(productIDRef, kCFNumberIntType, &productID); + + if (CGDisplayVendorNumber(displayID) == vendorID && + CGDisplayModelNumber(displayID) == productID) + { + // Info dictionary is used and freed below + break; + } + + CFRelease(info); + } + + IOObjectRelease(it); + + if (!service) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to find service port for display"); + return NULL; + } + + CFDictionaryRef names = + CFDictionaryGetValue(info, CFSTR(kDisplayProductName)); + + CFStringRef nameRef; + + if (!names || !CFDictionaryGetValueIfPresent(names, CFSTR("en_US"), + (const void**) &nameRef)) + { + // This may happen if a desktop Mac is running headless + CFRelease(info); + return NULL; + } + + const CFIndex size = + CFStringGetMaximumSizeForEncoding(CFStringGetLength(nameRef), + kCFStringEncodingUTF8); + char* name = calloc(size + 1, 1); + CFStringGetCString(nameRef, name, size, kCFStringEncodingUTF8); + + CFRelease(info); + return name; +} + +// Check whether the display mode should be included in enumeration +// +static GLFWbool modeIsGood(CGDisplayModeRef mode) +{ + uint32_t flags = CGDisplayModeGetIOFlags(mode); + + if (!(flags & kDisplayModeValidFlag) || !(flags & kDisplayModeSafeFlag)) + return GLFW_FALSE; + if (flags & kDisplayModeInterlacedFlag) + return GLFW_FALSE; + if (flags & kDisplayModeStretchedFlag) + return GLFW_FALSE; + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100 + CFStringRef format = CGDisplayModeCopyPixelEncoding(mode); + if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) && + CFStringCompare(format, CFSTR(IO32BitDirectPixels), 0)) + { + CFRelease(format); + return GLFW_FALSE; + } + + CFRelease(format); +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED */ + return GLFW_TRUE; +} + +// Convert Core Graphics display mode to GLFW video mode +// +static GLFWvidmode vidmodeFromCGDisplayMode(CGDisplayModeRef mode, + double fallbackRefreshRate) +{ + GLFWvidmode result; + result.width = (int) CGDisplayModeGetWidth(mode); + result.height = (int) CGDisplayModeGetHeight(mode); + result.refreshRate = (int) round(CGDisplayModeGetRefreshRate(mode)); + + if (result.refreshRate == 0) + result.refreshRate = (int) round(fallbackRefreshRate); + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100 + CFStringRef format = CGDisplayModeCopyPixelEncoding(mode); + if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) == 0) + { + result.redBits = 5; + result.greenBits = 5; + result.blueBits = 5; + } + else +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED */ + { + result.redBits = 8; + result.greenBits = 8; + result.blueBits = 8; + } + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100 + CFRelease(format); +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED */ + return result; +} + +// Starts reservation for display fading +// +static CGDisplayFadeReservationToken beginFadeReservation(void) +{ + CGDisplayFadeReservationToken token = kCGDisplayFadeReservationInvalidToken; + + if (CGAcquireDisplayFadeReservation(5, &token) == kCGErrorSuccess) + { + CGDisplayFade(token, 0.3, + kCGDisplayBlendNormal, + kCGDisplayBlendSolidColor, + 0.0, 0.0, 0.0, + TRUE); + } + + return token; +} + +// Ends reservation for display fading +// +static void endFadeReservation(CGDisplayFadeReservationToken token) +{ + if (token != kCGDisplayFadeReservationInvalidToken) + { + CGDisplayFade(token, 0.5, + kCGDisplayBlendSolidColor, + kCGDisplayBlendNormal, + 0.0, 0.0, 0.0, + FALSE); + CGReleaseDisplayFadeReservation(token); + } +} + +// Finds and caches the NSScreen corresponding to the specified monitor +// +static GLFWbool refreshMonitorScreen(_GLFWmonitor* monitor) +{ + if (monitor->ns.screen) + return GLFW_TRUE; + + for (NSScreen* screen in [NSScreen screens]) + { + NSNumber* displayID = [screen deviceDescription][@"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; + return GLFW_TRUE; + } + } + + _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to find a screen for monitor"); + return GLFW_FALSE; +} + +// Returns the display refresh rate queried from the I/O registry +// +static double getFallbackRefreshRate(CGDirectDisplayID displayID) +{ + double refreshRate = 60.0; + + io_iterator_t it; + io_service_t service; + + if (IOServiceGetMatchingServices(kIOMasterPortDefault, + IOServiceMatching("IOFramebuffer"), + &it) != 0) + { + return refreshRate; + } + + while ((service = IOIteratorNext(it)) != 0) + { + const CFNumberRef indexRef = + IORegistryEntryCreateCFProperty(service, + CFSTR("IOFramebufferOpenGLIndex"), + kCFAllocatorDefault, + kNilOptions); + if (!indexRef) + continue; + + uint32_t index = 0; + CFNumberGetValue(indexRef, kCFNumberIntType, &index); + CFRelease(indexRef); + + if (CGOpenGLDisplayMaskToDisplayID(1 << index) != displayID) + continue; + + const CFNumberRef clockRef = + IORegistryEntryCreateCFProperty(service, + CFSTR("IOFBCurrentPixelClock"), + kCFAllocatorDefault, + kNilOptions); + const CFNumberRef countRef = + IORegistryEntryCreateCFProperty(service, + CFSTR("IOFBCurrentPixelCount"), + kCFAllocatorDefault, + kNilOptions); + if (!clockRef || !countRef) + break; + + uint32_t clock = 0, count = 0; + CFNumberGetValue(clockRef, kCFNumberIntType, &clock); + CFNumberGetValue(countRef, kCFNumberIntType, &count); + CFRelease(clockRef); + CFRelease(countRef); + + if (clock > 0 && count > 0) + refreshRate = clock / (double) count; + + break; + } + + IOObjectRelease(it); + return refreshRate; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Poll for changes in the set of connected monitors +// +void _glfwPollMonitorsNS(void) +{ + uint32_t displayCount; + CGGetOnlineDisplayList(0, NULL, &displayCount); + CGDirectDisplayID* displays = calloc(displayCount, sizeof(CGDirectDisplayID)); + CGGetOnlineDisplayList(displayCount, displays, &displayCount); + + for (int i = 0; i < _glfw.monitorCount; i++) + _glfw.monitors[i]->ns.screen = nil; + + _GLFWmonitor** disconnected = NULL; + uint32_t disconnectedCount = _glfw.monitorCount; + if (disconnectedCount) + { + disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); + memcpy(disconnected, + _glfw.monitors, + _glfw.monitorCount * sizeof(_GLFWmonitor*)); + } + + for (uint32_t i = 0; i < displayCount; i++) + { + if (CGDisplayIsAsleep(displays[i])) + continue; + + // HACK: Compare unit numbers instead of display IDs to work around + // display replacement on machines with automatic graphics + // switching + const uint32_t unitNumber = CGDisplayUnitNumber(displays[i]); + for (uint32_t j = 0; j < disconnectedCount; j++) + { + if (disconnected[j] && disconnected[j]->ns.unitNumber == unitNumber) + { + disconnected[j] = NULL; + break; + } + } + + const CGSize size = CGDisplayScreenSize(displays[i]); + char* name = getDisplayName(displays[i]); + if (!name) + name = _glfw_strdup("Unknown"); + + _GLFWmonitor* monitor = _glfwAllocMonitor(name, size.width, size.height); + monitor->ns.displayID = displays[i]; + monitor->ns.unitNumber = unitNumber; + + free(name); + + CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displays[i]); + if (CGDisplayModeGetRefreshRate(mode) == 0.0) + monitor->ns.fallbackRefreshRate = getFallbackRefreshRate(displays[i]); + CGDisplayModeRelease(mode); + + _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST); + } + + for (uint32_t i = 0; i < disconnectedCount; i++) + { + if (disconnected[i]) + _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); + } + + free(disconnected); + free(displays); +} + +// Change the current video mode +// +void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired) +{ + GLFWvidmode current; + _glfwPlatformGetVideoMode(monitor, ¤t); + + const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired); + if (_glfwCompareVideoModes(¤t, best) == 0) + return; + + CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL); + const CFIndex count = CFArrayGetCount(modes); + CGDisplayModeRef native = NULL; + + for (CFIndex i = 0; i < count; i++) + { + CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i); + if (!modeIsGood(dm)) + continue; + + const GLFWvidmode mode = + vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate); + if (_glfwCompareVideoModes(best, &mode) == 0) + { + native = dm; + break; + } + } + + if (native) + { + if (monitor->ns.previousMode == NULL) + monitor->ns.previousMode = CGDisplayCopyDisplayMode(monitor->ns.displayID); + + CGDisplayFadeReservationToken token = beginFadeReservation(); + CGDisplaySetDisplayMode(monitor->ns.displayID, native, NULL); + endFadeReservation(token); + } + + CFRelease(modes); +} + +// Restore the previously saved (original) video mode +// +void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor) +{ + if (monitor->ns.previousMode) + { + CGDisplayFadeReservationToken token = beginFadeReservation(); + CGDisplaySetDisplayMode(monitor->ns.displayID, + monitor->ns.previousMode, NULL); + endFadeReservation(token); + + CGDisplayModeRelease(monitor->ns.previousMode); + monitor->ns.previousMode = NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) +{ +} + +void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) +{ + @autoreleasepool { + + const CGRect bounds = CGDisplayBounds(monitor->ns.displayID); + + if (xpos) + *xpos = (int) bounds.origin.x; + if (ypos) + *ypos = (int) bounds.origin.y; + + } // autoreleasepool +} + +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + @autoreleasepool { + + if (!refreshMonitorScreen(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); + + } // autoreleasepool +} + +void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, + int* xpos, int* ypos, + int* width, int* height) +{ + @autoreleasepool { + + if (!refreshMonitorScreen(monitor)) + return; + + const NSRect frameRect = [monitor->ns.screen visibleFrame]; + + if (xpos) + *xpos = frameRect.origin.x; + if (ypos) + *ypos = _glfwTransformYNS(frameRect.origin.y + frameRect.size.height - 1); + if (width) + *width = frameRect.size.width; + if (height) + *height = frameRect.size.height; + + } // autoreleasepool +} + +GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) +{ + @autoreleasepool { + + *count = 0; + + CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL); + const CFIndex found = CFArrayGetCount(modes); + GLFWvidmode* result = calloc(found, sizeof(GLFWvidmode)); + + for (CFIndex i = 0; i < found; i++) + { + CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i); + if (!modeIsGood(dm)) + continue; + + const GLFWvidmode mode = + vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate); + CFIndex j; + + for (j = 0; j < *count; j++) + { + if (_glfwCompareVideoModes(result + j, &mode) == 0) + break; + } + + // Skip duplicate modes + if (i < *count) + continue; + + (*count)++; + result[*count - 1] = mode; + } + + CFRelease(modes); + return result; + + } // autoreleasepool +} + +void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode *mode) +{ + @autoreleasepool { + + CGDisplayModeRef native = CGDisplayCopyDisplayMode(monitor->ns.displayID); + *mode = vidmodeFromCGDisplayMode(native, monitor->ns.fallbackRefreshRate); + CGDisplayModeRelease(native); + + } // autoreleasepool +} + +GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +{ + @autoreleasepool { + + uint32_t size = CGDisplayGammaTableCapacity(monitor->ns.displayID); + CGGammaValue* values = calloc(size * 3, sizeof(CGGammaValue)); + + CGGetDisplayTransferByTable(monitor->ns.displayID, + size, + values, + values + size, + values + size * 2, + &size); + + _glfwAllocGammaArrays(ramp, size); + + for (uint32_t i = 0; i < size; i++) + { + ramp->red[i] = (unsigned short) (values[i] * 65535); + ramp->green[i] = (unsigned short) (values[i + size] * 65535); + ramp->blue[i] = (unsigned short) (values[i + size * 2] * 65535); + } + + free(values); + return GLFW_TRUE; + + } // autoreleasepool +} + +void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) +{ + @autoreleasepool { + + CGGammaValue* values = calloc(ramp->size * 3, sizeof(CGGammaValue)); + + for (unsigned int i = 0; i < ramp->size; i++) + { + values[i] = ramp->red[i] / 65535.f; + values[i + ramp->size] = ramp->green[i] / 65535.f; + values[i + ramp->size * 2] = ramp->blue[i] / 65535.f; + } + + CGSetDisplayTransferByTable(monitor->ns.displayID, + ramp->size, + values, + values + ramp->size, + values + ramp->size * 2); + + free(values); + + } // autoreleasepool +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(kCGNullDirectDisplay); + return monitor->ns.displayID; +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/cocoa_platform.h b/source/MaterialXGraphEditor/External/Glfw/src/cocoa_platform.h new file mode 100644 index 0000000000..9a979af219 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/cocoa_platform.h @@ -0,0 +1,211 @@ +//======================================================================== +// GLFW 3.4 macOS - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2009-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include +#include + +#include + +// NOTE: All of NSGL was deprecated in the 10.14 SDK +// This disables the pointless warnings for every symbol we use +#define GL_SILENCE_DEPRECATION + +#if defined(__OBJC__) +#import +#else +typedef void* id; +#endif + +// NOTE: Many Cocoa enum values have been renamed and we need to build across +// SDK versions where one is unavailable or the other deprecated +// We use the newer names in code and these macros to handle compatibility +#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200 + #define NSBitmapFormatAlphaNonpremultiplied NSAlphaNonpremultipliedBitmapFormat + #define NSEventMaskAny NSAnyEventMask + #define NSEventMaskKeyUp NSKeyUpMask + #define NSEventModifierFlagCapsLock NSAlphaShiftKeyMask + #define NSEventModifierFlagCommand NSCommandKeyMask + #define NSEventModifierFlagControl NSControlKeyMask + #define NSEventModifierFlagDeviceIndependentFlagsMask NSDeviceIndependentModifierFlagsMask + #define NSEventModifierFlagOption NSAlternateKeyMask + #define NSEventModifierFlagShift NSShiftKeyMask + #define NSEventTypeApplicationDefined NSApplicationDefined + #define NSWindowStyleMaskBorderless NSBorderlessWindowMask + #define NSWindowStyleMaskClosable NSClosableWindowMask + #define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask + #define NSWindowStyleMaskResizable NSResizableWindowMask + #define NSWindowStyleMaskTitled NSTitledWindowMask +#endif + +typedef VkFlags VkMacOSSurfaceCreateFlagsMVK; +typedef VkFlags VkMetalSurfaceCreateFlagsEXT; + +typedef struct VkMacOSSurfaceCreateInfoMVK +{ + VkStructureType sType; + const void* pNext; + VkMacOSSurfaceCreateFlagsMVK flags; + const void* pView; +} VkMacOSSurfaceCreateInfoMVK; + +typedef struct VkMetalSurfaceCreateInfoEXT +{ + VkStructureType sType; + const void* pNext; + VkMetalSurfaceCreateFlagsEXT flags; + const void* pLayer; +} VkMetalSurfaceCreateInfoEXT; + +typedef VkResult (APIENTRY *PFN_vkCreateMacOSSurfaceMVK)(VkInstance,const VkMacOSSurfaceCreateInfoMVK*,const VkAllocationCallbacks*,VkSurfaceKHR*); +typedef VkResult (APIENTRY *PFN_vkCreateMetalSurfaceEXT)(VkInstance,const VkMetalSurfaceCreateInfoEXT*,const VkAllocationCallbacks*,VkSurfaceKHR*); + +#include "posix_thread.h" +#include "cocoa_joystick.h" +#include "nsgl_context.h" +#include "egl_context.h" +#include "osmesa_context.h" + +#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) +#define _glfw_dlclose(handle) dlclose(handle) +#define _glfw_dlsym(handle, name) dlsym(handle, name) + +#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->ns.view) +#define _GLFW_EGL_NATIVE_DISPLAY EGL_DEFAULT_DISPLAY + +#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNS ns +#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryNS ns +#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerNS ns +#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorNS ns +#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorNS ns + +// HIToolbox.framework pointer typedefs +#define kTISPropertyUnicodeKeyLayoutData _glfw.ns.tis.kPropertyUnicodeKeyLayoutData +typedef TISInputSourceRef (*PFN_TISCopyCurrentKeyboardLayoutInputSource)(void); +#define TISCopyCurrentKeyboardLayoutInputSource _glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource +typedef void* (*PFN_TISGetInputSourceProperty)(TISInputSourceRef,CFStringRef); +#define TISGetInputSourceProperty _glfw.ns.tis.GetInputSourceProperty +typedef UInt8 (*PFN_LMGetKbdType)(void); +#define LMGetKbdType _glfw.ns.tis.GetKbdType + + +// Cocoa-specific per-window data +// +typedef struct _GLFWwindowNS +{ + id object; + id delegate; + id view; + id layer; + + GLFWbool maximized; + GLFWbool retina; + + // Cached window properties to filter out duplicate events + int width, height; + int fbWidth, fbHeight; + float xscale, yscale; + + // The total sum of the distances the cursor has been warped + // since the last cursor motion event was processed + // This is kept to counteract Cocoa doing the same internally + double cursorWarpDeltaX, cursorWarpDeltaY; + +} _GLFWwindowNS; + +// Cocoa-specific global data +// +typedef struct _GLFWlibraryNS +{ + CGEventSourceRef eventSource; + id delegate; + GLFWbool finishedLaunching; + GLFWbool cursorHidden; + TISInputSourceRef inputSource; + IOHIDManagerRef hidManager; + id unicodeData; + id helper; + id keyUpMonitor; + id nibObjects; + + char keynames[GLFW_KEY_LAST + 1][17]; + short int keycodes[256]; + short int scancodes[GLFW_KEY_LAST + 1]; + char* clipboardString; + CGPoint cascadePoint; + // Where to place the cursor when re-enabled + double restoreCursorPosX, restoreCursorPosY; + // The window whose disabled cursor mode is active + _GLFWwindow* disabledCursorWindow; + + struct { + CFBundleRef bundle; + PFN_TISCopyCurrentKeyboardLayoutInputSource CopyCurrentKeyboardLayoutInputSource; + PFN_TISGetInputSourceProperty GetInputSourceProperty; + PFN_LMGetKbdType GetKbdType; + CFStringRef kPropertyUnicodeKeyLayoutData; + } tis; + +} _GLFWlibraryNS; + +// Cocoa-specific per-monitor data +// +typedef struct _GLFWmonitorNS +{ + CGDirectDisplayID displayID; + CGDisplayModeRef previousMode; + uint32_t unitNumber; + id screen; + double fallbackRefreshRate; + +} _GLFWmonitorNS; + +// Cocoa-specific per-cursor data +// +typedef struct _GLFWcursorNS +{ + id object; + +} _GLFWcursorNS; + +// Cocoa-specific global timer data +// +typedef struct _GLFWtimerNS +{ + uint64_t frequency; + +} _GLFWtimerNS; + + +void _glfwInitTimerNS(void); + +void _glfwPollMonitorsNS(void); +void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired); +void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor); + +float _glfwTransformYNS(float y); + +void* _glfwLoadLocalVulkanLoaderNS(void); + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/cocoa_time.c b/source/MaterialXGraphEditor/External/Glfw/src/cocoa_time.c new file mode 100644 index 0000000000..4bf646c8b6 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/cocoa_time.c @@ -0,0 +1,62 @@ +//======================================================================== +// GLFW 3.4 macOS - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2009-2016 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#include "internal.h" + +#include + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialise timer +// +void _glfwInitTimerNS(void) +{ + mach_timebase_info_data_t info; + mach_timebase_info(&info); + + _glfw.timer.ns.frequency = (info.denom * 1e9) / info.numer; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +uint64_t _glfwPlatformGetTimerValue(void) +{ + return mach_absolute_time(); +} + +uint64_t _glfwPlatformGetTimerFrequency(void) +{ + return _glfw.timer.ns.frequency; +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/cocoa_window.m b/source/MaterialXGraphEditor/External/Glfw/src/cocoa_window.m new file mode 100644 index 0000000000..e12b5cda7f --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/cocoa_window.m @@ -0,0 +1,1846 @@ +//======================================================================== +// GLFW 3.4 macOS - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2009-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#include "internal.h" + +#include +#include + +// Returns the style mask corresponding to the window settings +// +static NSUInteger getStyleMask(_GLFWwindow* window) +{ + NSUInteger styleMask = NSWindowStyleMaskMiniaturizable; + + if (window->monitor || !window->decorated) + styleMask |= NSWindowStyleMaskBorderless; + else + { + styleMask |= NSWindowStyleMaskTitled | + NSWindowStyleMaskClosable; + + if (window->resizable) + styleMask |= NSWindowStyleMaskResizable; + } + + return styleMask; +} + +// Returns whether the cursor is in the content area of the specified window +// +static GLFWbool cursorInContentArea(_GLFWwindow* window) +{ + const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream]; + 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 +// +static void updateCursorImage(_GLFWwindow* window) +{ + if (window->cursorMode == GLFW_CURSOR_NORMAL) + { + showCursor(window); + + if (window->cursor) + [(NSCursor*) window->cursor->ns.object set]; + else + [[NSCursor arrowCursor] set]; + } + else + hideCursor(window); +} + +// Apply chosen cursor mode to a focused window +// +static void updateCursorMode(_GLFWwindow* window) +{ + if (window->cursorMode == GLFW_CURSOR_DISABLED) + { + _glfw.ns.disabledCursorWindow = window; + _glfwPlatformGetCursorPos(window, + &_glfw.ns.restoreCursorPosX, + &_glfw.ns.restoreCursorPosY); + _glfwCenterCursorInContentArea(window); + CGAssociateMouseAndMouseCursorPosition(false); + } + else if (_glfw.ns.disabledCursorWindow == window) + { + _glfw.ns.disabledCursorWindow = NULL; + CGAssociateMouseAndMouseCursorPosition(true); + _glfwPlatformSetCursorPos(window, + _glfw.ns.restoreCursorPosX, + _glfw.ns.restoreCursorPosY); + } + + if (cursorInContentArea(window)) + updateCursorImage(window); +} + +// Make the specified window and its video mode active on its monitor +// +static void acquireMonitor(_GLFWwindow* window) +{ + _glfwSetVideoModeNS(window->monitor, &window->videoMode); + const CGRect bounds = CGDisplayBounds(window->monitor->ns.displayID); + const NSRect frame = NSMakeRect(bounds.origin.x, + _glfwTransformYNS(bounds.origin.y + bounds.size.height - 1), + bounds.size.width, + bounds.size.height); + + [window->ns.object setFrame:frame display:YES]; + + _glfwInputMonitorWindow(window->monitor, window); +} + +// Remove the window and restore the original video mode +// +static void releaseMonitor(_GLFWwindow* window) +{ + if (window->monitor->window != window) + return; + + _glfwInputMonitorWindow(window->monitor, NULL); + _glfwRestoreVideoModeNS(window->monitor); +} + +// Translates macOS key modifiers into GLFW ones +// +static int translateFlags(NSUInteger flags) +{ + int mods = 0; + + if (flags & NSEventModifierFlagShift) + mods |= GLFW_MOD_SHIFT; + if (flags & NSEventModifierFlagControl) + mods |= GLFW_MOD_CONTROL; + if (flags & NSEventModifierFlagOption) + mods |= GLFW_MOD_ALT; + if (flags & NSEventModifierFlagCommand) + mods |= GLFW_MOD_SUPER; + if (flags & NSEventModifierFlagCapsLock) + mods |= GLFW_MOD_CAPS_LOCK; + + return mods; +} + +// Translates a macOS keycode to a GLFW keycode +// +static int translateKey(unsigned int key) +{ + if (key >= sizeof(_glfw.ns.keycodes) / sizeof(_glfw.ns.keycodes[0])) + return GLFW_KEY_UNKNOWN; + + return _glfw.ns.keycodes[key]; +} + +// Translate a GLFW keycode to a Cocoa modifier flag +// +static NSUInteger translateKeyToModifierFlag(int key) +{ + switch (key) + { + case GLFW_KEY_LEFT_SHIFT: + case GLFW_KEY_RIGHT_SHIFT: + return NSEventModifierFlagShift; + case GLFW_KEY_LEFT_CONTROL: + case GLFW_KEY_RIGHT_CONTROL: + return NSEventModifierFlagControl; + case GLFW_KEY_LEFT_ALT: + case GLFW_KEY_RIGHT_ALT: + return NSEventModifierFlagOption; + case GLFW_KEY_LEFT_SUPER: + case GLFW_KEY_RIGHT_SUPER: + return NSEventModifierFlagCommand; + case GLFW_KEY_CAPS_LOCK: + return NSEventModifierFlagCapsLock; + } + + return 0; +} + +// Defines a constant for empty ranges in NSTextInputClient +// +static const NSRange kEmptyRange = { NSNotFound, 0 }; + + +//------------------------------------------------------------------------ +// Delegate for window related notifications +//------------------------------------------------------------------------ + +@interface GLFWWindowDelegate : NSObject +{ + _GLFWwindow* window; +} + +- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow; + +@end + +@implementation GLFWWindowDelegate + +- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow +{ + self = [super init]; + if (self != nil) + window = initWindow; + + return self; +} + +- (BOOL)windowShouldClose:(id)sender +{ + _glfwInputWindowCloseRequest(window); + return NO; +} + +- (void)windowDidResize:(NSNotification *)notification +{ + if (window->context.client != GLFW_NO_API) + [window->context.nsgl.object update]; + + if (_glfw.ns.disabledCursorWindow == window) + _glfwCenterCursorInContentArea(window); + + const int maximized = [window->ns.object isZoomed]; + if (window->ns.maximized != maximized) + { + window->ns.maximized = maximized; + _glfwInputWindowMaximize(window, maximized); + } + + const NSRect contentRect = [window->ns.view frame]; + 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); + } + + 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); + } +} + +- (void)windowDidMove:(NSNotification *)notification +{ + if (window->context.client != GLFW_NO_API) + [window->context.nsgl.object update]; + + if (_glfw.ns.disabledCursorWindow == window) + _glfwCenterCursorInContentArea(window); + + int x, y; + _glfwPlatformGetWindowPos(window, &x, &y); + _glfwInputWindowPos(window, x, y); +} + +- (void)windowDidMiniaturize:(NSNotification *)notification +{ + if (window->monitor) + releaseMonitor(window); + + _glfwInputWindowIconify(window, GLFW_TRUE); +} + +- (void)windowDidDeminiaturize:(NSNotification *)notification +{ + if (window->monitor) + acquireMonitor(window); + + _glfwInputWindowIconify(window, GLFW_FALSE); +} + +- (void)windowDidBecomeKey:(NSNotification *)notification +{ + if (_glfw.ns.disabledCursorWindow == window) + _glfwCenterCursorInContentArea(window); + + _glfwInputWindowFocus(window, GLFW_TRUE); + updateCursorMode(window); +} + +- (void)windowDidResignKey:(NSNotification *)notification +{ + if (window->monitor && window->autoIconify) + _glfwPlatformIconifyWindow(window); + + _glfwInputWindowFocus(window, GLFW_FALSE); +} + +@end + + +//------------------------------------------------------------------------ +// Content view class for the GLFW window +//------------------------------------------------------------------------ + +@interface GLFWContentView : NSView +{ + _GLFWwindow* window; + NSTrackingArea* trackingArea; + NSMutableAttributedString* markedText; +} + +- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow; + +@end + +@implementation GLFWContentView + +- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow +{ + self = [super init]; + if (self != nil) + { + window = initWindow; + trackingArea = nil; + markedText = [[NSMutableAttributedString alloc] init]; + + [self updateTrackingAreas]; + // NOTE: kUTTypeURL corresponds to NSPasteboardTypeURL but is available + // on 10.7 without having been deprecated yet + [self registerForDraggedTypes:@[(__bridge NSString*) kUTTypeURL]]; + } + + return self; +} + +- (void)dealloc +{ + [trackingArea release]; + [markedText release]; + [super dealloc]; +} + +- (BOOL)isOpaque +{ + return [window->ns.object isOpaque]; +} + +- (BOOL)canBecomeKeyView +{ + return YES; +} + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (BOOL)wantsUpdateLayer +{ + return YES; +} + +- (void)updateLayer +{ + if (window->context.client != GLFW_NO_API) + [window->context.nsgl.object update]; + + _glfwInputWindowDamage(window); +} + +- (void)cursorUpdate:(NSEvent *)event +{ + updateCursorImage(window); +} + +- (BOOL)acceptsFirstMouse:(NSEvent *)event +{ + return YES; +} + +- (void)mouseDown:(NSEvent *)event +{ + _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_LEFT, + GLFW_PRESS, + translateFlags([event modifierFlags])); +} + +- (void)mouseDragged:(NSEvent *)event +{ + [self mouseMoved:event]; +} + +- (void)mouseUp:(NSEvent *)event +{ + _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_LEFT, + GLFW_RELEASE, + translateFlags([event modifierFlags])); +} + +- (void)mouseMoved:(NSEvent *)event +{ + if (window->cursorMode == GLFW_CURSOR_DISABLED) + { + const double dx = [event deltaX] - window->ns.cursorWarpDeltaX; + const double dy = [event deltaY] - window->ns.cursorWarpDeltaY; + + _glfwInputCursorPos(window, + window->virtualCursorPosX + dx, + window->virtualCursorPosY + dy); + } + else + { + const NSRect contentRect = [window->ns.view frame]; + // NOTE: The returned location uses base 0,1 not 0,0 + const NSPoint pos = [event locationInWindow]; + + _glfwInputCursorPos(window, pos.x, contentRect.size.height - pos.y); + } + + window->ns.cursorWarpDeltaX = 0; + window->ns.cursorWarpDeltaY = 0; +} + +- (void)rightMouseDown:(NSEvent *)event +{ + _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_RIGHT, + GLFW_PRESS, + translateFlags([event modifierFlags])); +} + +- (void)rightMouseDragged:(NSEvent *)event +{ + [self mouseMoved:event]; +} + +- (void)rightMouseUp:(NSEvent *)event +{ + _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_RIGHT, + GLFW_RELEASE, + translateFlags([event modifierFlags])); +} + +- (void)otherMouseDown:(NSEvent *)event +{ + _glfwInputMouseClick(window, + (int) [event buttonNumber], + GLFW_PRESS, + translateFlags([event modifierFlags])); +} + +- (void)otherMouseDragged:(NSEvent *)event +{ + [self mouseMoved:event]; +} + +- (void)otherMouseUp:(NSEvent *)event +{ + _glfwInputMouseClick(window, + (int) [event buttonNumber], + GLFW_RELEASE, + translateFlags([event modifierFlags])); +} + +- (void)mouseExited:(NSEvent *)event +{ + if (window->cursorMode == GLFW_CURSOR_HIDDEN) + showCursor(window); + + _glfwInputCursorEnter(window, GLFW_FALSE); +} + +- (void)mouseEntered:(NSEvent *)event +{ + if (window->cursorMode == GLFW_CURSOR_HIDDEN) + hideCursor(window); + + _glfwInputCursorEnter(window, GLFW_TRUE); +} + +- (void)viewDidChangeBackingProperties +{ + const NSRect contentRect = [window->ns.view frame]; + 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); + } + + const float xscale = fbRect.size.width / contentRect.size.width; + const float yscale = fbRect.size.height / contentRect.size.height; + + if (xscale != window->ns.xscale || yscale != window->ns.yscale) + { + window->ns.xscale = xscale; + window->ns.yscale = yscale; + _glfwInputWindowContentScale(window, xscale, yscale); + + if (window->ns.retina && window->ns.layer) + [window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]]; + } +} + +- (void)drawRect:(NSRect)rect +{ + _glfwInputWindowDamage(window); +} + +- (void)updateTrackingAreas +{ + if (trackingArea != nil) + { + [self removeTrackingArea:trackingArea]; + [trackingArea release]; + } + + const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | + NSTrackingActiveInKeyWindow | + NSTrackingEnabledDuringMouseDrag | + NSTrackingCursorUpdate | + NSTrackingInVisibleRect | + NSTrackingAssumeInside; + + trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] + options:options + owner:self + userInfo:nil]; + + [self addTrackingArea:trackingArea]; + [super updateTrackingAreas]; +} + +- (void)keyDown:(NSEvent *)event +{ + const int key = translateKey([event keyCode]); + const int mods = translateFlags([event modifierFlags]); + + _glfwInputKey(window, key, [event keyCode], GLFW_PRESS, mods); + + [self interpretKeyEvents:@[event]]; +} + +- (void)flagsChanged:(NSEvent *)event +{ + int action; + const unsigned int modifierFlags = + [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask; + const int key = translateKey([event keyCode]); + const int mods = translateFlags(modifierFlags); + const NSUInteger keyFlag = translateKeyToModifierFlag(key); + + if (keyFlag & modifierFlags) + { + if (window->keys[key] == GLFW_PRESS) + action = GLFW_RELEASE; + else + action = GLFW_PRESS; + } + else + action = GLFW_RELEASE; + + _glfwInputKey(window, key, [event keyCode], action, mods); +} + +- (void)keyUp:(NSEvent *)event +{ + const int key = translateKey([event keyCode]); + const int mods = translateFlags([event modifierFlags]); + _glfwInputKey(window, key, [event keyCode], GLFW_RELEASE, mods); +} + +- (void)scrollWheel:(NSEvent *)event +{ + double deltaX = [event scrollingDeltaX]; + double deltaY = [event scrollingDeltaY]; + + if ([event hasPreciseScrollingDeltas]) + { + deltaX *= 0.1; + deltaY *= 0.1; + } + + if (fabs(deltaX) > 0.0 || fabs(deltaY) > 0.0) + _glfwInputScroll(window, deltaX, deltaY); +} + +- (NSDragOperation)draggingEntered:(id )sender +{ + // HACK: We don't know what to say here because we don't know what the + // application wants to do with the paths + return NSDragOperationGeneric; +} + +- (BOOL)performDragOperation:(id )sender +{ + const NSRect contentRect = [window->ns.view frame]; + // NOTE: The returned location uses base 0,1 not 0,0 + const NSPoint pos = [sender draggingLocation]; + _glfwInputCursorPos(window, pos.x, contentRect.size.height - pos.y); + + NSPasteboard* pasteboard = [sender draggingPasteboard]; + NSDictionary* options = @{NSPasteboardURLReadingFileURLsOnlyKey:@YES}; + NSArray* urls = [pasteboard readObjectsForClasses:@[[NSURL class]] + options:options]; + const NSUInteger count = [urls count]; + if (count) + { + char** paths = calloc(count, sizeof(char*)); + + for (NSUInteger i = 0; i < count; i++) + paths[i] = _glfw_strdup([urls[i] fileSystemRepresentation]); + + _glfwInputDrop(window, (int) count, (const char**) paths); + + for (NSUInteger i = 0; i < count; i++) + free(paths[i]); + free(paths); + } + + return YES; +} + +- (BOOL)hasMarkedText +{ + return [markedText length] > 0; +} + +- (NSRange)markedRange +{ + if ([markedText length] > 0) + return NSMakeRange(0, [markedText length] - 1); + else + return kEmptyRange; +} + +- (NSRange)selectedRange +{ + return kEmptyRange; +} + +- (void)setMarkedText:(id)string + selectedRange:(NSRange)selectedRange + replacementRange:(NSRange)replacementRange +{ + [markedText release]; + if ([string isKindOfClass:[NSAttributedString class]]) + markedText = [[NSMutableAttributedString alloc] initWithAttributedString:string]; + else + markedText = [[NSMutableAttributedString alloc] initWithString:string]; +} + +- (void)unmarkText +{ + [[markedText mutableString] setString:@""]; +} + +- (NSArray*)validAttributesForMarkedText +{ + return [NSArray array]; +} + +- (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range + actualRange:(NSRangePointer)actualRange +{ + return nil; +} + +- (NSUInteger)characterIndexForPoint:(NSPoint)point +{ + return 0; +} + +- (NSRect)firstRectForCharacterRange:(NSRange)range + actualRange:(NSRangePointer)actualRange +{ + const NSRect frame = [window->ns.view frame]; + return NSMakeRect(frame.origin.x, frame.origin.y, 0.0, 0.0); +} + +- (void)insertText:(id)string replacementRange:(NSRange)replacementRange +{ + NSString* characters; + NSEvent* event = [NSApp currentEvent]; + const int mods = translateFlags([event modifierFlags]); + const int plain = !(mods & GLFW_MOD_SUPER); + + if ([string isKindOfClass:[NSAttributedString class]]) + characters = [string string]; + else + characters = (NSString*) string; + + const NSUInteger length = [characters length]; + for (NSUInteger i = 0; i < length; i++) + { + const unichar codepoint = [characters characterAtIndex:i]; + if ((codepoint & 0xff00) == 0xf700) + continue; + + _glfwInputChar(window, codepoint, mods, plain); + } +} + +- (void)doCommandBySelector:(SEL)selector +{ +} + +@end + + +//------------------------------------------------------------------------ +// GLFW window class +//------------------------------------------------------------------------ + +@interface GLFWWindow : NSWindow {} +@end + +@implementation GLFWWindow + +- (BOOL)canBecomeKeyWindow +{ + // Required for NSWindowStyleMaskBorderless windows + return YES; +} + +- (BOOL)canBecomeMainWindow +{ + return YES; +} + +@end + + +// Create the Cocoa window +// +static GLFWbool createNativeWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWfbconfig* fbconfig) +{ + window->ns.delegate = [[GLFWWindowDelegate alloc] initWithGlfwWindow:window]; + if (window->ns.delegate == nil) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to create window delegate"); + return GLFW_FALSE; + } + + NSRect contentRect; + + if (window->monitor) + { + GLFWvidmode mode; + int xpos, ypos; + + _glfwPlatformGetVideoMode(window->monitor, &mode); + _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); + + contentRect = NSMakeRect(xpos, ypos, mode.width, mode.height); + } + else + contentRect = NSMakeRect(0, 0, wndconfig->width, wndconfig->height); + + window->ns.object = [[GLFWWindow alloc] + initWithContentRect:contentRect + styleMask:getStyleMask(window) + backing:NSBackingStoreBuffered + defer:NO]; + + if (window->ns.object == nil) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create window"); + return GLFW_FALSE; + } + + if (window->monitor) + [window->ns.object setLevel:NSMainMenuWindowLevel + 1]; + else + { + [(NSWindow*) window->ns.object center]; + _glfw.ns.cascadePoint = + NSPointToCGPoint([window->ns.object cascadeTopLeftFromPoint: + NSPointFromCGPoint(_glfw.ns.cascadePoint)]); + + if (wndconfig->resizable) + { + const NSWindowCollectionBehavior behavior = + NSWindowCollectionBehaviorFullScreenPrimary | + NSWindowCollectionBehaviorManaged; + [window->ns.object setCollectionBehavior:behavior]; + } + + if (wndconfig->floating) + [window->ns.object setLevel:NSFloatingWindowLevel]; + + if (wndconfig->maximized) + [window->ns.object zoom:nil]; + } + + if (strlen(wndconfig->ns.frameName)) + [window->ns.object setFrameAutosaveName:@(wndconfig->ns.frameName)]; + + window->ns.view = [[GLFWContentView alloc] initWithGlfwWindow:window]; + window->ns.retina = wndconfig->ns.retina; + + if (fbconfig->transparent) + { + [window->ns.object setOpaque:NO]; + [window->ns.object setHasShadow:NO]; + [window->ns.object setBackgroundColor:[NSColor clearColor]]; + } + + [window->ns.object setContentView:window->ns.view]; + [window->ns.object makeFirstResponder:window->ns.view]; + [window->ns.object setTitle:@(wndconfig->title)]; + [window->ns.object setDelegate:window->ns.delegate]; + [window->ns.object setAcceptsMouseMovedEvents:YES]; + [window->ns.object setRestorable:NO]; + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 + if ([window->ns.object respondsToSelector:@selector(setTabbingMode:)]) + [window->ns.object setTabbingMode:NSWindowTabbingModeDisallowed]; +#endif + + _glfwPlatformGetWindowSize(window, &window->ns.width, &window->ns.height); + _glfwPlatformGetFramebufferSize(window, &window->ns.fbWidth, &window->ns.fbHeight); + + return GLFW_TRUE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Transforms a y-coordinate between the CG display and NS screen spaces +// +float _glfwTransformYNS(float y) +{ + return CGDisplayBounds(CGMainDisplayID()).size.height - y - 1; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformCreateWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + @autoreleasepool { + + if (!_glfw.ns.finishedLaunching) + [NSApp run]; + + if (!createNativeWindow(window, wndconfig, fbconfig)) + return GLFW_FALSE; + + if (ctxconfig->client != GLFW_NO_API) + { + if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) + { + if (!_glfwInitNSGL()) + return GLFW_FALSE; + if (!_glfwCreateContextNSGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) + { + if (!_glfwInitEGL()) + return GLFW_FALSE; + if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + } + + if (window->monitor) + { + _glfwPlatformShowWindow(window); + _glfwPlatformFocusWindow(window); + acquireMonitor(window); + } + + return GLFW_TRUE; + + } // autoreleasepool +} + +void _glfwPlatformDestroyWindow(_GLFWwindow* window) +{ + @autoreleasepool { + + if (_glfw.ns.disabledCursorWindow == window) + _glfw.ns.disabledCursorWindow = NULL; + + [window->ns.object orderOut:nil]; + + if (window->monitor) + releaseMonitor(window); + + if (window->context.destroy) + window->context.destroy(window); + + [window->ns.object setDelegate:nil]; + [window->ns.delegate release]; + window->ns.delegate = nil; + + [window->ns.view release]; + window->ns.view = nil; + + [window->ns.object close]; + window->ns.object = nil; + + // HACK: Allow Cocoa to catch up before returning + _glfwPlatformPollEvents(); + + } // autoreleasepool +} + +void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) +{ + @autoreleasepool { + NSString* string = @(title); + [window->ns.object setTitle:string]; + // HACK: Set the miniwindow title explicitly as setTitle: doesn't update it + // if the window lacks NSWindowStyleMaskTitled + [window->ns.object setMiniwindowTitle:string]; + } // autoreleasepool +} + +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, + int count, const GLFWimage* images) +{ + // Regular windows do not have icons +} + +void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) +{ + @autoreleasepool { + + const NSRect contentRect = + [window->ns.object contentRectForFrameRect:[window->ns.object frame]]; + + if (xpos) + *xpos = contentRect.origin.x; + if (ypos) + *ypos = _glfwTransformYNS(contentRect.origin.y + contentRect.size.height - 1); + + } // autoreleasepool +} + +void _glfwPlatformSetWindowPos(_GLFWwindow* window, int x, int y) +{ + @autoreleasepool { + + const NSRect contentRect = [window->ns.view frame]; + const NSRect dummyRect = NSMakeRect(x, _glfwTransformYNS(y + contentRect.size.height - 1), 0, 0); + const NSRect frameRect = [window->ns.object frameRectForContentRect:dummyRect]; + [window->ns.object setFrameOrigin:frameRect.origin]; + + } // autoreleasepool +} + +void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) +{ + @autoreleasepool { + + const NSRect contentRect = [window->ns.view frame]; + + if (width) + *width = contentRect.size.width; + if (height) + *height = contentRect.size.height; + + } // autoreleasepool +} + +void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) +{ + @autoreleasepool { + + if (window->monitor) + { + if (window->monitor->window == window) + acquireMonitor(window); + } + else + { + NSRect contentRect = + [window->ns.object contentRectForFrameRect:[window->ns.object frame]]; + contentRect.origin.y += contentRect.size.height - height; + contentRect.size = NSMakeSize(width, height); + [window->ns.object setFrame:[window->ns.object frameRectForContentRect:contentRect] + display:YES]; + } + + } // autoreleasepool +} + +void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, + int minwidth, int minheight, + int maxwidth, int maxheight) +{ + @autoreleasepool { + + if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) + [window->ns.object setContentMinSize:NSMakeSize(0, 0)]; + else + [window->ns.object setContentMinSize:NSMakeSize(minwidth, minheight)]; + + if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE) + [window->ns.object setContentMaxSize:NSMakeSize(DBL_MAX, DBL_MAX)]; + else + [window->ns.object setContentMaxSize:NSMakeSize(maxwidth, maxheight)]; + + } // autoreleasepool +} + +void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) +{ + @autoreleasepool { + if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE) + [window->ns.object setResizeIncrements:NSMakeSize(1.0, 1.0)]; + else + [window->ns.object setContentAspectRatio:NSMakeSize(numer, denom)]; + } // autoreleasepool +} + +void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) +{ + @autoreleasepool { + + const NSRect contentRect = [window->ns.view frame]; + const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect]; + + if (width) + *width = (int) fbRect.size.width; + if (height) + *height = (int) fbRect.size.height; + + } // autoreleasepool +} + +void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, + int* left, int* top, + int* right, int* bottom) +{ + @autoreleasepool { + + const NSRect contentRect = [window->ns.view frame]; + const NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect]; + + if (left) + *left = contentRect.origin.x - frameRect.origin.x; + if (top) + *top = frameRect.origin.y + frameRect.size.height - + contentRect.origin.y - contentRect.size.height; + if (right) + *right = frameRect.origin.x + frameRect.size.width - + contentRect.origin.x - contentRect.size.width; + if (bottom) + *bottom = contentRect.origin.y - frameRect.origin.y; + + } // autoreleasepool +} + +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + @autoreleasepool { + + 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); + + } // autoreleasepool +} + +void _glfwPlatformIconifyWindow(_GLFWwindow* window) +{ + @autoreleasepool { + [window->ns.object miniaturize:nil]; + } // autoreleasepool +} + +void _glfwPlatformRestoreWindow(_GLFWwindow* window) +{ + @autoreleasepool { + if ([window->ns.object isMiniaturized]) + [window->ns.object deminiaturize:nil]; + else if ([window->ns.object isZoomed]) + [window->ns.object zoom:nil]; + } // autoreleasepool +} + +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ + @autoreleasepool { + if (![window->ns.object isZoomed]) + [window->ns.object zoom:nil]; + } // autoreleasepool +} + +void _glfwPlatformShowWindow(_GLFWwindow* window) +{ + @autoreleasepool { + [window->ns.object orderFront:nil]; + } // autoreleasepool +} + +void _glfwPlatformHideWindow(_GLFWwindow* window) +{ + @autoreleasepool { + [window->ns.object orderOut:nil]; + } // autoreleasepool +} + +void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) +{ + @autoreleasepool { + [NSApp requestUserAttention:NSInformationalRequest]; + } // autoreleasepool +} + +void _glfwPlatformFocusWindow(_GLFWwindow* window) +{ + @autoreleasepool { + // Make us the active application + // HACK: This is here to prevent applications using only hidden windows from + // being activated, but should probably not be done every time any + // window is shown + [NSApp activateIgnoringOtherApps:YES]; + [window->ns.object makeKeyAndOrderFront:nil]; + } // autoreleasepool +} + +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) +{ + @autoreleasepool { + + if (window->monitor == monitor) + { + if (monitor) + { + if (monitor->window == window) + acquireMonitor(window); + } + else + { + const NSRect contentRect = + NSMakeRect(xpos, _glfwTransformYNS(ypos + height - 1), width, height); + const NSRect frameRect = + [window->ns.object frameRectForContentRect:contentRect + styleMask:getStyleMask(window)]; + + [window->ns.object setFrame:frameRect display:YES]; + } + + return; + } + + if (window->monitor) + releaseMonitor(window); + + _glfwInputWindowMonitor(window, monitor); + + // HACK: Allow the state cached in Cocoa to catch up to reality + // TODO: Solve this in a less terrible way + _glfwPlatformPollEvents(); + + const NSUInteger styleMask = getStyleMask(window); + [window->ns.object setStyleMask:styleMask]; + // HACK: Changing the style mask can cause the first responder to be cleared + [window->ns.object makeFirstResponder:window->ns.view]; + + if (window->monitor) + { + [window->ns.object setLevel:NSMainMenuWindowLevel + 1]; + [window->ns.object setHasShadow:NO]; + + acquireMonitor(window); + } + else + { + NSRect contentRect = NSMakeRect(xpos, _glfwTransformYNS(ypos + height - 1), + width, height); + NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect + styleMask:styleMask]; + [window->ns.object setFrame:frameRect display:YES]; + + if (window->numer != GLFW_DONT_CARE && + window->denom != GLFW_DONT_CARE) + { + [window->ns.object setContentAspectRatio:NSMakeSize(window->numer, + window->denom)]; + } + + if (window->minwidth != GLFW_DONT_CARE && + window->minheight != GLFW_DONT_CARE) + { + [window->ns.object setContentMinSize:NSMakeSize(window->minwidth, + window->minheight)]; + } + + if (window->maxwidth != GLFW_DONT_CARE && + window->maxheight != GLFW_DONT_CARE) + { + [window->ns.object setContentMaxSize:NSMakeSize(window->maxwidth, + window->maxheight)]; + } + + if (window->floating) + [window->ns.object setLevel:NSFloatingWindowLevel]; + else + [window->ns.object setLevel:NSNormalWindowLevel]; + + [window->ns.object setHasShadow:YES]; + // 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]]; + } + + } // autoreleasepool +} + +int _glfwPlatformWindowFocused(_GLFWwindow* window) +{ + @autoreleasepool { + return [window->ns.object isKeyWindow]; + } // autoreleasepool +} + +int _glfwPlatformWindowIconified(_GLFWwindow* window) +{ + @autoreleasepool { + return [window->ns.object isMiniaturized]; + } // autoreleasepool +} + +int _glfwPlatformWindowVisible(_GLFWwindow* window) +{ + @autoreleasepool { + return [window->ns.object isVisible]; + } // autoreleasepool +} + +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + @autoreleasepool { + return [window->ns.object isZoomed]; + } // autoreleasepool +} + +int _glfwPlatformWindowHovered(_GLFWwindow* window) +{ + @autoreleasepool { + + const NSPoint point = [NSEvent mouseLocation]; + + if ([NSWindow windowNumberAtPoint:point belowWindowWithWindowNumber:0] != + [window->ns.object windowNumber]) + { + return GLFW_FALSE; + } + + return NSMouseInRect(point, + [window->ns.object convertRectToScreen:[window->ns.view frame]], NO); + + } // autoreleasepool +} + +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + @autoreleasepool { + return ![window->ns.object isOpaque] && ![window->ns.view isOpaque]; + } // autoreleasepool +} + +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +{ + @autoreleasepool { + [window->ns.object setStyleMask:getStyleMask(window)]; + } // autoreleasepool +} + +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +{ + @autoreleasepool { + [window->ns.object setStyleMask:getStyleMask(window)]; + [window->ns.object makeFirstResponder:window->ns.view]; + } // autoreleasepool +} + +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +{ + @autoreleasepool { + if (enabled) + [window->ns.object setLevel:NSFloatingWindowLevel]; + else + [window->ns.object setLevel:NSNormalWindowLevel]; + } // autoreleasepool +} + +float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) +{ + @autoreleasepool { + return (float) [window->ns.object alphaValue]; + } // autoreleasepool +} + +void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) +{ + @autoreleasepool { + [window->ns.object setAlphaValue:opacity]; + } // autoreleasepool +} + +void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) +{ +} + +GLFWbool _glfwPlatformRawMouseMotionSupported(void) +{ + return GLFW_FALSE; +} + +void _glfwPlatformPollEvents(void) +{ + @autoreleasepool { + + if (!_glfw.ns.finishedLaunching) + [NSApp run]; + + for (;;) + { + NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (event == nil) + break; + + [NSApp sendEvent:event]; + } + + } // autoreleasepool +} + +void _glfwPlatformWaitEvents(void) +{ + @autoreleasepool { + + if (!_glfw.ns.finishedLaunching) + [NSApp run]; + + // I wanted to pass NO to dequeue:, and rely on PollEvents to + // dequeue and send. For reasons not at all clear to me, passing + // NO to dequeue: causes this method never to return. + NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny + untilDate:[NSDate distantFuture] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + [NSApp sendEvent:event]; + + _glfwPlatformPollEvents(); + + } // autoreleasepool +} + +void _glfwPlatformWaitEventsTimeout(double timeout) +{ + @autoreleasepool { + + if (!_glfw.ns.finishedLaunching) + [NSApp run]; + + NSDate* date = [NSDate dateWithTimeIntervalSinceNow:timeout]; + NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny + untilDate:date + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (event) + [NSApp sendEvent:event]; + + _glfwPlatformPollEvents(); + + } // autoreleasepool +} + +void _glfwPlatformPostEmptyEvent(void) +{ + @autoreleasepool { + + if (!_glfw.ns.finishedLaunching) + [NSApp run]; + + NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined + location:NSMakePoint(0, 0) + modifierFlags:0 + timestamp:0 + windowNumber:0 + context:nil + subtype:0 + data1:0 + data2:0]; + [NSApp postEvent:event atStart:YES]; + + } // autoreleasepool +} + +void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) +{ + @autoreleasepool { + + const NSRect contentRect = [window->ns.view frame]; + // NOTE: The returned location uses base 0,1 not 0,0 + const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream]; + + if (xpos) + *xpos = pos.x; + if (ypos) + *ypos = contentRect.size.height - pos.y; + + } // autoreleasepool +} + +void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) +{ + @autoreleasepool { + + updateCursorImage(window); + + const NSRect contentRect = [window->ns.view frame]; + // NOTE: The returned location uses base 0,1 not 0,0 + const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream]; + + window->ns.cursorWarpDeltaX += x - pos.x; + window->ns.cursorWarpDeltaY += y - contentRect.size.height + pos.y; + + if (window->monitor) + { + CGDisplayMoveCursorToPoint(window->monitor->ns.displayID, + CGPointMake(x, y)); + } + else + { + const NSRect localRect = NSMakeRect(x, contentRect.size.height - y - 1, 0, 0); + const NSRect globalRect = [window->ns.object convertRectToScreen:localRect]; + const NSPoint globalPoint = globalRect.origin; + + CGWarpMouseCursorPosition(CGPointMake(globalPoint.x, + _glfwTransformYNS(globalPoint.y))); + } + + } // autoreleasepool +} + +void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) +{ + @autoreleasepool { + if (_glfwPlatformWindowFocused(window)) + updateCursorMode(window); + } // autoreleasepool +} + +const char* _glfwPlatformGetScancodeName(int scancode) +{ + @autoreleasepool { + + if (scancode < 0 || scancode > 0xff || + _glfw.ns.keycodes[scancode] == GLFW_KEY_UNKNOWN) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode"); + return NULL; + } + + const int key = _glfw.ns.keycodes[scancode]; + + UInt32 deadKeyState = 0; + UniChar characters[4]; + UniCharCount characterCount = 0; + + if (UCKeyTranslate([(NSData*) _glfw.ns.unicodeData bytes], + scancode, + kUCKeyActionDisplay, + 0, + LMGetKbdType(), + kUCKeyTranslateNoDeadKeysBit, + &deadKeyState, + sizeof(characters) / sizeof(characters[0]), + &characterCount, + characters) != noErr) + { + return NULL; + } + + if (!characterCount) + return NULL; + + CFStringRef string = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, + characters, + characterCount, + kCFAllocatorNull); + CFStringGetCString(string, + _glfw.ns.keynames[key], + sizeof(_glfw.ns.keynames[key]), + kCFStringEncodingUTF8); + CFRelease(string); + + return _glfw.ns.keynames[key]; + + } // autoreleasepool +} + +int _glfwPlatformGetKeyScancode(int key) +{ + return _glfw.ns.scancodes[key]; +} + +int _glfwPlatformCreateCursor(_GLFWcursor* cursor, + const GLFWimage* image, + int xhot, int yhot) +{ + @autoreleasepool { + + NSImage* native; + NSBitmapImageRep* rep; + + rep = [[NSBitmapImageRep alloc] + initWithBitmapDataPlanes:NULL + pixelsWide:image->width + pixelsHigh:image->height + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSCalibratedRGBColorSpace + bitmapFormat:NSBitmapFormatAlphaNonpremultiplied + bytesPerRow:image->width * 4 + bitsPerPixel:32]; + + if (rep == nil) + return GLFW_FALSE; + + memcpy([rep bitmapData], image->pixels, image->width * image->height * 4); + + native = [[NSImage alloc] initWithSize:NSMakeSize(image->width, image->height)]; + [native addRepresentation:rep]; + + cursor->ns.object = [[NSCursor alloc] initWithImage:native + hotSpot:NSMakePoint(xhot, yhot)]; + + [native release]; + [rep release]; + + if (cursor->ns.object == nil) + return GLFW_FALSE; + + return GLFW_TRUE; + + } // autoreleasepool +} + +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +{ + @autoreleasepool { + + SEL cursorSelector = NULL; + + // HACK: Try to use a private message + if (shape == GLFW_RESIZE_EW_CURSOR) + cursorSelector = NSSelectorFromString(@"_windowResizeEastWestCursor"); + else if (shape == GLFW_RESIZE_NS_CURSOR) + cursorSelector = NSSelectorFromString(@"_windowResizeNorthSouthCursor"); + else if (shape == GLFW_RESIZE_NWSE_CURSOR) + cursorSelector = NSSelectorFromString(@"_windowResizeNorthWestSouthEastCursor"); + else if (shape == GLFW_RESIZE_NESW_CURSOR) + cursorSelector = NSSelectorFromString(@"_windowResizeNorthEastSouthWestCursor"); + + if (cursorSelector && [NSCursor respondsToSelector:cursorSelector]) + { + id object = [NSCursor performSelector:cursorSelector]; + if ([object isKindOfClass:[NSCursor class]]) + cursor->ns.object = object; + } + + if (!cursor->ns.object) + { + if (shape == GLFW_ARROW_CURSOR) + cursor->ns.object = [NSCursor arrowCursor]; + else if (shape == GLFW_IBEAM_CURSOR) + cursor->ns.object = [NSCursor IBeamCursor]; + else if (shape == GLFW_CROSSHAIR_CURSOR) + cursor->ns.object = [NSCursor crosshairCursor]; + else if (shape == GLFW_POINTING_HAND_CURSOR) + cursor->ns.object = [NSCursor pointingHandCursor]; + else if (shape == GLFW_RESIZE_EW_CURSOR) + cursor->ns.object = [NSCursor resizeLeftRightCursor]; + else if (shape == GLFW_RESIZE_NS_CURSOR) + cursor->ns.object = [NSCursor resizeUpDownCursor]; + else if (shape == GLFW_RESIZE_ALL_CURSOR) + cursor->ns.object = [NSCursor closedHandCursor]; + else if (shape == GLFW_NOT_ALLOWED_CURSOR) + cursor->ns.object = [NSCursor operationNotAllowedCursor]; + } + + if (!cursor->ns.object) + { + _glfwInputError(GLFW_CURSOR_UNAVAILABLE, + "Cocoa: Standard cursor shape unavailable"); + return GLFW_FALSE; + } + + [cursor->ns.object retain]; + return GLFW_TRUE; + + } // autoreleasepool +} + +void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) +{ + @autoreleasepool { + if (cursor->ns.object) + [(NSCursor*) cursor->ns.object release]; + } // autoreleasepool +} + +void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) +{ + @autoreleasepool { + if (cursorInContentArea(window)) + updateCursorImage(window); + } // autoreleasepool +} + +void _glfwPlatformSetClipboardString(const char* string) +{ + @autoreleasepool { + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + [pasteboard declareTypes:@[NSPasteboardTypeString] owner:nil]; + [pasteboard setString:@(string) forType:NSPasteboardTypeString]; + } // autoreleasepool +} + +const char* _glfwPlatformGetClipboardString(void) +{ + @autoreleasepool { + + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + + if (![[pasteboard types] containsObject:NSPasteboardTypeString]) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "Cocoa: Failed to retrieve string from pasteboard"); + return NULL; + } + + NSString* object = [pasteboard stringForType:NSPasteboardTypeString]; + if (!object) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to retrieve object from pasteboard"); + return NULL; + } + + free(_glfw.ns.clipboardString); + _glfw.ns.clipboardString = _glfw_strdup([object UTF8String]); + + return _glfw.ns.clipboardString; + + } // autoreleasepool +} + +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) +{ + if (_glfw.vk.KHR_surface && _glfw.vk.EXT_metal_surface) + { + extensions[0] = "VK_KHR_surface"; + extensions[1] = "VK_EXT_metal_surface"; + } + else if (_glfw.vk.KHR_surface && _glfw.vk.MVK_macos_surface) + { + extensions[0] = "VK_KHR_surface"; + extensions[1] = "VK_MVK_macos_surface"; + } +} + +int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily) +{ + return GLFW_TRUE; +} + +VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, + _GLFWwindow* window, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) +{ + @autoreleasepool { + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101100 + // HACK: Dynamically load Core Animation to avoid adding an extra + // dependency for the majority who don't use MoltenVK + NSBundle* bundle = [NSBundle bundleWithPath:@"/System/Library/Frameworks/QuartzCore.framework"]; + if (!bundle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to find QuartzCore.framework"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + // NOTE: Create the layer here as makeBackingLayer should not return nil + window->ns.layer = [[bundle classNamed:@"CAMetalLayer"] layer]; + if (!window->ns.layer) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to create layer for view"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + if (window->ns.retina) + [window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]]; + + [window->ns.view setLayer:window->ns.layer]; + [window->ns.view setWantsLayer:YES]; + + VkResult err; + + if (_glfw.vk.EXT_metal_surface) + { + VkMetalSurfaceCreateInfoEXT sci; + + PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT; + vkCreateMetalSurfaceEXT = (PFN_vkCreateMetalSurfaceEXT) + vkGetInstanceProcAddr(instance, "vkCreateMetalSurfaceEXT"); + if (!vkCreateMetalSurfaceEXT) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Cocoa: Vulkan instance missing VK_EXT_metal_surface extension"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + memset(&sci, 0, sizeof(sci)); + sci.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT; + sci.pLayer = window->ns.layer; + + err = vkCreateMetalSurfaceEXT(instance, &sci, allocator, surface); + } + else + { + VkMacOSSurfaceCreateInfoMVK sci; + + PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK; + vkCreateMacOSSurfaceMVK = (PFN_vkCreateMacOSSurfaceMVK) + vkGetInstanceProcAddr(instance, "vkCreateMacOSSurfaceMVK"); + if (!vkCreateMacOSSurfaceMVK) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Cocoa: Vulkan instance missing VK_MVK_macos_surface extension"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + memset(&sci, 0, sizeof(sci)); + sci.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; + sci.pView = window->ns.view; + + err = vkCreateMacOSSurfaceMVK(instance, &sci, allocator, surface); + } + + if (err) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to create Vulkan surface: %s", + _glfwGetVulkanResultString(err)); + } + + return err; +#else + return VK_ERROR_EXTENSION_NOT_PRESENT; +#endif + + } // autoreleasepool +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI id glfwGetCocoaWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(nil); + return window->ns.object; +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/context.c b/source/MaterialXGraphEditor/External/Glfw/src/context.c new file mode 100644 index 0000000000..48311e5fd4 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/context.c @@ -0,0 +1,760 @@ +//======================================================================== +// GLFW 3.4 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// Please use C89 style variable declarations in this file because VS 2010 +//======================================================================== + +#include "internal.h" + +#include +#include +#include +#include +#include + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Checks whether the desired context attributes are valid +// +// This function checks things like whether the specified client API version +// exists and whether all relevant options have supported and non-conflicting +// values +// +GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) +{ + if (ctxconfig->share) + { + if (ctxconfig->client == GLFW_NO_API || + ctxconfig->share->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return GLFW_FALSE; + } + } + + if (ctxconfig->source != GLFW_NATIVE_CONTEXT_API && + ctxconfig->source != GLFW_EGL_CONTEXT_API && + ctxconfig->source != GLFW_OSMESA_CONTEXT_API) + { + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid context creation API 0x%08X", + ctxconfig->source); + return GLFW_FALSE; + } + + if (ctxconfig->client != GLFW_NO_API && + ctxconfig->client != GLFW_OPENGL_API && + ctxconfig->client != GLFW_OPENGL_ES_API) + { + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid client API 0x%08X", + ctxconfig->client); + return GLFW_FALSE; + } + + if (ctxconfig->client == GLFW_OPENGL_API) + { + if ((ctxconfig->major < 1 || ctxconfig->minor < 0) || + (ctxconfig->major == 1 && ctxconfig->minor > 5) || + (ctxconfig->major == 2 && ctxconfig->minor > 1) || + (ctxconfig->major == 3 && ctxconfig->minor > 3)) + { + // OpenGL 1.0 is the smallest valid version + // OpenGL 1.x series ended with version 1.5 + // OpenGL 2.x series ended with version 2.1 + // OpenGL 3.x series ended with version 3.3 + // For now, let everything else through + + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid OpenGL version %i.%i", + ctxconfig->major, ctxconfig->minor); + return GLFW_FALSE; + } + + if (ctxconfig->profile) + { + if (ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE && + ctxconfig->profile != GLFW_OPENGL_COMPAT_PROFILE) + { + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid OpenGL profile 0x%08X", + ctxconfig->profile); + return GLFW_FALSE; + } + + if (ctxconfig->major <= 2 || + (ctxconfig->major == 3 && ctxconfig->minor < 2)) + { + // Desktop OpenGL context profiles are only defined for version 3.2 + // and above + + _glfwInputError(GLFW_INVALID_VALUE, + "Context profiles are only defined for OpenGL version 3.2 and above"); + return GLFW_FALSE; + } + } + + if (ctxconfig->forward && ctxconfig->major <= 2) + { + // Forward-compatible contexts are only defined for OpenGL version 3.0 and above + _glfwInputError(GLFW_INVALID_VALUE, + "Forward-compatibility is only defined for OpenGL version 3.0 and above"); + return GLFW_FALSE; + } + } + else if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + if (ctxconfig->major < 1 || ctxconfig->minor < 0 || + (ctxconfig->major == 1 && ctxconfig->minor > 1) || + (ctxconfig->major == 2 && ctxconfig->minor > 0)) + { + // OpenGL ES 1.0 is the smallest valid version + // OpenGL ES 1.x series ended with version 1.1 + // OpenGL ES 2.x series ended with version 2.0 + // For now, let everything else through + + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid OpenGL ES version %i.%i", + ctxconfig->major, ctxconfig->minor); + return GLFW_FALSE; + } + } + + if (ctxconfig->robustness) + { + if (ctxconfig->robustness != GLFW_NO_RESET_NOTIFICATION && + ctxconfig->robustness != GLFW_LOSE_CONTEXT_ON_RESET) + { + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid context robustness mode 0x%08X", + ctxconfig->robustness); + return GLFW_FALSE; + } + } + + if (ctxconfig->release) + { + if (ctxconfig->release != GLFW_RELEASE_BEHAVIOR_NONE && + ctxconfig->release != GLFW_RELEASE_BEHAVIOR_FLUSH) + { + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid context release behavior 0x%08X", + ctxconfig->release); + return GLFW_FALSE; + } + } + + return GLFW_TRUE; +} + +// Chooses the framebuffer config that best matches the desired one +// +const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired, + const _GLFWfbconfig* alternatives, + unsigned int count) +{ + unsigned int i; + unsigned int missing, leastMissing = UINT_MAX; + unsigned int colorDiff, leastColorDiff = UINT_MAX; + unsigned int extraDiff, leastExtraDiff = UINT_MAX; + const _GLFWfbconfig* current; + const _GLFWfbconfig* closest = NULL; + + for (i = 0; i < count; i++) + { + current = alternatives + i; + + if (desired->stereo > 0 && current->stereo == 0) + { + // Stereo is a hard constraint + continue; + } + + if (desired->doublebuffer != current->doublebuffer) + { + // Double buffering is a hard constraint + continue; + } + + // Count number of missing buffers + { + missing = 0; + + if (desired->alphaBits > 0 && current->alphaBits == 0) + missing++; + + if (desired->depthBits > 0 && current->depthBits == 0) + missing++; + + if (desired->stencilBits > 0 && current->stencilBits == 0) + missing++; + + if (desired->auxBuffers > 0 && + current->auxBuffers < desired->auxBuffers) + { + missing += desired->auxBuffers - current->auxBuffers; + } + + if (desired->samples > 0 && current->samples == 0) + { + // Technically, several multisampling buffers could be + // involved, but that's a lower level implementation detail and + // not important to us here, so we count them as one + missing++; + } + + if (desired->transparent != current->transparent) + missing++; + } + + // These polynomials make many small channel size differences matter + // less than one large channel size difference + + // Calculate color channel size difference value + { + colorDiff = 0; + + if (desired->redBits != GLFW_DONT_CARE) + { + colorDiff += (desired->redBits - current->redBits) * + (desired->redBits - current->redBits); + } + + if (desired->greenBits != GLFW_DONT_CARE) + { + colorDiff += (desired->greenBits - current->greenBits) * + (desired->greenBits - current->greenBits); + } + + if (desired->blueBits != GLFW_DONT_CARE) + { + colorDiff += (desired->blueBits - current->blueBits) * + (desired->blueBits - current->blueBits); + } + } + + // Calculate non-color channel size difference value + { + extraDiff = 0; + + if (desired->alphaBits != GLFW_DONT_CARE) + { + extraDiff += (desired->alphaBits - current->alphaBits) * + (desired->alphaBits - current->alphaBits); + } + + if (desired->depthBits != GLFW_DONT_CARE) + { + extraDiff += (desired->depthBits - current->depthBits) * + (desired->depthBits - current->depthBits); + } + + if (desired->stencilBits != GLFW_DONT_CARE) + { + extraDiff += (desired->stencilBits - current->stencilBits) * + (desired->stencilBits - current->stencilBits); + } + + if (desired->accumRedBits != GLFW_DONT_CARE) + { + extraDiff += (desired->accumRedBits - current->accumRedBits) * + (desired->accumRedBits - current->accumRedBits); + } + + if (desired->accumGreenBits != GLFW_DONT_CARE) + { + extraDiff += (desired->accumGreenBits - current->accumGreenBits) * + (desired->accumGreenBits - current->accumGreenBits); + } + + if (desired->accumBlueBits != GLFW_DONT_CARE) + { + extraDiff += (desired->accumBlueBits - current->accumBlueBits) * + (desired->accumBlueBits - current->accumBlueBits); + } + + if (desired->accumAlphaBits != GLFW_DONT_CARE) + { + extraDiff += (desired->accumAlphaBits - current->accumAlphaBits) * + (desired->accumAlphaBits - current->accumAlphaBits); + } + + if (desired->samples != GLFW_DONT_CARE) + { + extraDiff += (desired->samples - current->samples) * + (desired->samples - current->samples); + } + + if (desired->sRGB && !current->sRGB) + extraDiff++; + } + + // Figure out if the current one is better than the best one found so far + // Least number of missing buffers is the most important heuristic, + // then color buffer size match and lastly size match for other buffers + + if (missing < leastMissing) + closest = current; + else if (missing == leastMissing) + { + if ((colorDiff < leastColorDiff) || + (colorDiff == leastColorDiff && extraDiff < leastExtraDiff)) + { + closest = current; + } + } + + if (current == closest) + { + leastMissing = missing; + leastColorDiff = colorDiff; + leastExtraDiff = extraDiff; + } + } + + return closest; +} + +// Retrieves the attributes of the current context +// +GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig) +{ + int i; + _GLFWwindow* previous; + const char* version; + const char* prefixes[] = + { + "OpenGL ES-CM ", + "OpenGL ES-CL ", + "OpenGL ES ", + NULL + }; + + window->context.source = ctxconfig->source; + window->context.client = GLFW_OPENGL_API; + + previous = _glfwPlatformGetTls(&_glfw.contextSlot); + glfwMakeContextCurrent((GLFWwindow*) window); + + window->context.GetIntegerv = (PFNGLGETINTEGERVPROC) + window->context.getProcAddress("glGetIntegerv"); + window->context.GetString = (PFNGLGETSTRINGPROC) + window->context.getProcAddress("glGetString"); + if (!window->context.GetIntegerv || !window->context.GetString) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Entry point retrieval is broken"); + glfwMakeContextCurrent((GLFWwindow*) previous); + return GLFW_FALSE; + } + + version = (const char*) window->context.GetString(GL_VERSION); + if (!version) + { + if (ctxconfig->client == GLFW_OPENGL_API) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OpenGL version string retrieval is broken"); + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OpenGL ES version string retrieval is broken"); + } + + glfwMakeContextCurrent((GLFWwindow*) previous); + return GLFW_FALSE; + } + + for (i = 0; prefixes[i]; i++) + { + const size_t length = strlen(prefixes[i]); + + if (strncmp(version, prefixes[i], length) == 0) + { + version += length; + window->context.client = GLFW_OPENGL_ES_API; + break; + } + } + + if (!sscanf(version, "%d.%d.%d", + &window->context.major, + &window->context.minor, + &window->context.revision)) + { + if (window->context.client == GLFW_OPENGL_API) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "No version found in OpenGL version string"); + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "No version found in OpenGL ES version string"); + } + + glfwMakeContextCurrent((GLFWwindow*) previous); + return GLFW_FALSE; + } + + if (window->context.major < ctxconfig->major || + (window->context.major == ctxconfig->major && + window->context.minor < ctxconfig->minor)) + { + // The desired OpenGL version is greater than the actual version + // This only happens if the machine lacks {GLX|WGL}_ARB_create_context + // /and/ the user has requested an OpenGL version greater than 1.0 + + // For API consistency, we emulate the behavior of the + // {GLX|WGL}_ARB_create_context extension and fail here + + if (window->context.client == GLFW_OPENGL_API) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "Requested OpenGL version %i.%i, got version %i.%i", + ctxconfig->major, ctxconfig->minor, + window->context.major, window->context.minor); + } + else + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "Requested OpenGL ES version %i.%i, got version %i.%i", + ctxconfig->major, ctxconfig->minor, + window->context.major, window->context.minor); + } + + glfwMakeContextCurrent((GLFWwindow*) previous); + return GLFW_FALSE; + } + + if (window->context.major >= 3) + { + // OpenGL 3.0+ uses a different function for extension string retrieval + // We cache it here instead of in glfwExtensionSupported mostly to alert + // users as early as possible that their build may be broken + + window->context.GetStringi = (PFNGLGETSTRINGIPROC) + window->context.getProcAddress("glGetStringi"); + if (!window->context.GetStringi) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Entry point retrieval is broken"); + glfwMakeContextCurrent((GLFWwindow*) previous); + return GLFW_FALSE; + } + } + + if (window->context.client == GLFW_OPENGL_API) + { + // Read back context flags (OpenGL 3.0 and above) + if (window->context.major >= 3) + { + GLint flags; + window->context.GetIntegerv(GL_CONTEXT_FLAGS, &flags); + + if (flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) + window->context.forward = GLFW_TRUE; + + if (flags & GL_CONTEXT_FLAG_DEBUG_BIT) + window->context.debug = GLFW_TRUE; + else if (glfwExtensionSupported("GL_ARB_debug_output") && + ctxconfig->debug) + { + // HACK: This is a workaround for older drivers (pre KHR_debug) + // not setting the debug bit in the context flags for + // debug contexts + window->context.debug = GLFW_TRUE; + } + + if (flags & GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR) + window->context.noerror = GLFW_TRUE; + } + + // Read back OpenGL context profile (OpenGL 3.2 and above) + if (window->context.major >= 4 || + (window->context.major == 3 && window->context.minor >= 2)) + { + GLint mask; + window->context.GetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask); + + if (mask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) + window->context.profile = GLFW_OPENGL_COMPAT_PROFILE; + else if (mask & GL_CONTEXT_CORE_PROFILE_BIT) + window->context.profile = GLFW_OPENGL_CORE_PROFILE; + else if (glfwExtensionSupported("GL_ARB_compatibility")) + { + // HACK: This is a workaround for the compatibility profile bit + // not being set in the context flags if an OpenGL 3.2+ + // context was created without having requested a specific + // version + window->context.profile = GLFW_OPENGL_COMPAT_PROFILE; + } + } + + // Read back robustness strategy + if (glfwExtensionSupported("GL_ARB_robustness")) + { + // NOTE: We avoid using the context flags for detection, as they are + // only present from 3.0 while the extension applies from 1.1 + + GLint strategy; + window->context.GetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, + &strategy); + + if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB) + window->context.robustness = GLFW_LOSE_CONTEXT_ON_RESET; + else if (strategy == GL_NO_RESET_NOTIFICATION_ARB) + window->context.robustness = GLFW_NO_RESET_NOTIFICATION; + } + } + else + { + // Read back robustness strategy + if (glfwExtensionSupported("GL_EXT_robustness")) + { + // NOTE: The values of these constants match those of the OpenGL ARB + // one, so we can reuse them here + + GLint strategy; + window->context.GetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, + &strategy); + + if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB) + window->context.robustness = GLFW_LOSE_CONTEXT_ON_RESET; + else if (strategy == GL_NO_RESET_NOTIFICATION_ARB) + window->context.robustness = GLFW_NO_RESET_NOTIFICATION; + } + } + + if (glfwExtensionSupported("GL_KHR_context_flush_control")) + { + GLint behavior; + window->context.GetIntegerv(GL_CONTEXT_RELEASE_BEHAVIOR, &behavior); + + if (behavior == GL_NONE) + window->context.release = GLFW_RELEASE_BEHAVIOR_NONE; + else if (behavior == GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH) + window->context.release = GLFW_RELEASE_BEHAVIOR_FLUSH; + } + + // Clearing the front buffer to black to avoid garbage pixels left over from + // previous uses of our bit of VRAM + { + PFNGLCLEARPROC glClear = (PFNGLCLEARPROC) + window->context.getProcAddress("glClear"); + glClear(GL_COLOR_BUFFER_BIT); + window->context.swapBuffers(window); + } + + glfwMakeContextCurrent((GLFWwindow*) previous); + return GLFW_TRUE; +} + +// Searches an extension string for the specified extension +// +GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions) +{ + const char* start = extensions; + + for (;;) + { + const char* where; + const char* terminator; + + where = strstr(start, string); + if (!where) + return GLFW_FALSE; + + terminator = where + strlen(string); + if (where == start || *(where - 1) == ' ') + { + if (*terminator == ' ' || *terminator == '\0') + break; + } + + start = terminator; + } + + return GLFW_TRUE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW public API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFWwindow* previous = _glfwPlatformGetTls(&_glfw.contextSlot); + + _GLFW_REQUIRE_INIT(); + + if (window && window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, + "Cannot make current with a window that has no OpenGL or OpenGL ES context"); + return; + } + + if (previous) + { + if (!window || window->context.source != previous->context.source) + previous->context.makeCurrent(NULL); + } + + if (window) + window->context.makeCurrent(window); +} + +GLFWAPI GLFWwindow* glfwGetCurrentContext(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return _glfwPlatformGetTls(&_glfw.contextSlot); +} + +GLFWAPI void glfwSwapBuffers(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, + "Cannot swap buffers of a window that has no OpenGL or OpenGL ES context"); + return; + } + + window->context.swapBuffers(window); +} + +GLFWAPI void glfwSwapInterval(int interval) +{ + _GLFWwindow* window; + + _GLFW_REQUIRE_INIT(); + + window = _glfwPlatformGetTls(&_glfw.contextSlot); + if (!window) + { + _glfwInputError(GLFW_NO_CURRENT_CONTEXT, + "Cannot set swap interval without a current OpenGL or OpenGL ES context"); + return; + } + + window->context.swapInterval(interval); +} + +GLFWAPI int glfwExtensionSupported(const char* extension) +{ + _GLFWwindow* window; + assert(extension != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + window = _glfwPlatformGetTls(&_glfw.contextSlot); + if (!window) + { + _glfwInputError(GLFW_NO_CURRENT_CONTEXT, + "Cannot query extension without a current OpenGL or OpenGL ES context"); + return GLFW_FALSE; + } + + if (*extension == '\0') + { + _glfwInputError(GLFW_INVALID_VALUE, "Extension name cannot be an empty string"); + return GLFW_FALSE; + } + + if (window->context.major >= 3) + { + int i; + GLint count; + + // Check if extension is in the modern OpenGL extensions string list + + window->context.GetIntegerv(GL_NUM_EXTENSIONS, &count); + + for (i = 0; i < count; i++) + { + const char* en = (const char*) + window->context.GetStringi(GL_EXTENSIONS, i); + if (!en) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Extension string retrieval is broken"); + return GLFW_FALSE; + } + + if (strcmp(en, extension) == 0) + return GLFW_TRUE; + } + } + else + { + // Check if extension is in the old style OpenGL extensions string + + const char* extensions = (const char*) + window->context.GetString(GL_EXTENSIONS); + if (!extensions) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Extension string retrieval is broken"); + return GLFW_FALSE; + } + + if (_glfwStringInExtensionString(extension, extensions)) + return GLFW_TRUE; + } + + // Check if extension is in the platform-specific string + return window->context.extensionSupported(extension); +} + +GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname) +{ + _GLFWwindow* window; + assert(procname != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + window = _glfwPlatformGetTls(&_glfw.contextSlot); + if (!window) + { + _glfwInputError(GLFW_NO_CURRENT_CONTEXT, + "Cannot query entry point without a current OpenGL or OpenGL ES context"); + return NULL; + } + + return window->context.getProcAddress(procname); +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/egl_context.c b/source/MaterialXGraphEditor/External/Glfw/src/egl_context.c new file mode 100644 index 0000000000..706a79224a --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/egl_context.c @@ -0,0 +1,789 @@ +//======================================================================== +// GLFW 3.4 EGL - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// Please use C89 style variable declarations in this file because VS 2010 +//======================================================================== + +#include "internal.h" + +#include +#include +#include +#include + + +// Return a description of the specified EGL error +// +static const char* getEGLErrorString(EGLint error) +{ + switch (error) + { + case EGL_SUCCESS: + return "Success"; + case EGL_NOT_INITIALIZED: + return "EGL is not or could not be initialized"; + case EGL_BAD_ACCESS: + return "EGL cannot access a requested resource"; + case EGL_BAD_ALLOC: + return "EGL failed to allocate resources for the requested operation"; + case EGL_BAD_ATTRIBUTE: + return "An unrecognized attribute or attribute value was passed in the attribute list"; + case EGL_BAD_CONTEXT: + return "An EGLContext argument does not name a valid EGL rendering context"; + case EGL_BAD_CONFIG: + return "An EGLConfig argument does not name a valid EGL frame buffer configuration"; + case EGL_BAD_CURRENT_SURFACE: + return "The current surface of the calling thread is a window, pixel buffer or pixmap that is no longer valid"; + case EGL_BAD_DISPLAY: + return "An EGLDisplay argument does not name a valid EGL display connection"; + case EGL_BAD_SURFACE: + return "An EGLSurface argument does not name a valid surface configured for GL rendering"; + case EGL_BAD_MATCH: + return "Arguments are inconsistent"; + case EGL_BAD_PARAMETER: + return "One or more argument values are invalid"; + case EGL_BAD_NATIVE_PIXMAP: + return "A NativePixmapType argument does not refer to a valid native pixmap"; + case EGL_BAD_NATIVE_WINDOW: + return "A NativeWindowType argument does not refer to a valid native window"; + case EGL_CONTEXT_LOST: + return "The application must destroy all contexts and reinitialise"; + default: + return "ERROR: UNKNOWN EGL ERROR"; + } +} + +// Returns the specified attribute of the specified EGLConfig +// +static int getEGLConfigAttrib(EGLConfig config, int attrib) +{ + int value; + eglGetConfigAttrib(_glfw.egl.display, config, attrib, &value); + return value; +} + +// Return the EGLConfig most closely matching the specified hints +// +static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* desired, + EGLConfig* result) +{ + EGLConfig* nativeConfigs; + _GLFWfbconfig* usableConfigs; + const _GLFWfbconfig* closest; + int i, nativeCount, usableCount; + + eglGetConfigs(_glfw.egl.display, NULL, 0, &nativeCount); + if (!nativeCount) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: No EGLConfigs returned"); + return GLFW_FALSE; + } + + nativeConfigs = calloc(nativeCount, sizeof(EGLConfig)); + eglGetConfigs(_glfw.egl.display, nativeConfigs, nativeCount, &nativeCount); + + usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); + usableCount = 0; + + for (i = 0; i < nativeCount; i++) + { + const EGLConfig n = nativeConfigs[i]; + _GLFWfbconfig* u = usableConfigs + usableCount; + + // Only consider RGB(A) EGLConfigs + if (getEGLConfigAttrib(n, EGL_COLOR_BUFFER_TYPE) != EGL_RGB_BUFFER) + continue; + + // Only consider window EGLConfigs + if (!(getEGLConfigAttrib(n, EGL_SURFACE_TYPE) & EGL_WINDOW_BIT)) + continue; + +#if defined(_GLFW_X11) + { + XVisualInfo vi = {0}; + + // Only consider EGLConfigs with associated Visuals + vi.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID); + if (!vi.visualid) + continue; + + if (desired->transparent) + { + int count; + XVisualInfo* vis = + XGetVisualInfo(_glfw.x11.display, VisualIDMask, &vi, &count); + if (vis) + { + u->transparent = _glfwIsVisualTransparentX11(vis[0].visual); + XFree(vis); + } + } + } +#endif // _GLFW_X11 + + if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + if (ctxconfig->major == 1) + { + if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES_BIT)) + continue; + } + else + { + if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES2_BIT)) + continue; + } + } + else if (ctxconfig->client == GLFW_OPENGL_API) + { + if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_BIT)) + continue; + } + + u->redBits = getEGLConfigAttrib(n, EGL_RED_SIZE); + u->greenBits = getEGLConfigAttrib(n, EGL_GREEN_SIZE); + u->blueBits = getEGLConfigAttrib(n, EGL_BLUE_SIZE); + + u->alphaBits = getEGLConfigAttrib(n, EGL_ALPHA_SIZE); + u->depthBits = getEGLConfigAttrib(n, EGL_DEPTH_SIZE); + u->stencilBits = getEGLConfigAttrib(n, EGL_STENCIL_SIZE); + + u->samples = getEGLConfigAttrib(n, EGL_SAMPLES); + u->doublebuffer = GLFW_TRUE; + + u->handle = (uintptr_t) n; + usableCount++; + } + + closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); + if (closest) + *result = (EGLConfig) closest->handle; + + free(nativeConfigs); + free(usableConfigs); + + return closest != NULL; +} + +static void makeContextCurrentEGL(_GLFWwindow* window) +{ + if (window) + { + if (!eglMakeCurrent(_glfw.egl.display, + window->context.egl.surface, + window->context.egl.surface, + window->context.egl.handle)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "EGL: Failed to make context current: %s", + getEGLErrorString(eglGetError())); + return; + } + } + else + { + if (!eglMakeCurrent(_glfw.egl.display, + EGL_NO_SURFACE, + EGL_NO_SURFACE, + EGL_NO_CONTEXT)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "EGL: Failed to clear current context: %s", + getEGLErrorString(eglGetError())); + return; + } + } + + _glfwPlatformSetTls(&_glfw.contextSlot, window); +} + +static void swapBuffersEGL(_GLFWwindow* window) +{ + if (window != _glfwPlatformGetTls(&_glfw.contextSlot)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "EGL: The context must be current on the calling thread when swapping buffers"); + return; + } + + eglSwapBuffers(_glfw.egl.display, window->context.egl.surface); +} + +static void swapIntervalEGL(int interval) +{ + eglSwapInterval(_glfw.egl.display, interval); +} + +static int extensionSupportedEGL(const char* extension) +{ + const char* extensions = eglQueryString(_glfw.egl.display, EGL_EXTENSIONS); + if (extensions) + { + if (_glfwStringInExtensionString(extension, extensions)) + return GLFW_TRUE; + } + + return GLFW_FALSE; +} + +static GLFWglproc getProcAddressEGL(const char* procname) +{ + _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); + + if (window->context.egl.client) + { + GLFWglproc proc = (GLFWglproc) _glfw_dlsym(window->context.egl.client, + procname); + if (proc) + return proc; + } + + return eglGetProcAddress(procname); +} + +static void destroyContextEGL(_GLFWwindow* window) +{ +#if defined(_GLFW_X11) + // NOTE: Do not unload libGL.so.1 while the X11 display is still open, + // as it will make XCloseDisplay segfault + if (window->context.client != GLFW_OPENGL_API) +#endif // _GLFW_X11 + { + if (window->context.egl.client) + { + _glfw_dlclose(window->context.egl.client); + window->context.egl.client = NULL; + } + } + + if (window->context.egl.surface) + { + eglDestroySurface(_glfw.egl.display, window->context.egl.surface); + window->context.egl.surface = EGL_NO_SURFACE; + } + + if (window->context.egl.handle) + { + eglDestroyContext(_glfw.egl.display, window->context.egl.handle); + window->context.egl.handle = EGL_NO_CONTEXT; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialize EGL +// +GLFWbool _glfwInitEGL(void) +{ + int i; + const char* sonames[] = + { +#if defined(_GLFW_EGL_LIBRARY) + _GLFW_EGL_LIBRARY, +#elif defined(_GLFW_WIN32) + "libEGL.dll", + "EGL.dll", +#elif defined(_GLFW_COCOA) + "libEGL.dylib", +#elif defined(__CYGWIN__) + "libEGL-1.so", +#else + "libEGL.so.1", +#endif + NULL + }; + + if (_glfw.egl.handle) + return GLFW_TRUE; + + for (i = 0; sonames[i]; i++) + { + _glfw.egl.handle = _glfw_dlopen(sonames[i]); + if (_glfw.egl.handle) + break; + } + + if (!_glfw.egl.handle) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: Library not found"); + return GLFW_FALSE; + } + + _glfw.egl.prefix = (strncmp(sonames[i], "lib", 3) == 0); + + _glfw.egl.GetConfigAttrib = (PFN_eglGetConfigAttrib) + _glfw_dlsym(_glfw.egl.handle, "eglGetConfigAttrib"); + _glfw.egl.GetConfigs = (PFN_eglGetConfigs) + _glfw_dlsym(_glfw.egl.handle, "eglGetConfigs"); + _glfw.egl.GetDisplay = (PFN_eglGetDisplay) + _glfw_dlsym(_glfw.egl.handle, "eglGetDisplay"); + _glfw.egl.GetError = (PFN_eglGetError) + _glfw_dlsym(_glfw.egl.handle, "eglGetError"); + _glfw.egl.Initialize = (PFN_eglInitialize) + _glfw_dlsym(_glfw.egl.handle, "eglInitialize"); + _glfw.egl.Terminate = (PFN_eglTerminate) + _glfw_dlsym(_glfw.egl.handle, "eglTerminate"); + _glfw.egl.BindAPI = (PFN_eglBindAPI) + _glfw_dlsym(_glfw.egl.handle, "eglBindAPI"); + _glfw.egl.CreateContext = (PFN_eglCreateContext) + _glfw_dlsym(_glfw.egl.handle, "eglCreateContext"); + _glfw.egl.DestroySurface = (PFN_eglDestroySurface) + _glfw_dlsym(_glfw.egl.handle, "eglDestroySurface"); + _glfw.egl.DestroyContext = (PFN_eglDestroyContext) + _glfw_dlsym(_glfw.egl.handle, "eglDestroyContext"); + _glfw.egl.CreateWindowSurface = (PFN_eglCreateWindowSurface) + _glfw_dlsym(_glfw.egl.handle, "eglCreateWindowSurface"); + _glfw.egl.MakeCurrent = (PFN_eglMakeCurrent) + _glfw_dlsym(_glfw.egl.handle, "eglMakeCurrent"); + _glfw.egl.SwapBuffers = (PFN_eglSwapBuffers) + _glfw_dlsym(_glfw.egl.handle, "eglSwapBuffers"); + _glfw.egl.SwapInterval = (PFN_eglSwapInterval) + _glfw_dlsym(_glfw.egl.handle, "eglSwapInterval"); + _glfw.egl.QueryString = (PFN_eglQueryString) + _glfw_dlsym(_glfw.egl.handle, "eglQueryString"); + _glfw.egl.GetProcAddress = (PFN_eglGetProcAddress) + _glfw_dlsym(_glfw.egl.handle, "eglGetProcAddress"); + + if (!_glfw.egl.GetConfigAttrib || + !_glfw.egl.GetConfigs || + !_glfw.egl.GetDisplay || + !_glfw.egl.GetError || + !_glfw.egl.Initialize || + !_glfw.egl.Terminate || + !_glfw.egl.BindAPI || + !_glfw.egl.CreateContext || + !_glfw.egl.DestroySurface || + !_glfw.egl.DestroyContext || + !_glfw.egl.CreateWindowSurface || + !_glfw.egl.MakeCurrent || + !_glfw.egl.SwapBuffers || + !_glfw.egl.SwapInterval || + !_glfw.egl.QueryString || + !_glfw.egl.GetProcAddress) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "EGL: Failed to load required entry points"); + + _glfwTerminateEGL(); + return GLFW_FALSE; + } + + _glfw.egl.display = eglGetDisplay(_GLFW_EGL_NATIVE_DISPLAY); + if (_glfw.egl.display == EGL_NO_DISPLAY) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "EGL: Failed to get EGL display: %s", + getEGLErrorString(eglGetError())); + + _glfwTerminateEGL(); + return GLFW_FALSE; + } + + if (!eglInitialize(_glfw.egl.display, &_glfw.egl.major, &_glfw.egl.minor)) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "EGL: Failed to initialize EGL: %s", + getEGLErrorString(eglGetError())); + + _glfwTerminateEGL(); + return GLFW_FALSE; + } + + _glfw.egl.KHR_create_context = + extensionSupportedEGL("EGL_KHR_create_context"); + _glfw.egl.KHR_create_context_no_error = + extensionSupportedEGL("EGL_KHR_create_context_no_error"); + _glfw.egl.KHR_gl_colorspace = + extensionSupportedEGL("EGL_KHR_gl_colorspace"); + _glfw.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; +} + +// Terminate EGL +// +void _glfwTerminateEGL(void) +{ + if (_glfw.egl.display) + { + eglTerminate(_glfw.egl.display); + _glfw.egl.display = EGL_NO_DISPLAY; + } + + if (_glfw.egl.handle) + { + _glfw_dlclose(_glfw.egl.handle); + _glfw.egl.handle = NULL; + } +} + +#define setAttrib(a, v) \ +{ \ + assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ + attribs[index++] = a; \ + attribs[index++] = v; \ +} + +// Create the OpenGL or OpenGL ES context +// +GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + EGLint attribs[40]; + EGLConfig config; + EGLContext share = NULL; + int index = 0; + + if (!_glfw.egl.display) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: API not available"); + return GLFW_FALSE; + } + + if (ctxconfig->share) + share = ctxconfig->share->context.egl.handle; + + if (!chooseEGLConfig(ctxconfig, fbconfig, &config)) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "EGL: Failed to find a suitable EGLConfig"); + return GLFW_FALSE; + } + + if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + if (!eglBindAPI(EGL_OPENGL_ES_API)) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "EGL: Failed to bind OpenGL ES: %s", + getEGLErrorString(eglGetError())); + return GLFW_FALSE; + } + } + else + { + if (!eglBindAPI(EGL_OPENGL_API)) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "EGL: Failed to bind OpenGL: %s", + getEGLErrorString(eglGetError())); + return GLFW_FALSE; + } + } + + if (_glfw.egl.KHR_create_context) + { + int mask = 0, flags = 0; + + if (ctxconfig->client == GLFW_OPENGL_API) + { + if (ctxconfig->forward) + flags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR; + + if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) + mask |= EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR; + else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) + mask |= EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR; + } + + if (ctxconfig->debug) + flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR; + + if (ctxconfig->robustness) + { + if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) + { + setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, + EGL_NO_RESET_NOTIFICATION_KHR); + } + else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) + { + setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, + EGL_LOSE_CONTEXT_ON_RESET_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) + { + setAttrib(EGL_CONTEXT_MAJOR_VERSION_KHR, ctxconfig->major); + setAttrib(EGL_CONTEXT_MINOR_VERSION_KHR, ctxconfig->minor); + } + + if (mask) + setAttrib(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, mask); + + if (flags) + setAttrib(EGL_CONTEXT_FLAGS_KHR, flags); + } + else + { + if (ctxconfig->client == GLFW_OPENGL_ES_API) + setAttrib(EGL_CONTEXT_CLIENT_VERSION, ctxconfig->major); + } + + if (_glfw.egl.KHR_context_flush_control) + { + 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, + config, share, attribs); + + if (window->context.egl.handle == EGL_NO_CONTEXT) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "EGL: Failed to create context: %s", + getEGLErrorString(eglGetError())); + return GLFW_FALSE; + } + + // Set up attributes for surface creation + { + int index = 0; + + if (fbconfig->sRGB) + { + if (_glfw.egl.KHR_gl_colorspace) + setAttrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR); + } + + setAttrib(EGL_NONE, EGL_NONE); + } + + window->context.egl.surface = + eglCreateWindowSurface(_glfw.egl.display, + config, + _GLFW_EGL_NATIVE_WINDOW, + attribs); + if (window->context.egl.surface == EGL_NO_SURFACE) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "EGL: Failed to create window surface: %s", + getEGLErrorString(eglGetError())); + return GLFW_FALSE; + } + + window->context.egl.config = config; + + // Load the appropriate client library + if (!_glfw.egl.KHR_get_all_proc_addresses) + { + int i; + const char** sonames; + const char* es1sonames[] = + { +#if defined(_GLFW_GLESV1_LIBRARY) + _GLFW_GLESV1_LIBRARY, +#elif defined(_GLFW_WIN32) + "GLESv1_CM.dll", + "libGLES_CM.dll", +#elif defined(_GLFW_COCOA) + "libGLESv1_CM.dylib", +#else + "libGLESv1_CM.so.1", + "libGLES_CM.so.1", +#endif + NULL + }; + const char* es2sonames[] = + { +#if defined(_GLFW_GLESV2_LIBRARY) + _GLFW_GLESV2_LIBRARY, +#elif defined(_GLFW_WIN32) + "GLESv2.dll", + "libGLESv2.dll", +#elif defined(_GLFW_COCOA) + "libGLESv2.dylib", +#elif defined(__CYGWIN__) + "libGLESv2-2.so", +#else + "libGLESv2.so.2", +#endif + NULL + }; + const char* glsonames[] = + { +#if defined(_GLFW_OPENGL_LIBRARY) + _GLFW_OPENGL_LIBRARY, +#elif defined(_GLFW_WIN32) +#elif defined(_GLFW_COCOA) +#else + "libGL.so.1", +#endif + NULL + }; + + if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + if (ctxconfig->major == 1) + sonames = es1sonames; + else + sonames = es2sonames; + } + else + sonames = glsonames; + + for (i = 0; sonames[i]; i++) + { + // HACK: Match presence of lib prefix to increase chance of finding + // a matching pair in the jungle that is Win32 EGL/GLES + if (_glfw.egl.prefix != (strncmp(sonames[i], "lib", 3) == 0)) + continue; + + window->context.egl.client = _glfw_dlopen(sonames[i]); + if (window->context.egl.client) + break; + } + + if (!window->context.egl.client) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "EGL: Failed to load client library"); + return GLFW_FALSE; + } + } + + window->context.makeCurrent = makeContextCurrentEGL; + window->context.swapBuffers = swapBuffersEGL; + window->context.swapInterval = swapIntervalEGL; + window->context.extensionSupported = extensionSupportedEGL; + window->context.getProcAddress = getProcAddressEGL; + window->context.destroy = destroyContextEGL; + + return GLFW_TRUE; +} + +#undef setAttrib + +// Returns the Visual and depth of the chosen EGLConfig +// +#if defined(_GLFW_X11) +GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig, + Visual** visual, int* depth) +{ + XVisualInfo* result; + XVisualInfo desired; + EGLConfig native; + EGLint visualID = 0, count = 0; + const long vimask = VisualScreenMask | VisualIDMask; + + if (!chooseEGLConfig(ctxconfig, fbconfig, &native)) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "EGL: Failed to find a suitable EGLConfig"); + return GLFW_FALSE; + } + + eglGetConfigAttrib(_glfw.egl.display, native, + EGL_NATIVE_VISUAL_ID, &visualID); + + desired.screen = _glfw.x11.screen; + desired.visualid = visualID; + + result = XGetVisualInfo(_glfw.x11.display, vimask, &desired, &count); + if (!result) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "EGL: Failed to retrieve Visual for EGLConfig"); + return GLFW_FALSE; + } + + *visual = result->visual; + *depth = result->depth; + + XFree(result); + return GLFW_TRUE; +} +#endif // _GLFW_X11 + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI EGLDisplay glfwGetEGLDisplay(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_DISPLAY); + return _glfw.egl.display; +} + +GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_CONTEXT); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return EGL_NO_CONTEXT; + } + + return window->context.egl.handle; +} + +GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_SURFACE); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return EGL_NO_SURFACE; + } + + return window->context.egl.surface; +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/egl_context.h b/source/MaterialXGraphEditor/External/Glfw/src/egl_context.h new file mode 100644 index 0000000000..8bfb7db6a7 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/egl_context.h @@ -0,0 +1,215 @@ +//======================================================================== +// GLFW 3.4 EGL - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#if defined(_GLFW_USE_EGLPLATFORM_H) + #include +#elif defined(_GLFW_WIN32) + #define EGLAPIENTRY __stdcall +typedef HDC EGLNativeDisplayType; +typedef HWND EGLNativeWindowType; +#elif defined(_GLFW_COCOA) + #define EGLAPIENTRY +typedef void* EGLNativeDisplayType; +typedef id EGLNativeWindowType; +#elif defined(_GLFW_X11) + #define EGLAPIENTRY +typedef Display* EGLNativeDisplayType; +typedef Window EGLNativeWindowType; +#elif defined(_GLFW_WAYLAND) + #define EGLAPIENTRY +typedef struct wl_display* EGLNativeDisplayType; +typedef struct wl_egl_window* EGLNativeWindowType; +#else + #error "No supported EGL platform selected" +#endif + +#define EGL_SUCCESS 0x3000 +#define EGL_NOT_INITIALIZED 0x3001 +#define EGL_BAD_ACCESS 0x3002 +#define EGL_BAD_ALLOC 0x3003 +#define EGL_BAD_ATTRIBUTE 0x3004 +#define EGL_BAD_CONFIG 0x3005 +#define EGL_BAD_CONTEXT 0x3006 +#define EGL_BAD_CURRENT_SURFACE 0x3007 +#define EGL_BAD_DISPLAY 0x3008 +#define EGL_BAD_MATCH 0x3009 +#define EGL_BAD_NATIVE_PIXMAP 0x300a +#define EGL_BAD_NATIVE_WINDOW 0x300b +#define EGL_BAD_PARAMETER 0x300c +#define EGL_BAD_SURFACE 0x300d +#define EGL_CONTEXT_LOST 0x300e +#define EGL_COLOR_BUFFER_TYPE 0x303f +#define EGL_RGB_BUFFER 0x308e +#define EGL_SURFACE_TYPE 0x3033 +#define EGL_WINDOW_BIT 0x0004 +#define EGL_RENDERABLE_TYPE 0x3040 +#define EGL_OPENGL_ES_BIT 0x0001 +#define EGL_OPENGL_ES2_BIT 0x0004 +#define EGL_OPENGL_BIT 0x0008 +#define EGL_ALPHA_SIZE 0x3021 +#define EGL_BLUE_SIZE 0x3022 +#define EGL_GREEN_SIZE 0x3023 +#define EGL_RED_SIZE 0x3024 +#define EGL_DEPTH_SIZE 0x3025 +#define EGL_STENCIL_SIZE 0x3026 +#define EGL_SAMPLES 0x3031 +#define EGL_OPENGL_ES_API 0x30a0 +#define EGL_OPENGL_API 0x30a2 +#define EGL_NONE 0x3038 +#define EGL_EXTENSIONS 0x3055 +#define EGL_CONTEXT_CLIENT_VERSION 0x3098 +#define EGL_NATIVE_VISUAL_ID 0x302e +#define EGL_NO_SURFACE ((EGLSurface) 0) +#define EGL_NO_DISPLAY ((EGLDisplay) 0) +#define EGL_NO_CONTEXT ((EGLContext) 0) +#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType) 0) + +#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002 +#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001 +#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002 +#define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001 +#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31bd +#define EGL_NO_RESET_NOTIFICATION_KHR 0x31be +#define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31bf +#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004 +#define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098 +#define EGL_CONTEXT_MINOR_VERSION_KHR 0x30fb +#define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30fd +#define EGL_CONTEXT_FLAGS_KHR 0x30fc +#define EGL_CONTEXT_OPENGL_NO_ERROR_KHR 0x31b3 +#define EGL_GL_COLORSPACE_KHR 0x309d +#define EGL_GL_COLORSPACE_SRGB_KHR 0x3089 +#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 unsigned int EGLBoolean; +typedef unsigned int EGLenum; +typedef void* EGLConfig; +typedef void* EGLContext; +typedef void* EGLDisplay; +typedef void* EGLSurface; + +// EGL function pointer typedefs +typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigAttrib)(EGLDisplay,EGLConfig,EGLint,EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigs)(EGLDisplay,EGLConfig*,EGLint,EGLint*); +typedef EGLDisplay (EGLAPIENTRY * PFN_eglGetDisplay)(EGLNativeDisplayType); +typedef EGLint (EGLAPIENTRY * PFN_eglGetError)(void); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglInitialize)(EGLDisplay,EGLint*,EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglTerminate)(EGLDisplay); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglBindAPI)(EGLenum); +typedef EGLContext (EGLAPIENTRY * PFN_eglCreateContext)(EGLDisplay,EGLConfig,EGLContext,const EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroySurface)(EGLDisplay,EGLSurface); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroyContext)(EGLDisplay,EGLContext); +typedef EGLSurface (EGLAPIENTRY * PFN_eglCreateWindowSurface)(EGLDisplay,EGLConfig,EGLNativeWindowType,const EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglMakeCurrent)(EGLDisplay,EGLSurface,EGLSurface,EGLContext); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapBuffers)(EGLDisplay,EGLSurface); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapInterval)(EGLDisplay,EGLint); +typedef const char* (EGLAPIENTRY * PFN_eglQueryString)(EGLDisplay,EGLint); +typedef GLFWglproc (EGLAPIENTRY * PFN_eglGetProcAddress)(const char*); +#define eglGetConfigAttrib _glfw.egl.GetConfigAttrib +#define eglGetConfigs _glfw.egl.GetConfigs +#define eglGetDisplay _glfw.egl.GetDisplay +#define eglGetError _glfw.egl.GetError +#define eglInitialize _glfw.egl.Initialize +#define eglTerminate _glfw.egl.Terminate +#define eglBindAPI _glfw.egl.BindAPI +#define eglCreateContext _glfw.egl.CreateContext +#define eglDestroySurface _glfw.egl.DestroySurface +#define eglDestroyContext _glfw.egl.DestroyContext +#define eglCreateWindowSurface _glfw.egl.CreateWindowSurface +#define eglMakeCurrent _glfw.egl.MakeCurrent +#define eglSwapBuffers _glfw.egl.SwapBuffers +#define eglSwapInterval _glfw.egl.SwapInterval +#define eglQueryString _glfw.egl.QueryString +#define eglGetProcAddress _glfw.egl.GetProcAddress + +#define _GLFW_EGL_CONTEXT_STATE _GLFWcontextEGL egl +#define _GLFW_EGL_LIBRARY_CONTEXT_STATE _GLFWlibraryEGL egl + + +// EGL-specific per-context data +// +typedef struct _GLFWcontextEGL +{ + EGLConfig config; + EGLContext handle; + EGLSurface surface; + + void* client; + +} _GLFWcontextEGL; + +// EGL-specific global data +// +typedef struct _GLFWlibraryEGL +{ + EGLDisplay display; + EGLint major, minor; + GLFWbool prefix; + + GLFWbool KHR_create_context; + GLFWbool KHR_create_context_no_error; + GLFWbool KHR_gl_colorspace; + GLFWbool KHR_get_all_proc_addresses; + GLFWbool KHR_context_flush_control; + + void* handle; + + PFN_eglGetConfigAttrib GetConfigAttrib; + PFN_eglGetConfigs GetConfigs; + PFN_eglGetDisplay GetDisplay; + PFN_eglGetError GetError; + PFN_eglInitialize Initialize; + PFN_eglTerminate Terminate; + PFN_eglBindAPI BindAPI; + PFN_eglCreateContext CreateContext; + PFN_eglDestroySurface DestroySurface; + PFN_eglDestroyContext DestroyContext; + PFN_eglCreateWindowSurface CreateWindowSurface; + PFN_eglMakeCurrent MakeCurrent; + PFN_eglSwapBuffers SwapBuffers; + PFN_eglSwapInterval SwapInterval; + PFN_eglQueryString QueryString; + PFN_eglGetProcAddress GetProcAddress; + +} _GLFWlibraryEGL; + + +GLFWbool _glfwInitEGL(void); +void _glfwTerminateEGL(void); +GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig); +#if defined(_GLFW_X11) +GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig, + Visual** visual, int* depth); +#endif /*_GLFW_X11*/ + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/glfw.rc.in b/source/MaterialXGraphEditor/External/Glfw/src/glfw.rc.in new file mode 100644 index 0000000000..ac3460a7ca --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/glfw.rc.in @@ -0,0 +1,30 @@ + +#include + +VS_VERSION_INFO VERSIONINFO +FILEVERSION @GLFW_VERSION_MAJOR@,@GLFW_VERSION_MINOR@,@GLFW_VERSION_PATCH@,0 +PRODUCTVERSION @GLFW_VERSION_MAJOR@,@GLFW_VERSION_MINOR@,@GLFW_VERSION_PATCH@,0 +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +FILEFLAGS 0 +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE 0 +{ + BLOCK "StringFileInfo" + { + BLOCK "040904B0" + { + VALUE "CompanyName", "GLFW" + VALUE "FileDescription", "GLFW @GLFW_VERSION@ DLL" + VALUE "FileVersion", "@GLFW_VERSION@" + VALUE "OriginalFilename", "glfw3.dll" + VALUE "ProductName", "GLFW" + VALUE "ProductVersion", "@GLFW_VERSION@" + } + } + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x409, 1200 + } +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/glfw3.pc.in b/source/MaterialXGraphEditor/External/Glfw/src/glfw3.pc.in new file mode 100644 index 0000000000..f74298d4b7 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/glfw3.pc.in @@ -0,0 +1,13 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ + +Name: GLFW +Description: A multi-platform library for OpenGL, window and input +Version: @GLFW_VERSION@ +URL: https://www.glfw.org/ +Requires.private: @GLFW_PKG_DEPS@ +Libs: -L${libdir} -l@GLFW_LIB_NAME@ +Libs.private: @GLFW_PKG_LIBS@ +Cflags: -I${includedir} diff --git a/source/MaterialXGraphEditor/External/Glfw/src/glfw3Config.cmake.in b/source/MaterialXGraphEditor/External/Glfw/src/glfw3Config.cmake.in new file mode 100644 index 0000000000..4a13a88b9e --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/glfw3Config.cmake.in @@ -0,0 +1,3 @@ +include(CMakeFindDependencyMacro) +find_dependency(Threads) +include("${CMAKE_CURRENT_LIST_DIR}/glfw3Targets.cmake") diff --git a/source/MaterialXGraphEditor/External/Glfw/src/glfw_config.h.in b/source/MaterialXGraphEditor/External/Glfw/src/glfw_config.h.in new file mode 100644 index 0000000000..f4876da285 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/glfw_config.h.in @@ -0,0 +1,58 @@ +//======================================================================== +// GLFW 3.4 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2010-2016 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// As glfw_config.h.in, this file is used by CMake to produce the +// glfw_config.h configuration header file. If you are adding a feature +// requiring conditional compilation, this is where to add the macro. +//======================================================================== +// As glfw_config.h, this file defines compile-time option macros for a +// specific platform and development environment. If you are using the +// GLFW CMake files, modify glfw_config.h.in instead of this file. If you +// are using your own build system, make this file define the appropriate +// macros in whatever way is suitable. +//======================================================================== + +// Define this to 1 if building GLFW for X11 +#cmakedefine _GLFW_X11 +// Define this to 1 if building GLFW for Win32 +#cmakedefine _GLFW_WIN32 +// Define this to 1 if building GLFW for Cocoa +#cmakedefine _GLFW_COCOA +// Define this to 1 if building GLFW for Wayland +#cmakedefine _GLFW_WAYLAND +// Define this to 1 if building GLFW for OSMesa +#cmakedefine _GLFW_OSMESA + +// Define this to 1 to use Vulkan loader linked statically into application +#cmakedefine _GLFW_VULKAN_STATIC + +// Define this to 1 to force use of high-performance GPU on hybrid systems +#cmakedefine _GLFW_USE_HYBRID_HPG + +// Define this to 1 if xkbcommon supports the compose key +#cmakedefine HAVE_XKBCOMMON_COMPOSE_H +// Define this to 1 if the libc supports memfd_create() +#cmakedefine HAVE_MEMFD_CREATE + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/glx_context.c b/source/MaterialXGraphEditor/External/Glfw/src/glx_context.c new file mode 100644 index 0000000000..7ccd137477 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/glx_context.c @@ -0,0 +1,698 @@ +//======================================================================== +// GLFW 3.4 GLX - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#include "internal.h" + +#include +#include +#include + +#ifndef GLXBadProfileARB + #define GLXBadProfileARB 13 +#endif + + +// Returns the specified attribute of the specified GLXFBConfig +// +static int getGLXFBConfigAttrib(GLXFBConfig fbconfig, int attrib) +{ + int value; + glXGetFBConfigAttrib(_glfw.x11.display, fbconfig, attrib, &value); + return value; +} + +// Return the GLXFBConfig most closely matching the specified hints +// +static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, + GLXFBConfig* result) +{ + GLXFBConfig* nativeConfigs; + _GLFWfbconfig* usableConfigs; + const _GLFWfbconfig* closest; + int i, nativeCount, usableCount; + const char* vendor; + GLFWbool trustWindowBit = GLFW_TRUE; + + // HACK: This is a (hopefully temporary) workaround for Chromium + // (VirtualBox GL) not setting the window bit on any GLXFBConfigs + vendor = glXGetClientString(_glfw.x11.display, GLX_VENDOR); + if (vendor && strcmp(vendor, "Chromium") == 0) + trustWindowBit = GLFW_FALSE; + + nativeConfigs = + glXGetFBConfigs(_glfw.x11.display, _glfw.x11.screen, &nativeCount); + if (!nativeConfigs || !nativeCount) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: No GLXFBConfigs returned"); + return GLFW_FALSE; + } + + usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); + usableCount = 0; + + for (i = 0; i < nativeCount; i++) + { + const GLXFBConfig n = nativeConfigs[i]; + _GLFWfbconfig* u = usableConfigs + usableCount; + + // Only consider RGBA GLXFBConfigs + if (!(getGLXFBConfigAttrib(n, GLX_RENDER_TYPE) & GLX_RGBA_BIT)) + continue; + + // Only consider window GLXFBConfigs + if (!(getGLXFBConfigAttrib(n, GLX_DRAWABLE_TYPE) & GLX_WINDOW_BIT)) + { + if (trustWindowBit) + 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->greenBits = getGLXFBConfigAttrib(n, GLX_GREEN_SIZE); + u->blueBits = getGLXFBConfigAttrib(n, GLX_BLUE_SIZE); + + u->alphaBits = getGLXFBConfigAttrib(n, GLX_ALPHA_SIZE); + u->depthBits = getGLXFBConfigAttrib(n, GLX_DEPTH_SIZE); + u->stencilBits = getGLXFBConfigAttrib(n, GLX_STENCIL_SIZE); + + u->accumRedBits = getGLXFBConfigAttrib(n, GLX_ACCUM_RED_SIZE); + u->accumGreenBits = getGLXFBConfigAttrib(n, GLX_ACCUM_GREEN_SIZE); + u->accumBlueBits = getGLXFBConfigAttrib(n, GLX_ACCUM_BLUE_SIZE); + u->accumAlphaBits = getGLXFBConfigAttrib(n, GLX_ACCUM_ALPHA_SIZE); + + u->auxBuffers = getGLXFBConfigAttrib(n, GLX_AUX_BUFFERS); + + if (getGLXFBConfigAttrib(n, GLX_STEREO)) + u->stereo = GLFW_TRUE; + if (getGLXFBConfigAttrib(n, GLX_DOUBLEBUFFER)) + u->doublebuffer = GLFW_TRUE; + + if (_glfw.glx.ARB_multisample) + u->samples = getGLXFBConfigAttrib(n, GLX_SAMPLES); + + if (_glfw.glx.ARB_framebuffer_sRGB || _glfw.glx.EXT_framebuffer_sRGB) + u->sRGB = getGLXFBConfigAttrib(n, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB); + + u->handle = (uintptr_t) n; + usableCount++; + } + + closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); + if (closest) + *result = (GLXFBConfig) closest->handle; + + XFree(nativeConfigs); + free(usableConfigs); + + return closest != NULL; +} + +// Create the OpenGL context using legacy API +// +static GLXContext createLegacyContextGLX(_GLFWwindow* window, + GLXFBConfig fbconfig, + GLXContext share) +{ + return glXCreateNewContext(_glfw.x11.display, + fbconfig, + GLX_RGBA_TYPE, + share, + True); +} + +static void makeContextCurrentGLX(_GLFWwindow* window) +{ + if (window) + { + if (!glXMakeCurrent(_glfw.x11.display, + window->context.glx.window, + window->context.glx.handle)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "GLX: Failed to make context current"); + return; + } + } + else + { + if (!glXMakeCurrent(_glfw.x11.display, None, NULL)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "GLX: Failed to clear current context"); + return; + } + } + + _glfwPlatformSetTls(&_glfw.contextSlot, window); +} + +static void swapBuffersGLX(_GLFWwindow* window) +{ + glXSwapBuffers(_glfw.x11.display, window->context.glx.window); +} + +static void swapIntervalGLX(int interval) +{ + _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); + + if (_glfw.glx.EXT_swap_control) + { + _glfw.glx.SwapIntervalEXT(_glfw.x11.display, + window->context.glx.window, + interval); + } + else if (_glfw.glx.MESA_swap_control) + _glfw.glx.SwapIntervalMESA(interval); + else if (_glfw.glx.SGI_swap_control) + { + if (interval > 0) + _glfw.glx.SwapIntervalSGI(interval); + } +} + +static int extensionSupportedGLX(const char* extension) +{ + const char* extensions = + glXQueryExtensionsString(_glfw.x11.display, _glfw.x11.screen); + if (extensions) + { + if (_glfwStringInExtensionString(extension, extensions)) + return GLFW_TRUE; + } + + return GLFW_FALSE; +} + +static GLFWglproc getProcAddressGLX(const char* procname) +{ + if (_glfw.glx.GetProcAddress) + return _glfw.glx.GetProcAddress((const GLubyte*) procname); + else if (_glfw.glx.GetProcAddressARB) + return _glfw.glx.GetProcAddressARB((const GLubyte*) procname); + else + return _glfw_dlsym(_glfw.glx.handle, procname); +} + +static void destroyContextGLX(_GLFWwindow* window) +{ + if (window->context.glx.window) + { + glXDestroyWindow(_glfw.x11.display, window->context.glx.window); + window->context.glx.window = None; + } + + if (window->context.glx.handle) + { + glXDestroyContext(_glfw.x11.display, window->context.glx.handle); + window->context.glx.handle = NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialize GLX +// +GLFWbool _glfwInitGLX(void) +{ + int i; + const char* sonames[] = + { +#if defined(_GLFW_GLX_LIBRARY) + _GLFW_GLX_LIBRARY, +#elif defined(__CYGWIN__) + "libGL-1.so", +#else + "libGL.so.1", + "libGL.so", +#endif + NULL + }; + + if (_glfw.glx.handle) + return GLFW_TRUE; + + for (i = 0; sonames[i]; i++) + { + _glfw.glx.handle = _glfw_dlopen(sonames[i]); + if (_glfw.glx.handle) + break; + } + + if (!_glfw.glx.handle) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: Failed to load GLX"); + return GLFW_FALSE; + } + + _glfw.glx.GetFBConfigs = + _glfw_dlsym(_glfw.glx.handle, "glXGetFBConfigs"); + _glfw.glx.GetFBConfigAttrib = + _glfw_dlsym(_glfw.glx.handle, "glXGetFBConfigAttrib"); + _glfw.glx.GetClientString = + _glfw_dlsym(_glfw.glx.handle, "glXGetClientString"); + _glfw.glx.QueryExtension = + _glfw_dlsym(_glfw.glx.handle, "glXQueryExtension"); + _glfw.glx.QueryVersion = + _glfw_dlsym(_glfw.glx.handle, "glXQueryVersion"); + _glfw.glx.DestroyContext = + _glfw_dlsym(_glfw.glx.handle, "glXDestroyContext"); + _glfw.glx.MakeCurrent = + _glfw_dlsym(_glfw.glx.handle, "glXMakeCurrent"); + _glfw.glx.SwapBuffers = + _glfw_dlsym(_glfw.glx.handle, "glXSwapBuffers"); + _glfw.glx.QueryExtensionsString = + _glfw_dlsym(_glfw.glx.handle, "glXQueryExtensionsString"); + _glfw.glx.CreateNewContext = + _glfw_dlsym(_glfw.glx.handle, "glXCreateNewContext"); + _glfw.glx.CreateWindow = + _glfw_dlsym(_glfw.glx.handle, "glXCreateWindow"); + _glfw.glx.DestroyWindow = + _glfw_dlsym(_glfw.glx.handle, "glXDestroyWindow"); + _glfw.glx.GetProcAddress = + _glfw_dlsym(_glfw.glx.handle, "glXGetProcAddress"); + _glfw.glx.GetProcAddressARB = + _glfw_dlsym(_glfw.glx.handle, "glXGetProcAddressARB"); + _glfw.glx.GetVisualFromFBConfig = + _glfw_dlsym(_glfw.glx.handle, "glXGetVisualFromFBConfig"); + + if (!_glfw.glx.GetFBConfigs || + !_glfw.glx.GetFBConfigAttrib || + !_glfw.glx.GetClientString || + !_glfw.glx.QueryExtension || + !_glfw.glx.QueryVersion || + !_glfw.glx.DestroyContext || + !_glfw.glx.MakeCurrent || + !_glfw.glx.SwapBuffers || + !_glfw.glx.QueryExtensionsString || + !_glfw.glx.CreateNewContext || + !_glfw.glx.CreateWindow || + !_glfw.glx.DestroyWindow || + !_glfw.glx.GetProcAddress || + !_glfw.glx.GetProcAddressARB || + !_glfw.glx.GetVisualFromFBConfig) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "GLX: Failed to load required entry points"); + return GLFW_FALSE; + } + + if (!glXQueryExtension(_glfw.x11.display, + &_glfw.glx.errorBase, + &_glfw.glx.eventBase)) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: GLX extension not found"); + return GLFW_FALSE; + } + + if (!glXQueryVersion(_glfw.x11.display, &_glfw.glx.major, &_glfw.glx.minor)) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "GLX: Failed to query GLX version"); + return GLFW_FALSE; + } + + if (_glfw.glx.major == 1 && _glfw.glx.minor < 3) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "GLX: GLX version 1.3 is required"); + return GLFW_FALSE; + } + + if (extensionSupportedGLX("GLX_EXT_swap_control")) + { + _glfw.glx.SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) + getProcAddressGLX("glXSwapIntervalEXT"); + + if (_glfw.glx.SwapIntervalEXT) + _glfw.glx.EXT_swap_control = GLFW_TRUE; + } + + if (extensionSupportedGLX("GLX_SGI_swap_control")) + { + _glfw.glx.SwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC) + getProcAddressGLX("glXSwapIntervalSGI"); + + if (_glfw.glx.SwapIntervalSGI) + _glfw.glx.SGI_swap_control = GLFW_TRUE; + } + + if (extensionSupportedGLX("GLX_MESA_swap_control")) + { + _glfw.glx.SwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC) + getProcAddressGLX("glXSwapIntervalMESA"); + + if (_glfw.glx.SwapIntervalMESA) + _glfw.glx.MESA_swap_control = GLFW_TRUE; + } + + if (extensionSupportedGLX("GLX_ARB_multisample")) + _glfw.glx.ARB_multisample = GLFW_TRUE; + + if (extensionSupportedGLX("GLX_ARB_framebuffer_sRGB")) + _glfw.glx.ARB_framebuffer_sRGB = GLFW_TRUE; + + if (extensionSupportedGLX("GLX_EXT_framebuffer_sRGB")) + _glfw.glx.EXT_framebuffer_sRGB = GLFW_TRUE; + + if (extensionSupportedGLX("GLX_ARB_create_context")) + { + _glfw.glx.CreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) + getProcAddressGLX("glXCreateContextAttribsARB"); + + if (_glfw.glx.CreateContextAttribsARB) + _glfw.glx.ARB_create_context = GLFW_TRUE; + } + + if (extensionSupportedGLX("GLX_ARB_create_context_robustness")) + _glfw.glx.ARB_create_context_robustness = GLFW_TRUE; + + if (extensionSupportedGLX("GLX_ARB_create_context_profile")) + _glfw.glx.ARB_create_context_profile = GLFW_TRUE; + + if (extensionSupportedGLX("GLX_EXT_create_context_es2_profile")) + _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")) + _glfw.glx.ARB_context_flush_control = GLFW_TRUE; + + return GLFW_TRUE; +} + +// Terminate GLX +// +void _glfwTerminateGLX(void) +{ + // NOTE: This function must not call any X11 functions, as it is called + // after XCloseDisplay (see _glfwPlatformTerminate for details) + + if (_glfw.glx.handle) + { + _glfw_dlclose(_glfw.glx.handle); + _glfw.glx.handle = NULL; + } +} + +#define setAttrib(a, v) \ +{ \ + assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ + attribs[index++] = a; \ + attribs[index++] = v; \ +} + +// Create the OpenGL or OpenGL ES context +// +GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + int attribs[40]; + GLXFBConfig native = NULL; + GLXContext share = NULL; + + if (ctxconfig->share) + share = ctxconfig->share->context.glx.handle; + + if (!chooseGLXFBConfig(fbconfig, &native)) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "GLX: Failed to find a suitable GLXFBConfig"); + return GLFW_FALSE; + } + + if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + if (!_glfw.glx.ARB_create_context || + !_glfw.glx.ARB_create_context_profile || + !_glfw.glx.EXT_create_context_es2_profile) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "GLX: OpenGL ES requested but GLX_EXT_create_context_es2_profile is unavailable"); + return GLFW_FALSE; + } + } + + if (ctxconfig->forward) + { + if (!_glfw.glx.ARB_create_context) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "GLX: Forward compatibility requested but GLX_ARB_create_context_profile is unavailable"); + return GLFW_FALSE; + } + } + + if (ctxconfig->profile) + { + if (!_glfw.glx.ARB_create_context || + !_glfw.glx.ARB_create_context_profile) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "GLX: An OpenGL profile requested but GLX_ARB_create_context_profile is unavailable"); + return GLFW_FALSE; + } + } + + _glfwGrabErrorHandlerX11(); + + if (_glfw.glx.ARB_create_context) + { + int index = 0, mask = 0, flags = 0; + + if (ctxconfig->client == GLFW_OPENGL_API) + { + if (ctxconfig->forward) + flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; + + if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) + mask |= GLX_CONTEXT_CORE_PROFILE_BIT_ARB; + else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) + mask |= GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; + } + else + mask |= GLX_CONTEXT_ES2_PROFILE_BIT_EXT; + + if (ctxconfig->debug) + flags |= GLX_CONTEXT_DEBUG_BIT_ARB; + + if (ctxconfig->robustness) + { + if (_glfw.glx.ARB_create_context_robustness) + { + if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) + { + setAttrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + GLX_NO_RESET_NOTIFICATION_ARB); + } + else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) + { + setAttrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + GLX_LOSE_CONTEXT_ON_RESET_ARB); + } + + flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB; + } + } + + if (ctxconfig->release) + { + if (_glfw.glx.ARB_context_flush_control) + { + if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) + { + setAttrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, + GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); + } + else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) + { + setAttrib(GLX_CONTEXT_RELEASE_BEHAVIOR_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 + // explicitly requesting version 1.0 does not always return the + // highest version supported by the driver + if (ctxconfig->major != 1 || ctxconfig->minor != 0) + { + setAttrib(GLX_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); + setAttrib(GLX_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); + } + + if (mask) + setAttrib(GLX_CONTEXT_PROFILE_MASK_ARB, mask); + + if (flags) + setAttrib(GLX_CONTEXT_FLAGS_ARB, flags); + + setAttrib(None, None); + + window->context.glx.handle = + _glfw.glx.CreateContextAttribsARB(_glfw.x11.display, + native, + share, + True, + attribs); + + // HACK: This is a fallback for broken versions of the Mesa + // implementation of GLX_ARB_create_context_profile that fail + // default 1.0 context creation with a GLXBadProfileARB error in + // violation of the extension spec + if (!window->context.glx.handle) + { + if (_glfw.x11.errorCode == _glfw.glx.errorBase + GLXBadProfileARB && + ctxconfig->client == GLFW_OPENGL_API && + ctxconfig->profile == GLFW_OPENGL_ANY_PROFILE && + ctxconfig->forward == GLFW_FALSE) + { + window->context.glx.handle = + createLegacyContextGLX(window, native, share); + } + } + } + else + { + window->context.glx.handle = + createLegacyContextGLX(window, native, share); + } + + _glfwReleaseErrorHandlerX11(); + + if (!window->context.glx.handle) + { + _glfwInputErrorX11(GLFW_VERSION_UNAVAILABLE, "GLX: Failed to create context"); + return GLFW_FALSE; + } + + window->context.glx.window = + glXCreateWindow(_glfw.x11.display, native, window->x11.handle, NULL); + if (!window->context.glx.window) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "GLX: Failed to create window"); + return GLFW_FALSE; + } + + window->context.makeCurrent = makeContextCurrentGLX; + window->context.swapBuffers = swapBuffersGLX; + window->context.swapInterval = swapIntervalGLX; + window->context.extensionSupported = extensionSupportedGLX; + window->context.getProcAddress = getProcAddressGLX; + window->context.destroy = destroyContextGLX; + + return GLFW_TRUE; +} + +#undef setAttrib + +// Returns the Visual and depth of the chosen GLXFBConfig +// +GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig, + Visual** visual, int* depth) +{ + GLXFBConfig native; + XVisualInfo* result; + + if (!chooseGLXFBConfig(fbconfig, &native)) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "GLX: Failed to find a suitable GLXFBConfig"); + return GLFW_FALSE; + } + + result = glXGetVisualFromFBConfig(_glfw.x11.display, native); + if (!result) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "GLX: Failed to retrieve Visual for GLXFBConfig"); + return GLFW_FALSE; + } + + *visual = result->visual; + *depth = result->depth; + + XFree(result); + return GLFW_TRUE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return NULL; + } + + return window->context.glx.handle; +} + +GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(None); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return None; + } + + return window->context.glx.window; +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/glx_context.h b/source/MaterialXGraphEditor/External/Glfw/src/glx_context.h new file mode 100644 index 0000000000..94f07e2ebb --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/glx_context.h @@ -0,0 +1,181 @@ +//======================================================================== +// GLFW 3.4 GLX - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#define GLX_VENDOR 1 +#define GLX_RGBA_BIT 0x00000001 +#define GLX_WINDOW_BIT 0x00000001 +#define GLX_DRAWABLE_TYPE 0x8010 +#define GLX_RENDER_TYPE 0x8011 +#define GLX_RGBA_TYPE 0x8014 +#define GLX_DOUBLEBUFFER 5 +#define GLX_STEREO 6 +#define GLX_AUX_BUFFERS 7 +#define GLX_RED_SIZE 8 +#define GLX_GREEN_SIZE 9 +#define GLX_BLUE_SIZE 10 +#define GLX_ALPHA_SIZE 11 +#define GLX_DEPTH_SIZE 12 +#define GLX_STENCIL_SIZE 13 +#define GLX_ACCUM_RED_SIZE 14 +#define GLX_ACCUM_GREEN_SIZE 15 +#define GLX_ACCUM_BLUE_SIZE 16 +#define GLX_ACCUM_ALPHA_SIZE 17 +#define GLX_SAMPLES 0x186a1 +#define GLX_VISUAL_ID 0x800b + +#define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20b2 +#define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define GLX_CONTEXT_FLAGS_ARB 0x2094 +#define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 +#define GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define GLX_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define GLX_NO_RESET_NOTIFICATION_ARB 0x8261 +#define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 +#define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 +#define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 +#define GLX_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3 + +typedef XID GLXWindow; +typedef XID GLXDrawable; +typedef struct __GLXFBConfig* GLXFBConfig; +typedef struct __GLXcontext* GLXContext; +typedef void (*__GLXextproc)(void); + +typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*); +typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int); +typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*); +typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*); +typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display*,GLXContext); +typedef Bool (*PFNGLXMAKECURRENTPROC)(Display*,GLXDrawable,GLXContext); +typedef void (*PFNGLXSWAPBUFFERSPROC)(Display*,GLXDrawable); +typedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int); +typedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*); +typedef GLXContext (*PFNGLXCREATENEWCONTEXTPROC)(Display*,GLXFBConfig,int,GLXContext,Bool); +typedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const GLubyte *procName); +typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int); +typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig); +typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*); +typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow); + +typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int); +typedef int (*PFNGLXSWAPINTERVALSGIPROC)(int); +typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*); + +// libGL.so function pointer typedefs +#define glXGetFBConfigs _glfw.glx.GetFBConfigs +#define glXGetFBConfigAttrib _glfw.glx.GetFBConfigAttrib +#define glXGetClientString _glfw.glx.GetClientString +#define glXQueryExtension _glfw.glx.QueryExtension +#define glXQueryVersion _glfw.glx.QueryVersion +#define glXDestroyContext _glfw.glx.DestroyContext +#define glXMakeCurrent _glfw.glx.MakeCurrent +#define glXSwapBuffers _glfw.glx.SwapBuffers +#define glXQueryExtensionsString _glfw.glx.QueryExtensionsString +#define glXCreateNewContext _glfw.glx.CreateNewContext +#define glXGetVisualFromFBConfig _glfw.glx.GetVisualFromFBConfig +#define glXCreateWindow _glfw.glx.CreateWindow +#define glXDestroyWindow _glfw.glx.DestroyWindow + +#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextGLX glx +#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryGLX glx + + +// GLX-specific per-context data +// +typedef struct _GLFWcontextGLX +{ + GLXContext handle; + GLXWindow window; + +} _GLFWcontextGLX; + +// GLX-specific global data +// +typedef struct _GLFWlibraryGLX +{ + int major, minor; + int eventBase; + int errorBase; + + // dlopen handle for libGL.so.1 + void* handle; + + // GLX 1.3 functions + PFNGLXGETFBCONFIGSPROC GetFBConfigs; + PFNGLXGETFBCONFIGATTRIBPROC GetFBConfigAttrib; + PFNGLXGETCLIENTSTRINGPROC GetClientString; + PFNGLXQUERYEXTENSIONPROC QueryExtension; + PFNGLXQUERYVERSIONPROC QueryVersion; + PFNGLXDESTROYCONTEXTPROC DestroyContext; + PFNGLXMAKECURRENTPROC MakeCurrent; + PFNGLXSWAPBUFFERSPROC SwapBuffers; + PFNGLXQUERYEXTENSIONSSTRINGPROC QueryExtensionsString; + PFNGLXCREATENEWCONTEXTPROC CreateNewContext; + PFNGLXGETVISUALFROMFBCONFIGPROC GetVisualFromFBConfig; + PFNGLXCREATEWINDOWPROC CreateWindow; + PFNGLXDESTROYWINDOWPROC DestroyWindow; + + // GLX 1.4 and extension functions + PFNGLXGETPROCADDRESSPROC GetProcAddress; + PFNGLXGETPROCADDRESSPROC GetProcAddressARB; + PFNGLXSWAPINTERVALSGIPROC SwapIntervalSGI; + PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT; + PFNGLXSWAPINTERVALMESAPROC SwapIntervalMESA; + PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; + GLFWbool SGI_swap_control; + GLFWbool EXT_swap_control; + GLFWbool MESA_swap_control; + GLFWbool ARB_multisample; + GLFWbool ARB_framebuffer_sRGB; + GLFWbool EXT_framebuffer_sRGB; + GLFWbool ARB_create_context; + GLFWbool ARB_create_context_profile; + GLFWbool ARB_create_context_robustness; + GLFWbool EXT_create_context_es2_profile; + GLFWbool ARB_create_context_no_error; + GLFWbool ARB_context_flush_control; + +} _GLFWlibraryGLX; + +GLFWbool _glfwInitGLX(void); +void _glfwTerminateGLX(void); +GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig); +void _glfwDestroyContextGLX(_GLFWwindow* window); +GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig, + Visual** visual, int* depth); + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/init.c b/source/MaterialXGraphEditor/External/Glfw/src/init.c new file mode 100644 index 0000000000..13baff707f --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/init.c @@ -0,0 +1,342 @@ +//======================================================================== +// GLFW 3.4 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2018 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// Please use C89 style variable declarations in this file because VS 2010 +//======================================================================== + +#include "internal.h" +#include "mappings.h" + +#include +#include +#include +#include +#include + + +// The global variables below comprise all mutable global data in GLFW +// +// Any other global variable is a bug + +// Global state shared between compilation units of GLFW +// +_GLFWlibrary _glfw = { GLFW_FALSE }; + +// These are outside of _glfw so they can be used before initialization and +// after termination +// +static _GLFWerror _glfwMainThreadError; +static GLFWerrorfun _glfwErrorCallback; +static _GLFWinitconfig _glfwInitHints = +{ + GLFW_TRUE, // hat buttons + { + GLFW_TRUE, // macOS menu bar + GLFW_TRUE // macOS bundle chdir + } +}; + +// Terminate the library +// +static void terminate(void) +{ + int i; + + memset(&_glfw.callbacks, 0, sizeof(_glfw.callbacks)); + + while (_glfw.windowListHead) + glfwDestroyWindow((GLFWwindow*) _glfw.windowListHead); + + while (_glfw.cursorListHead) + glfwDestroyCursor((GLFWcursor*) _glfw.cursorListHead); + + for (i = 0; i < _glfw.monitorCount; i++) + { + _GLFWmonitor* monitor = _glfw.monitors[i]; + if (monitor->originalRamp.size) + _glfwPlatformSetGammaRamp(monitor, &monitor->originalRamp); + _glfwFreeMonitor(monitor); + } + + free(_glfw.monitors); + _glfw.monitors = NULL; + _glfw.monitorCount = 0; + + free(_glfw.mappings); + _glfw.mappings = NULL; + _glfw.mappingCount = 0; + + _glfwTerminateVulkan(); + _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)); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +char* _glfw_strdup(const char* source) +{ + const size_t length = strlen(source); + char* result = calloc(length + 1, 1); + strcpy(result, source); + return result; +} + +float _glfw_fminf(float a, float b) +{ + if (a != a) + return b; + else if (b != b) + return a; + else if (a < b) + return a; + else + return b; +} + +float _glfw_fmaxf(float a, float b) +{ + if (a != a) + return b; + else if (b != b) + return a; + else if (a > b) + return a; + else + return b; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW event API ////// +////////////////////////////////////////////////////////////////////////// + +// Notifies shared code of an error +// +void _glfwInputError(int code, const char* format, ...) +{ + _GLFWerror* error; + char description[_GLFW_MESSAGE_SIZE]; + + if (format) + { + va_list vl; + + va_start(vl, format); + vsnprintf(description, sizeof(description), format, vl); + va_end(vl); + + description[sizeof(description) - 1] = '\0'; + } + else + { + if (code == GLFW_NOT_INITIALIZED) + strcpy(description, "The GLFW library is not initialized"); + else if (code == GLFW_NO_CURRENT_CONTEXT) + strcpy(description, "There is no current context"); + else if (code == GLFW_INVALID_ENUM) + strcpy(description, "Invalid argument for enum parameter"); + else if (code == GLFW_INVALID_VALUE) + strcpy(description, "Invalid value for parameter"); + else if (code == GLFW_OUT_OF_MEMORY) + strcpy(description, "Out of memory"); + else if (code == GLFW_API_UNAVAILABLE) + strcpy(description, "The requested API is unavailable"); + else if (code == GLFW_VERSION_UNAVAILABLE) + strcpy(description, "The requested API version is unavailable"); + else if (code == GLFW_PLATFORM_ERROR) + strcpy(description, "A platform-specific error occurred"); + else if (code == GLFW_FORMAT_UNAVAILABLE) + strcpy(description, "The requested format is unavailable"); + else if (code == GLFW_NO_WINDOW_CONTEXT) + strcpy(description, "The specified window has no context"); + else if (code == GLFW_CURSOR_UNAVAILABLE) + strcpy(description, "The specified cursor shape is unavailable"); + else + strcpy(description, "ERROR: UNKNOWN GLFW ERROR"); + } + + 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); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW public API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI int glfwInit(void) +{ + if (_glfw.initialized) + return GLFW_TRUE; + + memset(&_glfw, 0, sizeof(_glfw)); + _glfw.hints.init = _glfwInitHints; + + if (!_glfwPlatformInit()) + { + terminate(); + return GLFW_FALSE; + } + + if (!_glfwPlatformCreateMutex(&_glfw.errorLock) || + !_glfwPlatformCreateTls(&_glfw.errorSlot) || + !_glfwPlatformCreateTls(&_glfw.contextSlot)) + { + terminate(); + return GLFW_FALSE; + } + + _glfwPlatformSetTls(&_glfw.errorSlot, &_glfwMainThreadError); + + _glfw.initialized = GLFW_TRUE; + _glfw.timer.offset = _glfwPlatformGetTimerValue(); + + glfwDefaultWindowHints(); + + { + int i; + + for (i = 0; _glfwDefaultMappings[i]; i++) + { + if (!glfwUpdateGamepadMappings(_glfwDefaultMappings[i])) + { + terminate(); + return GLFW_FALSE; + } + } + } + + return GLFW_TRUE; +} + +GLFWAPI void glfwTerminate(void) +{ + if (!_glfw.initialized) + return; + + terminate(); +} + +GLFWAPI void glfwInitHint(int hint, int value) +{ + switch (hint) + { + case GLFW_JOYSTICK_HAT_BUTTONS: + _glfwInitHints.hatButtons = value; + return; + case GLFW_COCOA_CHDIR_RESOURCES: + _glfwInitHints.ns.chdir = value; + return; + case GLFW_COCOA_MENUBAR: + _glfwInitHints.ns.menubar = value; + return; + } + + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid init hint 0x%08X", hint); +} + +GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev) +{ + if (major != NULL) + *major = GLFW_VERSION_MAJOR; + if (minor != NULL) + *minor = GLFW_VERSION_MINOR; + if (rev != NULL) + *rev = GLFW_VERSION_REVISION; +} + +GLFWAPI const char* glfwGetVersionString(void) +{ + 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) +{ + _GLFW_SWAP_POINTERS(_glfwErrorCallback, cbfun); + return cbfun; +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/input.c b/source/MaterialXGraphEditor/External/Glfw/src/input.c new file mode 100644 index 0000000000..f6163093c1 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/input.c @@ -0,0 +1,1360 @@ +//======================================================================== +// GLFW 3.4 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// Please use C89 style variable declarations in this file because VS 2010 +//======================================================================== + +#include "internal.h" + +#include +#include +#include +#include +#include + +// Internal key state used for sticky keys +#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->index >> 4) >= js->hatCount) + return GLFW_FALSE; + else if (e->type == _GLFW_JOYSTICK_BUTTON && e->index >= js->buttonCount) + return GLFW_FALSE; + else if (e->type == _GLFW_JOYSTICK_AXIS && e->index >= 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) + { + // TODO: Implement output modifiers + if (*c == '+' || *c == '-') + return GLFW_FALSE; + + 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) + { + _GLFWmapelement* e = fields[i].element; + int8_t minimum = -1; + int8_t maximum = 1; + + if (*c == '+') + { + minimum = 0; + c += 1; + } + else if (*c == '-') + { + maximum = 0; + c += 1; + } + + if (*c == 'a') + e->type = _GLFW_JOYSTICK_AXIS; + else if (*c == 'b') + e->type = _GLFW_JOYSTICK_BUTTON; + else if (*c == 'h') + e->type = _GLFW_JOYSTICK_HATBIT; + else + break; + + if (e->type == _GLFW_JOYSTICK_HATBIT) + { + const unsigned long hat = strtoul(c + 1, (char**) &c, 10); + const unsigned long bit = strtoul(c + 1, (char**) &c, 10); + e->index = (uint8_t) ((hat << 4) | bit); + } + else + e->index = (uint8_t) strtoul(c + 1, (char**) &c, 10); + + if (e->type == _GLFW_JOYSTICK_AXIS) + { + e->axisScale = 2 / (maximum - minimum); + e->axisOffset = -(maximum + minimum); + + if (*c == '~') + { + e->axisScale = -e->axisScale; + e->axisOffset = -e->axisOffset; + } + } + } + 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 ////// +////////////////////////////////////////////////////////////////////////// + +// Notifies shared code of a physical key event +// +void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (key >= 0 && key <= GLFW_KEY_LAST) + { + GLFWbool repeated = GLFW_FALSE; + + if (action == GLFW_RELEASE && window->keys[key] == GLFW_RELEASE) + return; + + if (action == GLFW_PRESS && window->keys[key] == GLFW_PRESS) + repeated = GLFW_TRUE; + + if (action == GLFW_RELEASE && window->stickyKeys) + window->keys[key] = _GLFW_STICK; + else + window->keys[key] = (char) action; + + if (repeated) + action = GLFW_REPEAT; + } + + if (!window->lockKeyMods) + mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK); + + if (window->callbacks.key) + window->callbacks.key((GLFWwindow*) window, key, scancode, action, mods); +} + +// Notifies shared code of a Unicode codepoint input event +// The 'plain' parameter determines whether to emit a regular character event +// +void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint, int mods, GLFWbool plain) +{ + if (codepoint < 32 || (codepoint > 126 && codepoint < 160)) + return; + + if (!window->lockKeyMods) + mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK); + + if (window->callbacks.charmods) + window->callbacks.charmods((GLFWwindow*) window, codepoint, mods); + + if (plain) + { + if (window->callbacks.character) + window->callbacks.character((GLFWwindow*) window, codepoint); + } +} + +// Notifies shared code of a scroll event +// +void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset) +{ + if (window->callbacks.scroll) + window->callbacks.scroll((GLFWwindow*) window, xoffset, yoffset); +} + +// Notifies shared code of a mouse button click event +// +void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods) +{ + if (button < 0 || button > GLFW_MOUSE_BUTTON_LAST) + return; + + if (!window->lockKeyMods) + mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK); + + if (action == GLFW_RELEASE && window->stickyMouseButtons) + window->mouseButtons[button] = _GLFW_STICK; + else + window->mouseButtons[button] = (char) action; + + if (window->callbacks.mouseButton) + window->callbacks.mouseButton((GLFWwindow*) window, button, action, mods); +} + +// Notifies shared code of a cursor motion event +// The position is specified in content area relative screen coordinates +// +void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos) +{ + if (window->virtualCursorPosX == xpos && window->virtualCursorPosY == ypos) + return; + + window->virtualCursorPosX = xpos; + window->virtualCursorPosY = ypos; + + if (window->callbacks.cursorPos) + window->callbacks.cursorPos((GLFWwindow*) window, xpos, ypos); +} + +// Notifies shared code of a cursor enter/leave event +// +void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered) +{ + if (window->callbacks.cursorEnter) + window->callbacks.cursorEnter((GLFWwindow*) window, entered); +} + +// Notifies shared code of files or directories dropped on a window +// +void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths) +{ + if (window->callbacks.drop) + window->callbacks.drop((GLFWwindow*) window, count, paths); +} + +// Notifies shared code of a joystick connection or disconnection +// +void _glfwInputJoystick(_GLFWjoystick* js, int event) +{ + const int jid = (int) (js - _glfw.joysticks); + + if (_glfw.callbacks.joystick) + _glfw.callbacks.joystick(jid, event); +} + +// Notifies shared code of the new value of a joystick axis +// +void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value) +{ + js->axes[axis] = value; +} + +// Notifies shared code of the new value of a joystick button +// +void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value) +{ + js->buttons[button] = value; +} + +// Notifies shared code of the new value of a joystick hat +// +void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value) +{ + const int base = js->buttonCount + hat * 4; + + js->buttons[base + 0] = (value & 0x01) ? GLFW_PRESS : GLFW_RELEASE; + js->buttons[base + 1] = (value & 0x02) ? GLFW_PRESS : GLFW_RELEASE; + js->buttons[base + 2] = (value & 0x04) ? GLFW_PRESS : GLFW_RELEASE; + js->buttons[base + 3] = (value & 0x08) ? GLFW_PRESS : GLFW_RELEASE; + + js->hats[hat] = value; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Returns an available joystick object with arrays and name allocated +// +_GLFWjoystick* _glfwAllocJoystick(const char* name, + const char* guid, + int axisCount, + int buttonCount, + int hatCount) +{ + int jid; + _GLFWjoystick* js; + + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (!_glfw.joysticks[jid].present) + break; + } + + if (jid > GLFW_JOYSTICK_LAST) + return NULL; + + js = _glfw.joysticks + jid; + js->present = GLFW_TRUE; + js->name = _glfw_strdup(name); + js->axes = calloc(axisCount, sizeof(float)); + js->buttons = calloc(buttonCount + (size_t) hatCount * 4, 1); + js->hats = calloc(hatCount, 1); + js->axisCount = axisCount; + js->buttonCount = buttonCount; + js->hatCount = hatCount; + + strncpy(js->guid, guid, sizeof(js->guid) - 1); + js->mapping = findValidMapping(js); + + return js; +} + +// Frees arrays and name and flags the joystick object as unused +// +void _glfwFreeJoystick(_GLFWjoystick* js) +{ + free(js->name); + free(js->axes); + free(js->buttons); + free(js->hats); + memset(js, 0, sizeof(_GLFWjoystick)); +} + +// Center the cursor in the content area of the specified window +// +void _glfwCenterCursorInContentArea(_GLFWwindow* window) +{ + int width, height; + + _glfwPlatformGetWindowSize(window, &width, &height); + _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW public API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(0); + + switch (mode) + { + case GLFW_CURSOR: + return window->cursorMode; + case GLFW_STICKY_KEYS: + return window->stickyKeys; + case GLFW_STICKY_MOUSE_BUTTONS: + return window->stickyMouseButtons; + case GLFW_LOCK_KEY_MODS: + return window->lockKeyMods; + case GLFW_RAW_MOUSE_MOTION: + return window->rawMouseMotion; + } + + _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode); + return 0; +} + +GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (mode == GLFW_CURSOR) + { + if (value != GLFW_CURSOR_NORMAL && + value != GLFW_CURSOR_HIDDEN && + value != GLFW_CURSOR_DISABLED) + { + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid cursor mode 0x%08X", + value); + return; + } + + if (window->cursorMode == value) + return; + + window->cursorMode = value; + + _glfwPlatformGetCursorPos(window, + &window->virtualCursorPosX, + &window->virtualCursorPosY); + _glfwPlatformSetCursorMode(window, value); + } + else if (mode == GLFW_STICKY_KEYS) + { + value = value ? GLFW_TRUE : GLFW_FALSE; + if (window->stickyKeys == value) + return; + + if (!value) + { + int i; + + // Release all sticky keys + for (i = 0; i <= GLFW_KEY_LAST; i++) + { + if (window->keys[i] == _GLFW_STICK) + window->keys[i] = GLFW_RELEASE; + } + } + + window->stickyKeys = value; + } + else if (mode == GLFW_STICKY_MOUSE_BUTTONS) + { + value = value ? GLFW_TRUE : GLFW_FALSE; + if (window->stickyMouseButtons == value) + return; + + if (!value) + { + int i; + + // Release all sticky mouse buttons + for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) + { + if (window->mouseButtons[i] == _GLFW_STICK) + window->mouseButtons[i] = GLFW_RELEASE; + } + } + + window->stickyMouseButtons = value; + } + else if (mode == GLFW_LOCK_KEY_MODS) + { + window->lockKeyMods = value ? GLFW_TRUE : GLFW_FALSE; + } + else if (mode == GLFW_RAW_MOUSE_MOTION) + { + if (!_glfwPlatformRawMouseMotionSupported()) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Raw mouse motion is not supported on this system"); + return; + } + + value = value ? GLFW_TRUE : GLFW_FALSE; + if (window->rawMouseMotion == value) + return; + + window->rawMouseMotion = value; + _glfwPlatformSetRawMouseMotion(window, value); + } + else + _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode); +} + +GLFWAPI int glfwRawMouseMotionSupported(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + return _glfwPlatformRawMouseMotionSupported(); +} + +GLFWAPI const char* glfwGetKeyName(int key, int scancode) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + 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) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(-1); + + if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key); + return GLFW_RELEASE; + } + + return _glfwPlatformGetKeyScancode(key); +} + +GLFWAPI int glfwGetKey(GLFWwindow* handle, int key) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_RELEASE); + + if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key); + return GLFW_RELEASE; + } + + if (window->keys[key] == _GLFW_STICK) + { + // Sticky mode: release key now + window->keys[key] = GLFW_RELEASE; + return GLFW_PRESS; + } + + return (int) window->keys[key]; +} + +GLFWAPI int glfwGetMouseButton(GLFWwindow* handle, int button) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_RELEASE); + + if (button < GLFW_MOUSE_BUTTON_1 || button > GLFW_MOUSE_BUTTON_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid mouse button %i", button); + return GLFW_RELEASE; + } + + if (window->mouseButtons[button] == _GLFW_STICK) + { + // Sticky mode: release mouse button now + window->mouseButtons[button] = GLFW_RELEASE; + return GLFW_PRESS; + } + + return (int) window->mouseButtons[button]; +} + +GLFWAPI void glfwGetCursorPos(GLFWwindow* handle, double* xpos, double* ypos) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + if (xpos) + *xpos = 0; + if (ypos) + *ypos = 0; + + _GLFW_REQUIRE_INIT(); + + if (window->cursorMode == GLFW_CURSOR_DISABLED) + { + if (xpos) + *xpos = window->virtualCursorPosX; + if (ypos) + *ypos = window->virtualCursorPosY; + } + else + _glfwPlatformGetCursorPos(window, xpos, ypos); +} + +GLFWAPI void glfwSetCursorPos(GLFWwindow* handle, double xpos, double ypos) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (xpos != xpos || xpos < -DBL_MAX || xpos > DBL_MAX || + ypos != ypos || ypos < -DBL_MAX || ypos > DBL_MAX) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid cursor position %f %f", + xpos, ypos); + return; + } + + if (!_glfwPlatformWindowFocused(window)) + return; + + if (window->cursorMode == GLFW_CURSOR_DISABLED) + { + // Only update the accumulated position if the cursor is disabled + window->virtualCursorPosX = xpos; + window->virtualCursorPosY = ypos; + } + else + { + // Update system cursor position + _glfwPlatformSetCursorPos(window, xpos, ypos); + } +} + +GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot) +{ + _GLFWcursor* cursor; + + assert(image != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + cursor = calloc(1, sizeof(_GLFWcursor)); + cursor->next = _glfw.cursorListHead; + _glfw.cursorListHead = cursor; + + if (!_glfwPlatformCreateCursor(cursor, image, xhot, yhot)) + { + glfwDestroyCursor((GLFWcursor*) cursor); + return NULL; + } + + return (GLFWcursor*) cursor; +} + +GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape) +{ + _GLFWcursor* cursor; + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (shape != GLFW_ARROW_CURSOR && + shape != GLFW_IBEAM_CURSOR && + shape != GLFW_CROSSHAIR_CURSOR && + shape != GLFW_POINTING_HAND_CURSOR && + shape != GLFW_RESIZE_EW_CURSOR && + shape != GLFW_RESIZE_NS_CURSOR && + shape != GLFW_RESIZE_NWSE_CURSOR && + shape != GLFW_RESIZE_NESW_CURSOR && + shape != GLFW_RESIZE_ALL_CURSOR && + shape != GLFW_NOT_ALLOWED_CURSOR) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid standard cursor 0x%08X", shape); + return NULL; + } + + cursor = calloc(1, sizeof(_GLFWcursor)); + cursor->next = _glfw.cursorListHead; + _glfw.cursorListHead = cursor; + + if (!_glfwPlatformCreateStandardCursor(cursor, shape)) + { + glfwDestroyCursor((GLFWcursor*) cursor); + return NULL; + } + + return (GLFWcursor*) cursor; +} + +GLFWAPI void glfwDestroyCursor(GLFWcursor* handle) +{ + _GLFWcursor* cursor = (_GLFWcursor*) handle; + + _GLFW_REQUIRE_INIT(); + + if (cursor == NULL) + return; + + // Make sure the cursor is not being used by any window + { + _GLFWwindow* window; + + for (window = _glfw.windowListHead; window; window = window->next) + { + if (window->cursor == cursor) + glfwSetCursor((GLFWwindow*) window, NULL); + } + } + + _glfwPlatformDestroyCursor(cursor); + + // Unlink cursor from global linked list + { + _GLFWcursor** prev = &_glfw.cursorListHead; + + while (*prev != cursor) + prev = &((*prev)->next); + + *prev = cursor->next; + } + + free(cursor); +} + +GLFWAPI void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle) +{ + _GLFWwindow* window = (_GLFWwindow*) windowHandle; + _GLFWcursor* cursor = (_GLFWcursor*) cursorHandle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + window->cursor = cursor; + + _glfwPlatformSetCursor(window, cursor); +} + +GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.key, cbfun); + return cbfun; +} + +GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* handle, GLFWcharfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.character, cbfun); + return cbfun; +} + +GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* handle, GLFWcharmodsfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.charmods, cbfun); + return cbfun; +} + +GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* handle, + GLFWmousebuttonfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.mouseButton, cbfun); + return cbfun; +} + +GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* handle, + GLFWcursorposfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.cursorPos, cbfun); + return cbfun; +} + +GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* handle, + GLFWcursorenterfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.cursorEnter, cbfun); + return cbfun; +} + +GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* handle, + GLFWscrollfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.scroll, cbfun); + return cbfun; +} + +GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.drop, cbfun); + return cbfun; +} + +GLFWAPI int glfwJoystickPresent(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; + + return _glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE); +} + +GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + assert(count != NULL); + + *count = 0; + + _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_AXES)) + return NULL; + + *count = js->axisCount; + return js->axes; +} + +GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + assert(count != NULL); + + *count = 0; + + _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_BUTTONS)) + return NULL; + + if (_glfw.hints.init.hatButtons) + *count = js->buttonCount + js->hatCount * 4; + else + *count = js->buttonCount; + + return js->buttons; +} + +GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + assert(count != NULL); + + *count = 0; + + _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_BUTTONS)) + return NULL; + + *count = js->hatCount; + return js->hats; +} + +GLFWAPI const char* glfwGetJoystickName(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->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 void glfwSetJoystickUserPointer(int jid, void* pointer) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + + _GLFW_REQUIRE_INIT(); + + js = _glfw.joysticks + jid; + if (!js->present) + return; + + js->userPointer = pointer; +} + +GLFWAPI void* glfwGetJoystickUserPointer(int jid) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + js = _glfw.joysticks + jid; + if (!js->present) + return NULL; + + return js->userPointer; +} + +GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(_glfw.callbacks.joystick, 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 ((*c >= '0' && *c <= '9') || + (*c >= 'a' && *c <= 'f') || + (*c >= 'A' && *c <= 'F')) + { + 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++) + { + const _GLFWmapelement* e = js->mapping->buttons + i; + if (e->type == _GLFW_JOYSTICK_AXIS) + { + const float value = js->axes[e->index] * e->axisScale + e->axisOffset; + // HACK: This should be baked into the value transform + // TODO: Bake into transform when implementing output modifiers + if (e->axisOffset < 0 || (e->axisOffset == 0 && e->axisScale > 0)) + { + if (value >= 0.f) + state->buttons[i] = GLFW_PRESS; + } + else + { + if (value <= 0.f) + state->buttons[i] = GLFW_PRESS; + } + } + else if (e->type == _GLFW_JOYSTICK_HATBIT) + { + const unsigned int hat = e->index >> 4; + const unsigned int bit = e->index & 0xf; + if (js->hats[hat] & bit) + state->buttons[i] = GLFW_PRESS; + } + else if (e->type == _GLFW_JOYSTICK_BUTTON) + state->buttons[i] = js->buttons[e->index]; + } + + for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++) + { + const _GLFWmapelement* e = js->mapping->axes + i; + if (e->type == _GLFW_JOYSTICK_AXIS) + { + const float value = js->axes[e->index] * e->axisScale + e->axisOffset; + state->axes[i] = _glfw_fminf(_glfw_fmaxf(value, -1.f), 1.f); + } + else if (e->type == _GLFW_JOYSTICK_HATBIT) + { + const unsigned int hat = e->index >> 4; + const unsigned int bit = e->index & 0xf; + if (js->hats[hat] & bit) + state->axes[i] = 1.f; + else + state->axes[i] = -1.f; + } + else if (e->type == _GLFW_JOYSTICK_BUTTON) + state->axes[i] = js->buttons[e->index] * 2.f - 1.f; + } + + return GLFW_TRUE; +} + +GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string) +{ + assert(string != NULL); + + _GLFW_REQUIRE_INIT(); + _glfwPlatformSetClipboardString(string); +} + +GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return _glfwPlatformGetClipboardString(); +} + +GLFWAPI double glfwGetTime(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(0.0); + return (double) (_glfwPlatformGetTimerValue() - _glfw.timer.offset) / + _glfwPlatformGetTimerFrequency(); +} + +GLFWAPI void glfwSetTime(double time) +{ + _GLFW_REQUIRE_INIT(); + + if (time != time || time < 0.0 || time > 18446744073.0) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid time %f", time); + return; + } + + _glfw.timer.offset = _glfwPlatformGetTimerValue() - + (uint64_t) (time * _glfwPlatformGetTimerFrequency()); +} + +GLFWAPI uint64_t glfwGetTimerValue(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(0); + return _glfwPlatformGetTimerValue(); +} + +GLFWAPI uint64_t glfwGetTimerFrequency(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(0); + return _glfwPlatformGetTimerFrequency(); +} diff --git a/source/MaterialXGraphEditor/External/Glfw/src/internal.h b/source/MaterialXGraphEditor/External/Glfw/src/internal.h new file mode 100644 index 0000000000..6d7587c8c4 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/internal.h @@ -0,0 +1,781 @@ +//======================================================================== +// GLFW 3.4 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#pragma once + +#if defined(_GLFW_USE_CONFIG_H) + #include "glfw_config.h" +#endif + +#if defined(GLFW_INCLUDE_GLCOREARB) || \ + defined(GLFW_INCLUDE_ES1) || \ + defined(GLFW_INCLUDE_ES2) || \ + defined(GLFW_INCLUDE_ES3) || \ + defined(GLFW_INCLUDE_ES31) || \ + defined(GLFW_INCLUDE_ES32) || \ + defined(GLFW_INCLUDE_NONE) || \ + defined(GLFW_INCLUDE_GLEXT) || \ + defined(GLFW_INCLUDE_GLU) || \ + defined(GLFW_INCLUDE_VULKAN) || \ + defined(GLFW_DLL) + #error "You must not define any header option macros when compiling GLFW" +#endif + +#define GLFW_INCLUDE_NONE +#include "../include/GLFW/glfw3.h" + +#define _GLFW_INSERT_FIRST 0 +#define _GLFW_INSERT_LAST 1 + +#define _GLFW_POLL_PRESENCE 0 +#define _GLFW_POLL_AXES 1 +#define _GLFW_POLL_BUTTONS 2 +#define _GLFW_POLL_ALL (_GLFW_POLL_AXES | _GLFW_POLL_BUTTONS) + +#define _GLFW_MESSAGE_SIZE 1024 + +typedef int GLFWbool; + +typedef struct _GLFWerror _GLFWerror; +typedef struct _GLFWinitconfig _GLFWinitconfig; +typedef struct _GLFWwndconfig _GLFWwndconfig; +typedef struct _GLFWctxconfig _GLFWctxconfig; +typedef struct _GLFWfbconfig _GLFWfbconfig; +typedef struct _GLFWcontext _GLFWcontext; +typedef struct _GLFWwindow _GLFWwindow; +typedef struct _GLFWlibrary _GLFWlibrary; +typedef struct _GLFWmonitor _GLFWmonitor; +typedef struct _GLFWcursor _GLFWcursor; +typedef struct _GLFWmapelement _GLFWmapelement; +typedef struct _GLFWmapping _GLFWmapping; +typedef struct _GLFWjoystick _GLFWjoystick; +typedef struct _GLFWtls _GLFWtls; +typedef struct _GLFWmutex _GLFWmutex; + +typedef void (* _GLFWmakecontextcurrentfun)(_GLFWwindow*); +typedef void (* _GLFWswapbuffersfun)(_GLFWwindow*); +typedef void (* _GLFWswapintervalfun)(int); +typedef int (* _GLFWextensionsupportedfun)(const char*); +typedef GLFWglproc (* _GLFWgetprocaddressfun)(const char*); +typedef void (* _GLFWdestroycontextfun)(_GLFWwindow*); + +#define GL_VERSION 0x1f02 +#define GL_NONE 0 +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_EXTENSIONS 0x1f03 +#define GL_NUM_EXTENSIONS 0x821d +#define GL_CONTEXT_FLAGS 0x821e +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 +#define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define GL_NO_RESET_NOTIFICATION_ARB 0x8261 +#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82fb +#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82fc +#define GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR 0x00000008 + +typedef int GLint; +typedef unsigned int GLuint; +typedef unsigned int GLenum; +typedef unsigned int GLbitfield; +typedef unsigned char GLubyte; + +typedef void (APIENTRY * PFNGLCLEARPROC)(GLbitfield); +typedef const GLubyte* (APIENTRY * PFNGLGETSTRINGPROC)(GLenum); +typedef void (APIENTRY * PFNGLGETINTEGERVPROC)(GLenum,GLint*); +typedef const GLubyte* (APIENTRY * PFNGLGETSTRINGIPROC)(GLenum,GLuint); + +#define VK_NULL_HANDLE 0 + +typedef void* VkInstance; +typedef void* VkPhysicalDevice; +typedef uint64_t VkSurfaceKHR; +typedef uint32_t VkFlags; +typedef uint32_t VkBool32; + +typedef enum VkStructureType +{ + VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR = 1000004000, + VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR = 1000005000, + VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000, + VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000, + VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000123000, + VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT = 1000217000, + VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkStructureType; + +typedef enum VkResult +{ + VK_SUCCESS = 0, + VK_NOT_READY = 1, + VK_TIMEOUT = 2, + VK_EVENT_SET = 3, + VK_EVENT_RESET = 4, + VK_INCOMPLETE = 5, + VK_ERROR_OUT_OF_HOST_MEMORY = -1, + VK_ERROR_OUT_OF_DEVICE_MEMORY = -2, + VK_ERROR_INITIALIZATION_FAILED = -3, + VK_ERROR_DEVICE_LOST = -4, + VK_ERROR_MEMORY_MAP_FAILED = -5, + VK_ERROR_LAYER_NOT_PRESENT = -6, + VK_ERROR_EXTENSION_NOT_PRESENT = -7, + VK_ERROR_FEATURE_NOT_PRESENT = -8, + VK_ERROR_INCOMPATIBLE_DRIVER = -9, + VK_ERROR_TOO_MANY_OBJECTS = -10, + VK_ERROR_FORMAT_NOT_SUPPORTED = -11, + VK_ERROR_SURFACE_LOST_KHR = -1000000000, + VK_SUBOPTIMAL_KHR = 1000001003, + VK_ERROR_OUT_OF_DATE_KHR = -1000001004, + VK_ERROR_INCOMPATIBLE_DISPLAY_KHR = -1000003001, + VK_ERROR_NATIVE_WINDOW_IN_USE_KHR = -1000000001, + VK_ERROR_VALIDATION_FAILED_EXT = -1000011001, + VK_RESULT_MAX_ENUM = 0x7FFFFFFF +} VkResult; + +typedef struct VkAllocationCallbacks VkAllocationCallbacks; + +typedef struct VkExtensionProperties +{ + char extensionName[256]; + uint32_t specVersion; +} VkExtensionProperties; + +typedef void (APIENTRY * PFN_vkVoidFunction)(void); + +#if defined(_GLFW_VULKAN_STATIC) + PFN_vkVoidFunction vkGetInstanceProcAddr(VkInstance,const char*); + VkResult vkEnumerateInstanceExtensionProperties(const char*,uint32_t*,VkExtensionProperties*); +#else + typedef PFN_vkVoidFunction (APIENTRY * PFN_vkGetInstanceProcAddr)(VkInstance,const char*); + typedef VkResult (APIENTRY * PFN_vkEnumerateInstanceExtensionProperties)(const char*,uint32_t*,VkExtensionProperties*); + #define vkEnumerateInstanceExtensionProperties _glfw.vk.EnumerateInstanceExtensionProperties + #define vkGetInstanceProcAddr _glfw.vk.GetInstanceProcAddr +#endif + +#if defined(_GLFW_COCOA) + #include "cocoa_platform.h" +#elif defined(_GLFW_WIN32) + #include "win32_platform.h" +#elif defined(_GLFW_X11) + #include "x11_platform.h" +#elif defined(_GLFW_WAYLAND) + #include "wl_platform.h" +#elif defined(_GLFW_OSMESA) + #include "null_platform.h" +#else + #error "No supported window creation API selected" +#endif + +// Constructs a version number string from the public header macros +#define _GLFW_CONCAT_VERSION(m, n, r) #m "." #n "." #r +#define _GLFW_MAKE_VERSION(m, n, r) _GLFW_CONCAT_VERSION(m, n, r) +#define _GLFW_VERSION_NUMBER _GLFW_MAKE_VERSION(GLFW_VERSION_MAJOR, \ + GLFW_VERSION_MINOR, \ + GLFW_VERSION_REVISION) + +// Checks for whether the library has been initialized +#define _GLFW_REQUIRE_INIT() \ + if (!_glfw.initialized) \ + { \ + _glfwInputError(GLFW_NOT_INITIALIZED, NULL); \ + return; \ + } +#define _GLFW_REQUIRE_INIT_OR_RETURN(x) \ + if (!_glfw.initialized) \ + { \ + _glfwInputError(GLFW_NOT_INITIALIZED, NULL); \ + return x; \ + } + +// Swaps the provided pointers +#define _GLFW_SWAP_POINTERS(x, y) \ + { \ + void* t; \ + t = x; \ + x = y; \ + y = t; \ + } + +// Per-thread error structure +// +struct _GLFWerror +{ + _GLFWerror* next; + int code; + char description[_GLFW_MESSAGE_SIZE]; +}; + +// Initialization configuration +// +// Parameters relating to the initialization of the library +// +struct _GLFWinitconfig +{ + GLFWbool hatButtons; + struct { + GLFWbool menubar; + GLFWbool chdir; + } ns; +}; + +// Window configuration +// +// Parameters relating to the creation of the window but not directly related +// to the framebuffer. This is used to pass window creation parameters from +// shared code to the platform API. +// +struct _GLFWwndconfig +{ + int width; + int height; + const char* title; + GLFWbool resizable; + GLFWbool visible; + GLFWbool decorated; + GLFWbool focused; + GLFWbool autoIconify; + GLFWbool floating; + GLFWbool maximized; + GLFWbool centerCursor; + GLFWbool focusOnShow; + GLFWbool scaleToMonitor; + struct { + GLFWbool retina; + char frameName[256]; + } ns; + struct { + char className[256]; + char instanceName[256]; + } x11; + struct { + GLFWbool keymenu; + } win32; +}; + +// Context configuration +// +// Parameters relating to the creation of the context but not directly related +// to the framebuffer. This is used to pass context creation parameters from +// shared code to the platform API. +// +struct _GLFWctxconfig +{ + int client; + int source; + int major; + int minor; + GLFWbool forward; + GLFWbool debug; + GLFWbool noerror; + int profile; + int robustness; + int release; + _GLFWwindow* share; + struct { + GLFWbool offline; + } nsgl; +}; + +// Framebuffer configuration +// +// This describes buffers and their sizes. It also contains +// a platform-specific ID used to map back to the backend API object. +// +// It is used to pass framebuffer parameters from shared code to the platform +// API and also to enumerate and select available framebuffer configs. +// +struct _GLFWfbconfig +{ + int redBits; + int greenBits; + int blueBits; + int alphaBits; + int depthBits; + int stencilBits; + int accumRedBits; + int accumGreenBits; + int accumBlueBits; + int accumAlphaBits; + int auxBuffers; + GLFWbool stereo; + int samples; + GLFWbool sRGB; + GLFWbool doublebuffer; + GLFWbool transparent; + uintptr_t handle; +}; + +// Context structure +// +struct _GLFWcontext +{ + int client; + int source; + int major, minor, revision; + GLFWbool forward, debug, noerror; + int profile; + int robustness; + int release; + + PFNGLGETSTRINGIPROC GetStringi; + PFNGLGETINTEGERVPROC GetIntegerv; + PFNGLGETSTRINGPROC GetString; + + _GLFWmakecontextcurrentfun makeCurrent; + _GLFWswapbuffersfun swapBuffers; + _GLFWswapintervalfun swapInterval; + _GLFWextensionsupportedfun extensionSupported; + _GLFWgetprocaddressfun getProcAddress; + _GLFWdestroycontextfun destroy; + + // This is defined in the context API's context.h + _GLFW_PLATFORM_CONTEXT_STATE; + // This is defined in egl_context.h + _GLFW_EGL_CONTEXT_STATE; + // This is defined in osmesa_context.h + _GLFW_OSMESA_CONTEXT_STATE; +}; + +// Window and context structure +// +struct _GLFWwindow +{ + struct _GLFWwindow* next; + + // Window settings and state + GLFWbool resizable; + GLFWbool decorated; + GLFWbool autoIconify; + GLFWbool floating; + GLFWbool focusOnShow; + GLFWbool shouldClose; + void* userPointer; + GLFWvidmode videoMode; + _GLFWmonitor* monitor; + _GLFWcursor* cursor; + + int minwidth, minheight; + int maxwidth, maxheight; + int numer, denom; + + GLFWbool stickyKeys; + GLFWbool stickyMouseButtons; + GLFWbool lockKeyMods; + int cursorMode; + char mouseButtons[GLFW_MOUSE_BUTTON_LAST + 1]; + char keys[GLFW_KEY_LAST + 1]; + // Virtual cursor position when cursor is disabled + double virtualCursorPosX, virtualCursorPosY; + GLFWbool rawMouseMotion; + + _GLFWcontext context; + + struct { + GLFWwindowposfun pos; + GLFWwindowsizefun size; + GLFWwindowclosefun close; + GLFWwindowrefreshfun refresh; + GLFWwindowfocusfun focus; + GLFWwindowiconifyfun iconify; + GLFWwindowmaximizefun maximize; + GLFWframebuffersizefun fbsize; + GLFWwindowcontentscalefun scale; + GLFWmousebuttonfun mouseButton; + GLFWcursorposfun cursorPos; + GLFWcursorenterfun cursorEnter; + GLFWscrollfun scroll; + GLFWkeyfun key; + GLFWcharfun character; + GLFWcharmodsfun charmods; + GLFWdropfun drop; + } callbacks; + + // This is defined in the window API's platform.h + _GLFW_PLATFORM_WINDOW_STATE; +}; + +// Monitor structure +// +struct _GLFWmonitor +{ + char* name; + void* userPointer; + + // Physical dimensions in millimeters. + int widthMM, heightMM; + + // The window whose video mode is current on this monitor + _GLFWwindow* window; + + GLFWvidmode* modes; + int modeCount; + GLFWvidmode currentMode; + + GLFWgammaramp originalRamp; + GLFWgammaramp currentRamp; + + // This is defined in the window API's platform.h + _GLFW_PLATFORM_MONITOR_STATE; +}; + +// Cursor structure +// +struct _GLFWcursor +{ + _GLFWcursor* next; + + // This is defined in the window API's platform.h + _GLFW_PLATFORM_CURSOR_STATE; +}; + +// Gamepad mapping element structure +// +struct _GLFWmapelement +{ + uint8_t type; + uint8_t index; + int8_t axisScale; + int8_t axisOffset; +}; + +// Gamepad mapping structure +// +struct _GLFWmapping +{ + char name[128]; + char guid[33]; + _GLFWmapelement buttons[15]; + _GLFWmapelement axes[6]; +}; + +// Joystick structure +// +struct _GLFWjoystick +{ + GLFWbool present; + float* axes; + int axisCount; + unsigned char* buttons; + int buttonCount; + unsigned char* hats; + int hatCount; + char* name; + void* userPointer; + char guid[33]; + _GLFWmapping* mapping; + + // This is defined in the joystick API's joystick.h + _GLFW_PLATFORM_JOYSTICK_STATE; +}; + +// Thread local storage structure +// +struct _GLFWtls +{ + // This is defined in the platform's thread.h + _GLFW_PLATFORM_TLS_STATE; +}; + +// Mutex structure +// +struct _GLFWmutex +{ + // This is defined in the platform's thread.h + _GLFW_PLATFORM_MUTEX_STATE; +}; + +// Library global data +// +struct _GLFWlibrary +{ + GLFWbool initialized; + + struct { + _GLFWinitconfig init; + _GLFWfbconfig framebuffer; + _GLFWwndconfig window; + _GLFWctxconfig context; + int refreshRate; + } hints; + + _GLFWerror* errorListHead; + _GLFWcursor* cursorListHead; + _GLFWwindow* windowListHead; + + _GLFWmonitor** monitors; + int monitorCount; + + _GLFWjoystick joysticks[GLFW_JOYSTICK_LAST + 1]; + _GLFWmapping* mappings; + int mappingCount; + + _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 { + GLFWbool available; + void* handle; + char* extensions[2]; +#if !defined(_GLFW_VULKAN_STATIC) + PFN_vkEnumerateInstanceExtensionProperties EnumerateInstanceExtensionProperties; + PFN_vkGetInstanceProcAddr GetInstanceProcAddr; +#endif + GLFWbool KHR_surface; +#if defined(_GLFW_WIN32) + GLFWbool KHR_win32_surface; +#elif defined(_GLFW_COCOA) + GLFWbool MVK_macos_surface; + GLFWbool EXT_metal_surface; +#elif defined(_GLFW_X11) + GLFWbool KHR_xlib_surface; + GLFWbool KHR_xcb_surface; +#elif defined(_GLFW_WAYLAND) + GLFWbool KHR_wayland_surface; +#endif + } vk; + + struct { + GLFWmonitorfun monitor; + GLFWjoystickfun joystick; + } callbacks; + + // This is defined in the window API's platform.h + _GLFW_PLATFORM_LIBRARY_WINDOW_STATE; + // This is defined in the context API's context.h + _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE; + // This is defined in the platform's joystick.h + _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE; + // This is defined in egl_context.h + _GLFW_EGL_LIBRARY_CONTEXT_STATE; + // This is defined in osmesa_context.h + _GLFW_OSMESA_LIBRARY_CONTEXT_STATE; +}; + +// Global state shared between compilation units of GLFW +// +extern _GLFWlibrary _glfw; + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformInit(void); +void _glfwPlatformTerminate(void); +const char* _glfwPlatformGetVersionString(void); + +void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos); +void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos); +void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode); +void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled); +GLFWbool _glfwPlatformRawMouseMotionSupported(void); +int _glfwPlatformCreateCursor(_GLFWcursor* cursor, + const GLFWimage* image, int xhot, int yhot); +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape); +void _glfwPlatformDestroyCursor(_GLFWcursor* cursor); +void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor); + +const char* _glfwPlatformGetScancodeName(int scancode); +int _glfwPlatformGetKeyScancode(int key); + +void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor); +void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos); +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale); +void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int *width, int *height); +GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count); +void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode); +GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp); +void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp); + +void _glfwPlatformSetClipboardString(const char* string); +const char* _glfwPlatformGetClipboardString(void); + +int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode); +void _glfwPlatformUpdateGamepadGUID(char* guid); + +uint64_t _glfwPlatformGetTimerValue(void); +uint64_t _glfwPlatformGetTimerFrequency(void); + +int _glfwPlatformCreateWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig); +void _glfwPlatformDestroyWindow(_GLFWwindow* window); +void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title); +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, + int count, const GLFWimage* images); +void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos); +void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos); +void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height); +void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height); +void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, + int minwidth, int minheight, + int maxwidth, int maxheight); +void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom); +void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height); +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 _glfwPlatformRestoreWindow(_GLFWwindow* window); +void _glfwPlatformMaximizeWindow(_GLFWwindow* window); +void _glfwPlatformShowWindow(_GLFWwindow* window); +void _glfwPlatformHideWindow(_GLFWwindow* window); +void _glfwPlatformRequestWindowAttention(_GLFWwindow* window); +void _glfwPlatformFocusWindow(_GLFWwindow* window); +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, + int xpos, int ypos, int width, int height, + int refreshRate); +int _glfwPlatformWindowFocused(_GLFWwindow* window); +int _glfwPlatformWindowIconified(_GLFWwindow* window); +int _glfwPlatformWindowVisible(_GLFWwindow* window); +int _glfwPlatformWindowMaximized(_GLFWwindow* window); +int _glfwPlatformWindowHovered(_GLFWwindow* window); +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window); +float _glfwPlatformGetWindowOpacity(_GLFWwindow* window); +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled); +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled); +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled); +void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity); + +void _glfwPlatformPollEvents(void); +void _glfwPlatformWaitEvents(void); +void _glfwPlatformWaitEventsTimeout(double timeout); +void _glfwPlatformPostEmptyEvent(void); + +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions); +int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily); +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); + + +////////////////////////////////////////////////////////////////////////// +////// GLFW event API ////// +////////////////////////////////////////////////////////////////////////// + +void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused); +void _glfwInputWindowPos(_GLFWwindow* window, int xpos, int ypos); +void _glfwInputWindowSize(_GLFWwindow* window, int width, int height); +void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height); +void _glfwInputWindowContentScale(_GLFWwindow* window, + float xscale, float yscale); +void _glfwInputWindowIconify(_GLFWwindow* window, GLFWbool iconified); +void _glfwInputWindowMaximize(_GLFWwindow* window, GLFWbool maximized); +void _glfwInputWindowDamage(_GLFWwindow* window); +void _glfwInputWindowCloseRequest(_GLFWwindow* window); +void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor); + +void _glfwInputKey(_GLFWwindow* window, + int key, int scancode, int action, int mods); +void _glfwInputChar(_GLFWwindow* window, + unsigned int codepoint, int mods, GLFWbool plain); +void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset); +void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods); +void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos); +void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered); +void _glfwInputDrop(_GLFWwindow* window, int count, const char** names); +void _glfwInputJoystick(_GLFWjoystick* js, int event); +void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value); +void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value); +void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value); + +void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement); +void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window); + +#if defined(__GNUC__) +void _glfwInputError(int code, const char* format, ...) + __attribute__((format(printf, 2, 3))); +#else +void _glfwInputError(int code, const char* format, ...); +#endif + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions); +const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired, + const _GLFWfbconfig* alternatives, + unsigned int count); +GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig); +GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig); + +const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor, + const GLFWvidmode* desired); +int _glfwCompareVideoModes(const GLFWvidmode* first, const GLFWvidmode* second); +_GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM); +void _glfwFreeMonitor(_GLFWmonitor* monitor); +void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size); +void _glfwFreeGammaArrays(GLFWgammaramp* ramp); +void _glfwSplitBPP(int bpp, int* red, int* green, int* blue); + +_GLFWjoystick* _glfwAllocJoystick(const char* name, + const char* guid, + int axisCount, + int buttonCount, + int hatCount); +void _glfwFreeJoystick(_GLFWjoystick* js); +void _glfwCenterCursorInContentArea(_GLFWwindow* window); + +GLFWbool _glfwInitVulkan(int mode); +void _glfwTerminateVulkan(void); +const char* _glfwGetVulkanResultString(VkResult result); + +char* _glfw_strdup(const char* source); +float _glfw_fminf(float a, float b); +float _glfw_fmaxf(float a, float b); + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/linux_joystick.c b/source/MaterialXGraphEditor/External/Glfw/src/linux_joystick.c new file mode 100644 index 0000000000..1f9b35fe04 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/linux_joystick.c @@ -0,0 +1,433 @@ +//======================================================================== +// GLFW 3.4 Linux - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#include "internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SYN_DROPPED // < v2.6.39 kernel headers +// Workaround for CentOS-6, which is supported till 2020-11-30, but still on v2.6.32 +#define SYN_DROPPED 3 +#endif + +// 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) +{ + for (int 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 +// +static GLFWbool openJoystickDevice(const char* path) +{ + for (int jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (!_glfw.joysticks[jid].present) + continue; + if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0) + return GLFW_FALSE; + } + + _GLFWjoystickLinux linjs = {0}; + linjs.fd = open(path, O_RDONLY | O_NONBLOCK); + if (linjs.fd == -1) + return GLFW_FALSE; + + char evBits[(EV_CNT + 7) / 8] = {0}; + char keyBits[(KEY_CNT + 7) / 8] = {0}; + char absBits[(ABS_CNT + 7) / 8] = {0}; + struct input_id id; + + if (ioctl(linjs.fd, EVIOCGBIT(0, sizeof(evBits)), evBits) < 0 || + ioctl(linjs.fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits) < 0 || + ioctl(linjs.fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits) < 0 || + ioctl(linjs.fd, EVIOCGID, &id) < 0) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Linux: Failed to query input device: %s", + strerror(errno)); + close(linjs.fd); + return GLFW_FALSE; + } + + // 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; + } + + char name[256] = ""; + + if (ioctl(linjs.fd, EVIOCGNAME(sizeof(name)), name) < 0) + strncpy(name, "Unknown", sizeof(name)); + + char guid[33] = ""; + + // Generate a joystick GUID that matches the SDL 2.0.5+ one + 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]); + } + + int axisCount = 0, buttonCount = 0, hatCount = 0; + + for (int code = BTN_MISC; code < KEY_CNT; code++) + { + if (!isBitSet(code, keyBits)) + continue; + + linjs.keyMap[code - BTN_MISC] = buttonCount; + buttonCount++; + } + + for (int 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++; + } + } + + _GLFWjoystick* js = + _glfwAllocJoystick(name, guid, axisCount, buttonCount, hatCount); + if (!js) + { + close(linjs.fd); + return GLFW_FALSE; + } + + strncpy(linjs.path, path, sizeof(linjs.path) - 1); + memcpy(&js->linjs, &linjs, sizeof(linjs)); + + pollAbsState(js); + + _glfwInputJoystick(js, GLFW_CONNECTED); + return GLFW_TRUE; +} + +#undef isBitSet + +// Frees all resources associated with the specified joystick +// +static void closeJoystick(_GLFWjoystick* js) +{ + close(js->linjs.fd); + _glfwFreeJoystick(js); + _glfwInputJoystick(js, GLFW_DISCONNECTED); +} + +// Lexically compare joysticks by name; used by qsort +// +static int compareJoysticks(const void* fp, const void* sp) +{ + const _GLFWjoystick* fj = fp; + const _GLFWjoystick* sj = sp; + return strcmp(fj->linjs.path, sj->linjs.path); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialize joystick interface +// +GLFWbool _glfwInitJoysticksLinux(void) +{ + const char* dirname = "/dev/input"; + + _glfw.linjs.inotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (_glfw.linjs.inotify > 0) + { + // HACK: Register for IN_ATTRIB to get notified when udev is done + // This works well in practice but the true way is libudev + + _glfw.linjs.watch = inotify_add_watch(_glfw.linjs.inotify, + dirname, + IN_CREATE | IN_ATTRIB | IN_DELETE); + } + + // 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"); + return GLFW_FALSE; + } + + int count = 0; + + DIR* dir = opendir(dirname); + if (dir) + { + struct dirent* entry; + + while ((entry = readdir(dir))) + { + regmatch_t match; + + if (regexec(&_glfw.linjs.regex, entry->d_name, 1, &match, 0) != 0) + continue; + + char path[PATH_MAX]; + + snprintf(path, sizeof(path), "%s/%s", dirname, entry->d_name); + + if (openJoystickDevice(path)) + count++; + } + + closedir(dir); + } + + // Continue with no joysticks if enumeration fails + + qsort(_glfw.joysticks, count, sizeof(_GLFWjoystick), compareJoysticks); + return GLFW_TRUE; +} + +// Close all opened joystick handles +// +void _glfwTerminateJoysticksLinux(void) +{ + int jid; + + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + _GLFWjoystick* js = _glfw.joysticks + jid; + if (js->present) + closeJoystick(js); + } + + regfree(&_glfw.linjs.regex); + + if (_glfw.linjs.inotify > 0) + { + if (_glfw.linjs.watch > 0) + inotify_rm_watch(_glfw.linjs.inotify, _glfw.linjs.watch); + + close(_glfw.linjs.inotify); + } +} + +void _glfwDetectJoystickConnectionLinux(void) +{ + if (_glfw.linjs.inotify <= 0) + return; + + ssize_t offset = 0; + char buffer[16384]; + const ssize_t size = read(_glfw.linjs.inotify, buffer, sizeof(buffer)); + + while (size > offset) + { + regmatch_t match; + const struct inotify_event* e = (struct inotify_event*) (buffer + offset); + + offset += sizeof(struct inotify_event) + e->len; + + 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); + + if (e->mask & (IN_CREATE | IN_ATTRIB)) + openJoystickDevice(path); + else if (e->mask & IN_DELETE) + { + for (int jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0) + { + closeJoystick(_glfw.joysticks + jid); + break; + } + } + } + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) +{ + // Read all queued events (non-blocking) + for (;;) + { + struct input_event e; + + errno = 0; + if (read(js->linjs.fd, &e, sizeof(e)) < 0) + { + // Reset the joystick slot if the device was disconnected + if (errno == ENODEV) + closeJoystick(js); + + break; + } + + if (e.type == EV_SYN) + { + if (e.code == SYN_DROPPED) + _glfw.linjs.dropped = GLFW_TRUE; + else if (e.code == SYN_REPORT) + { + _glfw.linjs.dropped = GLFW_FALSE; + pollAbsState(js); + } + } + + if (_glfw.linjs.dropped) + continue; + + 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; +} + +void _glfwPlatformUpdateGamepadGUID(char* guid) +{ +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/linux_joystick.h b/source/MaterialXGraphEditor/External/Glfw/src/linux_joystick.h new file mode 100644 index 0000000000..7373f13037 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/linux_joystick.h @@ -0,0 +1,62 @@ +//======================================================================== +// GLFW 3.4 Linux - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2014 Jonas Ã…dahl +// +// 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. +// +//======================================================================== + +#include +#include +#include + +#define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickLinux linjs +#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE _GLFWlibraryLinux linjs + +#define _GLFW_PLATFORM_MAPPING_NAME "Linux" + +// Linux-specific joystick data +// +typedef struct _GLFWjoystickLinux +{ + int fd; + 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; + +// Linux-specific joystick API data +// +typedef struct _GLFWlibraryLinux +{ + int inotify; + int watch; + regex_t regex; + GLFWbool dropped; +} _GLFWlibraryLinux; + + +GLFWbool _glfwInitJoysticksLinux(void); +void _glfwTerminateJoysticksLinux(void); +void _glfwDetectJoystickConnectionLinux(void); + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/mappings.h b/source/MaterialXGraphEditor/External/Glfw/src/mappings.h new file mode 100644 index 0000000000..eda9a54963 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/mappings.h @@ -0,0 +1,476 @@ +//======================================================================== +// GLFW 3.4 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2006-2018 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// As mappings.h.in, this file is used by CMake to produce the mappings.h +// header file. If you are adding a GLFW specific gamepad mapping, this is +// where to put it. +//======================================================================== +// As mappings.h, this provides all pre-defined gamepad mappings, including +// all available in SDL_GameControllerDB. Do not edit this file. Any gamepad +// mappings not specific to GLFW should be submitted to SDL_GameControllerDB. +// This file can be re-generated from mappings.h.in and the upstream +// gamecontrollerdb.txt with the GenerateMappings.cmake script. +//======================================================================== + +// All gamepad mappings not labeled GLFW are copied from the +// SDL_GameControllerDB project under the following license: +// +// Simple DirectMedia Layer +// Copyright (C) 1997-2013 Sam Lantinga +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the +// use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. + +const char* _glfwDefaultMappings[] = +{ +"03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows,", +"03000000022000000090000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", +"03000000203800000900000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", +"03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", +"03000000a00500003232000000000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,", +"030000008f0e00001200000000000000,Acme,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,", +"03000000341a00003608000000000000,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,", +"03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows,", +"030000006b1400000055000000000000,bigben ps3padstreetnew,a:b0,b:b1,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:b2,y:b3,platform:Windows,", +"0300000066f700000500000000000000,BrutalLegendTest,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:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", +"03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows,", +"03000000e82000006058000000000000,Cideko AK08b,a:b2,b:b1,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:b3,y:b0,platform:Windows,", +"030000005e0400008e02000000000000,Controller (XBOX 360 For Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000260900008888000000000000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a4,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Windows,", +"03000000a306000022f6000000000000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000791d00000103000000000000,Dual Box WII,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"030000004f04000023b3000000000000,Dual Trigger 3-in-1,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:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000341a00000108000000000000,EXEQ RF USB Gamepad 8206,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"030000000d0f00008500000000000000,Fighting Commander 2016 PS3,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,", +"030000000d0f00008400000000000000,Fighting Commander 5,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:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00008800000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,", +"030000000d0f00008700000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"78696e70757403000000000000000000,Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows,", +"03000000260900002625000000000000,Gamecube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows,", +"030000008f0e00000d31000000000000,GAMEPAD 3 TURBO,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,", +"03000000280400000140000000000000,GamePad Pro USB,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"03000000ffff00000000000000000000,GameStop Gamepad,a:b0,b:b1,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:b2,y:b3,platform:Windows,", +"03000000451300000010000000000000,Generic USB Joystick,a:b0,b:b1,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:b2,y:b3,platform:Windows,", +"03000000341a00000302000000000000,Hama Scorpad,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,", +"030000000d0f00004900000000000000,Hatsune Miku Sho 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,", +"03000000d81400000862000000000000,HitBox Edition Cthulhu+,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00005f00000000000000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00005e00000000000000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00004000000000000000,Hori Fighting Stick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00006e00000000000000,HORIPAD 4 (PS3),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,", +"030000000d0f00006600000000000000,HORIPAD 4 (PS4),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,", +"030000000d0f0000ee00000000000000,HORIPAD mini4,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:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00004d00000000000000,HORIPAD3 A,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,", +"03000000250900000017000000000000,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows,", +"030000008f0e00001330000000000000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Windows,", +"03000000d81d00000f00000000000000,iBUFFALO BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000d81d00001000000000000000,iBUFFALO BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000830500006020000000000000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Windows,", +"03000000b50700001403000000000000,IMPACT BLACK,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", +"030000006f0e00002401000000000000,INJUSTICE FightStick for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"03000000491900000204000000000000,Ipega PG-9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,platform:Windows,", +"030000006d04000016c2000000000000,Logitech Dual Action,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,", +"030000006d04000018c2000000000000,Logitech F510 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,", +"030000006d04000019c2000000000000,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,", +"03000000380700005032000000000000,Mad Catz FightPad PRO (PS3),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,", +"03000000380700005082000000000000,Mad Catz FightPad PRO (PS4),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,", +"03000000380700008433000000000000,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008483000000000000,Mad Catz FightStick TE S+ PS4,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:b6,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008134000000000000,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008184000000000000,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008532000000000000,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700003888000000000000,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700001888000000000000,MadCatz SFIV FightStick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000008305000031b0000000000000,MaxfireBlaze3,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,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,", +"03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows,", +"03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", +"03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,back:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b4,leftstick:b0,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", +"030000008f0e00001030000000000000,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,rightshoulder:b2,righttrigger:b7,start:b9,x:b3,y:b4,platform:Windows,", +"0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,", +"03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (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:Windows,", +"030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Windows,", +"03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,", +"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,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:b2,y:b3,platform:Windows,", +"030000004b120000014d000000000000,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,leftx:h0.6,lefty:h0.12,rightshoulder:b5,rightstick:a2,righttrigger:b7,rightx:h0.9,righty:h0.4,start:b9,x:b2,y:b3,platform:Windows,", +"03000000362800000100000000000000,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,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows,", +"03000000120c0000f60e000000000000,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,", +"030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", +"03000000d62000006dca000000000000,PowerA Pro Ex,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,", +"030000008f0e00007530000000000000,PS (R) Gamepad,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:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000e30500009605000000000000,PS to USB convert cable,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", +"03000000100800000100000000000000,PS1 USB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", +"03000000100800000300000000000000,PS2 USB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", +"03000000888800000803000000000000,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,", +"030000004c0500006802000000000000,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,", +"03000000250900000500000000000000,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,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,", +"03000000100000008200000000000000,PS360+ v1.66,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:h0.4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000004c050000a00b000000000000,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,", +"030000004c050000c405000000000000,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,", +"030000004c050000cc09000000000000,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,", +"03000000300f00000011000000000000,QanBa Arcade JoyStick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows,", +"03000000300f00001611000000000000,QanBa Arcade JoyStick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,", +"03000000222c00000020000000000000,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000300f00001210000000000000,QanBa Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,", +"03000000341a00000104000000000000,QanBa Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows,", +"03000000222c00000223000000000000,Qanba Obsidian Arcade Joystick PS3 Mode,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,", +"03000000222c00000023000000000000,Qanba Obsidian Arcade Joystick PS4 Mode,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:Windows,", +"03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000000d0f00001100000000000000,REAL ARCADE PRO.3,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,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00008b00000000000000,Real Arcade Pro.4,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,", +"030000000d0f00008a00000000000000,Real Arcade Pro.4,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,", +"030000000d0f00006b00000000000000,Real Arcade Pro.4,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,", +"030000000d0f00006a00000000000000,Real Arcade Pro.4,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,", +"030000000d0f00007000000000000000,REAL ARCADE PRO.4 VLX,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,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00002200000000000000,REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00005c00000000000000,Real Arcade Pro.V4,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,", +"030000000d0f00005b00000000000000,Real Arcade Pro.V4,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:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000790000001100000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,", +"0300000000f000000300000000000000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,", +"0300000000f00000f100000000000000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,", +"030000006b140000010d000000000000,Revolution Pro 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,", +"030000006f0e00001e01000000000000,Rock Candy Gamepad for PS3,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,", +"030000004f04000003d0000000000000,run'n'drive,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000a30600001af5000000000000,Saitek Cyborg,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:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000a306000023f6000000000000,Saitek Cyborg V.1 Game pad,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:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000300f00001201000000000000,Saitek Dual Analog Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", +"03000000a30600000cff000000000000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,x:b0,y:b1,platform:Windows,", +"03000000a30600000c04000000000000,Saitek P2900,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", +"03000000300f00001001000000000000,Saitek P480 Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", +"03000000a30600000b04000000000000,Saitek P990,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:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", +"03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,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:a3,righty:a2,start:b8,x:b0,y:b3,platform:Windows,", +"03000000300f00001101000000000000,saitek rumble pad,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", +"0300000000050000289b000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,", +"030000009b2800000500000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000341a00000208000000000000,SL-6555-SBK,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows,", +"030000008f0e00000800000000000000,SpeedLink Strike FX Wireless,a:b2,b:b1,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:b3,y:b0,platform:Windows,", +"03000000ff1100003133000000000000,SVEN X-PAD,a:b2,b:b3,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a4,start:b5,x:b0,y:b1,platform:Windows,", +"03000000fa1900000706000000000000,Team 5,a:b2,b:b1,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:b3,y:b0,platform:Windows,", +"03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", +"030000004f04000015b3000000000000,Thrustmaster Dual Analog 2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,", +"030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Windows,", +"030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,", +"03000000666600000488000000000000,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", +"03000000d90400000200000000000000,TwinShock PS2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", +"03000000380700006652000000000000,UnKnown,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:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000632500002305000000000000,USB Vibration Joystick (BM),a:b2,b:b1,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:b3,y:b0,platform:Windows,", +"03000000790000001b18000000000000,Venom Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"03000000450c00002043000000000000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,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:b2,y:b3,platform:Windows,", +"03000000172700004431000000000000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", +"03000000786901006e70000000000000,XInput 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:Windows,", +"03000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000022000000090000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000102800000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,", +"030000008305000031b0000000000000,Cideko AK08b,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,", +"03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X,", +"03000000a306000022f6000001030000,Cyborg V.3 Rumble Pad,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:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000ad1b000001f9000000000000,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,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,", +"030000000d0f00005f00000000010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00005e00000000010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00005f00000000000000,HORI Fighting Commander 4 PS3,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:Mac OS X,", +"030000000d0f00005e00000000000000,HORI Fighting Commander 4 PS4,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:Mac OS X,", +"030000000d0f00004d00000000000000,HORI Gem Pad 3,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:Mac OS X,", +"030000000d0f00006e00000000010000,HORIPAD 4 (PS3),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:Mac OS X,", +"030000000d0f00006600000000010000,HORIPAD 4 (PS4),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,", +"030000000d0f00006600000000000000,HORIPAD FPS PLUS 4,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:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000008f0e00001330000011010000,HuiJia SNES Controller,a:b4,b:b2,back:b16,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b12,rightshoulder:b14,start:b18,x:b6,y:b0,platform:Mac OS X,", +"03000000830500006020000000010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,", +"03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,", +"030000006d04000016c2000000020000,Logitech Dual Action,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,", +"030000006d04000016c2000000030000,Logitech Dual Action,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,", +"030000006d04000016c2000014040000,Logitech Dual Action,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,", +"030000006d04000016c2000000000000,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,", +"030000006d04000018c2000000000000,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,", +"030000006d0400001fc2000000000000,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,", +"030000006d04000019c2000000000000,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,", +"03000000380700005032000000010000,Mad Catz FightPad PRO (PS3),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:Mac OS X,", +"03000000380700005082000000010000,Mad Catz FightPad PRO (PS4),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,", +"03000000790000004418000000010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Mac OS X,", +"0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X,", +"03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,", +"03000000d8140000cecf000000000000,MC Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Mac OS X,", +"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,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:b2,y:b3,platform:Mac OS X,", +"030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X,", +"030000004c0500006802000000010000,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,", +"030000004c0500006802000000000000,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,", +"030000004c050000a00b000000010000,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,", +"030000004c050000cc09000000010000,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,", +"030000004c050000c405000000000000,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,", +"030000004c050000c405000000010000,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,", +"030000008916000000fd000000000000,Razer Onza TE,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,", +"03000000321500000010000000010000,Razer RAIJU,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,", +"0300000032150000030a000000000000,Razer Wildcat,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,", +"03000000790000001100000000000000,Retrolink Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a3,lefty:a4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", +"030000006b140000010d000000010000,Revolution Pro 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,", +"03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X,", +"03000000b40400000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X,", +"030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", +"030000004c050000cc09000000000000,Sony DualShock 4 V2,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:Mac OS X,", +"030000004c050000a00b000000000000,Sony DualShock 4 Wireless Adaptor,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:Mac OS X,", +"030000005e0400008e02000001000000,Steam Virtual GamePad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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,", +"03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X,", +"03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,", +"03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,", +"03000000110100001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,", +"03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,", +"030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X,", +"030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X,", +"03000000bd12000015d0000000010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000bd12000015d0000000000000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X,", +"050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X,", +"050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X,", +"030000005e0400008e02000000000000,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,", +"03000000c6240000045d000000000000,Xbox 360 Wired 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,", +"030000005e040000e302000000000000,Xbox One Wired 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,", +"030000005e040000d102000000000000,Xbox One Wired 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,", +"030000005e040000dd02000000000000,Xbox One Wired 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,", +"030000005e040000e002000000000000,Xbox 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:Mac OS X,", +"030000005e040000fd02000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"030000005e040000ea02000000000000,Xbox Wireless 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,", +"030000005e040000e002000003090000,Xbox 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:Mac OS X,", +"03000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000120c0000100e000000010000,ZEROPLUS P4 Gamepad,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,", +"05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"03000000022000000090000011010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"05000000c82d00002038000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"05000000c82d00003028000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", +"05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", +"05000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,", +"05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,", +"030000006f0e00003901000020060000,Afterglow Wired Controller for Xbox One,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,", +"03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,", +"03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux,", +"03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,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:b3,y:b0,platform:Linux,", +"03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Linux,", +"03000000a306000022f6000011010000,Cyborg V.3 Rumble Pad,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:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", +"03000000b40400000a01000000010000,CYPRESS USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,", +"03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,a:b2,b:b1,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:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux,", +"030000006f0e00003001000001010000,EA Sports 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:Linux,", +"03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,", +"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,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:b2,y:b3,platform:Linux,", +"030000006f0e00000104000000010000,Gamestop Logic3 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,", +"030000006f0e00001304000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:a0,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:a3,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006f0e00001f01000000010000,Generic X-Box pad,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,", +"03000000f0250000c183000010010000,Goodbetterbest Ltd USB 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:Linux,", +"03000000280400000140000000010000,Gravis GamePad Pro USB ,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"030000008f0e00000610000000010000,GreenAsia Electronics 4Axes 12Keys GamePad ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux,", +"030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", +"030000008f0e00000300000010010000,GreenAsia Inc. USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", +"0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,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,", +"06000000adde0000efbe000002010000,Hidromancer Game 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,", +"03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux,", +"03000000c9110000f055000011010000,HJC Game GAMEPAD,a:b0,b:b1,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:b2,y:b3,platform:Linux,", +"030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux,", +"030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00006a00000011010000,HORI CO. LTD. Real Arcade Pro.4,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:Linux,", +"030000000d0f00006b00000011010000,HORI CO. LTD. Real Arcade Pro.4,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:Linux,", +"030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00005f00000011010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00005e00000011010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,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,", +"030000000d0f00006e00000011010000,HORIPAD 4 (PS3),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:Linux,", +"030000000d0f00006600000011010000,HORIPAD 4 (PS4),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:Linux,", +"030000000d0f00006700000001010000,HORIPAD ONE,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,", +"030000008f0e00001330000010010000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Linux,", +"03000000830500006020000010010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,", +"050000006964726f69643a636f6e0000,idroid:con,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,", +"03000000b50700001503000010010000,impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,", +"03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux,", +"030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux,", +"03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,", +"03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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,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,", +"030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,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,", +"030000006d04000016c2000011010000,Logitech Dual Action,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,", +"030000006d04000016c2000010010000,Logitech Dual Action,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,", +"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,", +"030000006d04000015c2000010010000,Logitech Logitech Extreme 3D,a:b0,b:b4,back:b6,guide:b8,leftshoulder:b9,leftstick:h0.8,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:h0.2,start:b7,x:b2,y:b5,platform:Linux,", +"030000006d04000018c2000010010000,Logitech RumblePad 2,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,", +"030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,platform:Linux,", +"05000000380700006652000025010000,Mad Catz C.T.R.L.R ,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:Linux,", +"03000000380700005032000011010000,Mad Catz FightPad PRO (PS3),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:Linux,", +"03000000380700005082000011010000,Mad Catz FightPad PRO (PS4),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:Linux,", +"03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux,", +"03000000380700008034000011010000,Mad Catz fightstick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700008084000011010000,Mad Catz fightstick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700008433000011010000,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700008483000011010000,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700001647000010040000,Mad Catz Wired Xbox 360 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,", +"03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"03000000ad1b000016f0000090040000,Mad Catz Xbox 360 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,", +"03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,", +"03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", +"030000005e0400008e02000004010000,Microsoft X-Box 360 pad,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,", +"030000005e0400008e02000062230000,Microsoft X-Box 360 pad,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,", +"030000005e040000d102000001010000,Microsoft X-Box One pad,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,", +"030000005e040000d102000003020000,Microsoft X-Box One pad v2,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,", +"030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", +"030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", +"05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,", +"030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Linux,", +"050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,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:b2,y:b3,platform:Linux,", +"050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,", +"05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux,", +"03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", +"03000000451300000830000010010000,NYKO CORE,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:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000005e0400000202000000010000,Old Xbox pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,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,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,", +"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,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,", +"03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,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:b3,y:b0,platform:Linux,", +"030000006f0e00006401000001010000,PDP Battlefield One,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,", +"03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", +"030000004c0500006802000010010000,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,", +"050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"03000000341a00003608000011010000,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:Linux,", +"030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", +"030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,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,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,", +"05000000504c415953544154494f4e00,PS3 Controller (Bluetooth),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,", +"050000004c050000c405000000010000,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:Linux,", +"030000004c050000a00b000011010000,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:Linux,", +"050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"050000004c050000cc09000000010000,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:Linux,", +"030000004c050000cc09000011010000,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:Linux,", +"030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"030000004c050000c405000011010000,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:Linux,", +"03000000300f00001211000011010000,QanBa Arcade JoyStick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux,", +"030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux,", +"030000008916000001fd000024010000,Razer Onza Classic Edition,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,", +"030000008916000000fd000024010000,Razer Onza Tournament,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,", +"03000000321500000010000011010000,Razer RAIJU,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:Linux,", +"03000000c6240000045d000025010000,Razer Sabertooth,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,", +"03000000321500000009000011010000,Razer Serval,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:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", +"050000003215000000090000163a0000,Razer Serval,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:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", +"0300000032150000030a000001010000,Razer Wildcat,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,", +"03000000790000001100000010010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,", +"0300000000f000000300000000010000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,", +"0300000000f00000f100000000010000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,", +"030000006b140000010d000011010000,Revolution Pro 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:Linux,", +"030000006f0e00001e01000011010000,Rock Candy Gamepad for PS3,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:Linux,", +"030000006f0e00004601000001010000,Rock Candy Wired Controller for Xbox One,a:b0,b:b1,back:b6,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,", +"03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,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:a4,start:b9,x:b0,y:b3,platform:Linux,", +"03000000a30600000cff000010010000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,", +"03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux,", +"03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,", +"03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,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:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux,", +"03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", +"03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,a:b12,b:b10,back:b4,dpdown:b2,dpleft:b3,dpright:b1,dpup:b0,leftshoulder:b9,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b8,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b5,x:b13,y:b11,platform:Linux,", +"03000000f025000021c1000010010000,ShanWan Gioteck PS3 Wired Controller,a:b2,b:b1,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:b3,y:b0,platform:Linux,", +"03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,", +"030000005e0400008e02000073050000,Speedlink TORID Wireless 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,", +"030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,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,", +"03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", +"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", +"03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", +"05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", +"03000000de280000fc11000001000000,Steam 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,", +"03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", +"03000000de280000ff11000001000000,Steam Virtual 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,", +"03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,", +"030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,", +"030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,", +"030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,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:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Linux,", +"030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,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:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,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:Linux,", +"03000000bd12000015d0000010010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,", +"03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,", +"03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", +"03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", +"03000000790000001100000000010000,USB Gamepad1,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux,", +"05000000ac0500003232000001000000,VR-BOX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", +"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,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,", +"030000005e040000a102000007010000,X360 Wireless 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,", +"030000005e040000a102000000010000,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,", +"0000000058626f782033363020576900,Xbox 360 Wireless Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux,", +"0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),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:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", +"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,", +"050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000450c00002043000010010000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,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:b2,y:b3,platform:Linux,", +"05000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux,", +"03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux,", +"03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,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:Linux,", +"64633436313965656664373634323364,Microsoft X-Box 360 pad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android,", +"61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", +"4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", +"37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", +"35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,", +"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android,", +"5477696e20555342204a6f7973746963,Twin USB Joystick,a:b22,b:b21,back:b28,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,platform:Android,", +"34356136633366613530316338376136,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,platform:Android,", +"4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS,", +"4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS,", +"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS,", + +"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 +}; + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/mappings.h.in b/source/MaterialXGraphEditor/External/Glfw/src/mappings.h.in new file mode 100644 index 0000000000..583e98b2fe --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/mappings.h.in @@ -0,0 +1,73 @@ +//======================================================================== +// GLFW 3.4 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2006-2018 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// As mappings.h.in, this file is used by CMake to produce the mappings.h +// header file. If you are adding a GLFW specific gamepad mapping, this is +// where to put it. +//======================================================================== +// As mappings.h, this provides all pre-defined gamepad mappings, including +// all available in SDL_GameControllerDB. Do not edit this file. Any gamepad +// mappings not specific to GLFW should be submitted to SDL_GameControllerDB. +// This file can be re-generated from mappings.h.in and the upstream +// gamecontrollerdb.txt with the GenerateMappings.cmake script. +//======================================================================== + +// All gamepad mappings not labeled GLFW are copied from the +// SDL_GameControllerDB project under the following license: +// +// Simple DirectMedia Layer +// Copyright (C) 1997-2013 Sam Lantinga +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the +// use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. + +const char* _glfwDefaultMappings[] = +{ +@GLFW_GAMEPAD_MAPPINGS@ +"78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"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 +}; + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/monitor.c b/source/MaterialXGraphEditor/External/Glfw/src/monitor.c new file mode 100644 index 0000000000..394026f5a9 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/monitor.c @@ -0,0 +1,544 @@ +//======================================================================== +// GLFW 3.4 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// Please use C89 style variable declarations in this file because VS 2010 +//======================================================================== + +#include "internal.h" + +#include +#include +#include +#include +#include +#include + + +// Lexically compare video modes, used by qsort +// +static int compareVideoModes(const void* fp, const void* sp) +{ + const GLFWvidmode* fm = fp; + const GLFWvidmode* sm = sp; + const int fbpp = fm->redBits + fm->greenBits + fm->blueBits; + const int sbpp = sm->redBits + sm->greenBits + sm->blueBits; + const int farea = fm->width * fm->height; + const int sarea = sm->width * sm->height; + + // First sort on color bits per pixel + if (fbpp != sbpp) + return fbpp - sbpp; + + // Then sort on screen area + if (farea != sarea) + return farea - sarea; + + // Then sort on width + if (fm->width != sm->width) + return fm->width - sm->width; + + // Lastly sort on refresh rate + return fm->refreshRate - sm->refreshRate; +} + +// Retrieves the available modes for the specified monitor +// +static GLFWbool refreshVideoModes(_GLFWmonitor* monitor) +{ + int modeCount; + GLFWvidmode* modes; + + if (monitor->modes) + return GLFW_TRUE; + + modes = _glfwPlatformGetVideoModes(monitor, &modeCount); + if (!modes) + return GLFW_FALSE; + + qsort(modes, modeCount, sizeof(GLFWvidmode), compareVideoModes); + + free(monitor->modes); + monitor->modes = modes; + monitor->modeCount = modeCount; + + return GLFW_TRUE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW event API ////// +////////////////////////////////////////////////////////////////////////// + +// Notifies shared code of a monitor connection or disconnection +// +void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement) +{ + if (action == GLFW_CONNECTED) + { + _glfw.monitorCount++; + _glfw.monitors = + realloc(_glfw.monitors, sizeof(_GLFWmonitor*) * _glfw.monitorCount); + + if (placement == _GLFW_INSERT_FIRST) + { + memmove(_glfw.monitors + 1, + _glfw.monitors, + ((size_t) _glfw.monitorCount - 1) * sizeof(_GLFWmonitor*)); + _glfw.monitors[0] = monitor; + } + else + _glfw.monitors[_glfw.monitorCount - 1] = monitor; + } + else if (action == GLFW_DISCONNECTED) + { + 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++) + { + if (_glfw.monitors[i] == monitor) + { + _glfw.monitorCount--; + memmove(_glfw.monitors + i, + _glfw.monitors + i + 1, + ((size_t) _glfw.monitorCount - i) * sizeof(_GLFWmonitor*)); + break; + } + } + } + + if (_glfw.callbacks.monitor) + _glfw.callbacks.monitor((GLFWmonitor*) monitor, action); + + if (action == GLFW_DISCONNECTED) + _glfwFreeMonitor(monitor); +} + +// Notifies shared code that a full screen window has acquired or released +// a monitor +// +void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window) +{ + monitor->window = window; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Allocates and returns a monitor object with the specified name and dimensions +// +_GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM) +{ + _GLFWmonitor* monitor = calloc(1, sizeof(_GLFWmonitor)); + monitor->widthMM = widthMM; + monitor->heightMM = heightMM; + + if (name) + monitor->name = _glfw_strdup(name); + + return monitor; +} + +// Frees a monitor object and any data associated with it +// +void _glfwFreeMonitor(_GLFWmonitor* monitor) +{ + if (monitor == NULL) + return; + + _glfwPlatformFreeMonitor(monitor); + + _glfwFreeGammaArrays(&monitor->originalRamp); + _glfwFreeGammaArrays(&monitor->currentRamp); + + free(monitor->modes); + free(monitor->name); + free(monitor); +} + +// Allocates red, green and blue value arrays of the specified size +// +void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size) +{ + ramp->red = calloc(size, sizeof(unsigned short)); + ramp->green = calloc(size, sizeof(unsigned short)); + ramp->blue = calloc(size, sizeof(unsigned short)); + ramp->size = size; +} + +// Frees the red, green and blue value arrays and clears the struct +// +void _glfwFreeGammaArrays(GLFWgammaramp* ramp) +{ + free(ramp->red); + free(ramp->green); + free(ramp->blue); + + memset(ramp, 0, sizeof(GLFWgammaramp)); +} + +// Chooses the video mode most closely matching the desired one +// +const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor, + const GLFWvidmode* desired) +{ + int i; + unsigned int sizeDiff, leastSizeDiff = UINT_MAX; + unsigned int rateDiff, leastRateDiff = UINT_MAX; + unsigned int colorDiff, leastColorDiff = UINT_MAX; + const GLFWvidmode* current; + const GLFWvidmode* closest = NULL; + + if (!refreshVideoModes(monitor)) + return NULL; + + for (i = 0; i < monitor->modeCount; i++) + { + current = monitor->modes + i; + + colorDiff = 0; + + if (desired->redBits != GLFW_DONT_CARE) + colorDiff += abs(current->redBits - desired->redBits); + if (desired->greenBits != GLFW_DONT_CARE) + colorDiff += abs(current->greenBits - desired->greenBits); + if (desired->blueBits != GLFW_DONT_CARE) + colorDiff += abs(current->blueBits - desired->blueBits); + + sizeDiff = abs((current->width - desired->width) * + (current->width - desired->width) + + (current->height - desired->height) * + (current->height - desired->height)); + + if (desired->refreshRate != GLFW_DONT_CARE) + rateDiff = abs(current->refreshRate - desired->refreshRate); + else + rateDiff = UINT_MAX - current->refreshRate; + + if ((colorDiff < leastColorDiff) || + (colorDiff == leastColorDiff && sizeDiff < leastSizeDiff) || + (colorDiff == leastColorDiff && sizeDiff == leastSizeDiff && rateDiff < leastRateDiff)) + { + closest = current; + leastSizeDiff = sizeDiff; + leastRateDiff = rateDiff; + leastColorDiff = colorDiff; + } + } + + return closest; +} + +// Performs lexical comparison between two @ref GLFWvidmode structures +// +int _glfwCompareVideoModes(const GLFWvidmode* fm, const GLFWvidmode* sm) +{ + return compareVideoModes(fm, sm); +} + +// Splits a color depth into red, green and blue bit depths +// +void _glfwSplitBPP(int bpp, int* red, int* green, int* blue) +{ + int delta; + + // We assume that by 32 the user really meant 24 + if (bpp == 32) + bpp = 24; + + // Convert "bits per pixel" to red, green & blue sizes + + *red = *green = *blue = bpp / 3; + delta = bpp - (*red * 3); + if (delta >= 1) + *green = *green + 1; + + if (delta == 2) + *red = *red + 1; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW public API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI GLFWmonitor** glfwGetMonitors(int* count) +{ + assert(count != NULL); + + *count = 0; + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + *count = _glfw.monitorCount; + return (GLFWmonitor**) _glfw.monitors; +} + +GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (!_glfw.monitorCount) + return NULL; + + return (GLFWmonitor*) _glfw.monitors[0]; +} + +GLFWAPI void glfwGetMonitorPos(GLFWmonitor* handle, int* xpos, int* ypos) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + if (xpos) + *xpos = 0; + if (ypos) + *ypos = 0; + + _GLFW_REQUIRE_INIT(); + + _glfwPlatformGetMonitorPos(monitor, xpos, ypos); +} + +GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* handle, + int* xpos, int* ypos, + int* width, int* height) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + if (xpos) + *xpos = 0; + if (ypos) + *ypos = 0; + if (width) + *width = 0; + if (height) + *height = 0; + + _GLFW_REQUIRE_INIT(); + + _glfwPlatformGetMonitorWorkarea(monitor, xpos, ypos, width, height); +} + +GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* handle, int* widthMM, int* heightMM) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + if (widthMM) + *widthMM = 0; + if (heightMM) + *heightMM = 0; + + _GLFW_REQUIRE_INIT(); + + if (widthMM) + *widthMM = monitor->widthMM; + if (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) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return monitor->name; +} + +GLFWAPI void glfwSetMonitorUserPointer(GLFWmonitor* handle, void* pointer) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + _GLFW_REQUIRE_INIT(); + monitor->userPointer = pointer; +} + +GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return monitor->userPointer; +} + +GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(_glfw.callbacks.monitor, cbfun); + return cbfun; +} + +GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* handle, int* count) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + assert(count != NULL); + + *count = 0; + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (!refreshVideoModes(monitor)) + return NULL; + + *count = monitor->modeCount; + return monitor->modes; +} + +GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + _glfwPlatformGetVideoMode(monitor, &monitor->currentMode); + return &monitor->currentMode; +} + +GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma) +{ + unsigned int i; + unsigned short* values; + GLFWgammaramp ramp; + const GLFWgammaramp* original; + assert(handle != NULL); + assert(gamma > 0.f); + assert(gamma <= FLT_MAX); + + _GLFW_REQUIRE_INIT(); + + if (gamma != gamma || gamma <= 0.f || gamma > FLT_MAX) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid gamma value %f", gamma); + return; + } + + original = glfwGetGammaRamp(handle); + if (!original) + return; + + values = calloc(original->size, sizeof(unsigned short)); + + for (i = 0; i < original->size; i++) + { + float value; + + // Calculate intensity + value = i / (float) (original->size - 1); + // Apply gamma curve + value = powf(value, 1.f / gamma) * 65535.f + 0.5f; + // Clamp to value range + value = _glfw_fminf(value, 65535.f); + + values[i] = (unsigned short) value; + } + + ramp.red = values; + ramp.green = values; + ramp.blue = values; + ramp.size = original->size; + + glfwSetGammaRamp(handle, &ramp); + free(values); +} + +GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + _glfwFreeGammaArrays(&monitor->currentRamp); + if (!_glfwPlatformGetGammaRamp(monitor, &monitor->currentRamp)) + return NULL; + + return &monitor->currentRamp; +} + +GLFWAPI void glfwSetGammaRamp(GLFWmonitor* handle, const GLFWgammaramp* ramp) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + assert(ramp != NULL); + assert(ramp->size > 0); + assert(ramp->red != NULL); + assert(ramp->green != NULL); + assert(ramp->blue != NULL); + + if (ramp->size <= 0) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid gamma ramp size %i", + ramp->size); + return; + } + + _GLFW_REQUIRE_INIT(); + + if (!monitor->originalRamp.size) + { + if (!_glfwPlatformGetGammaRamp(monitor, &monitor->originalRamp)) + return; + } + + _glfwPlatformSetGammaRamp(monitor, ramp); +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/nsgl_context.h b/source/MaterialXGraphEditor/External/Glfw/src/nsgl_context.h new file mode 100644 index 0000000000..9c31436cf6 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/nsgl_context.h @@ -0,0 +1,66 @@ +//======================================================================== +// GLFW 3.4 macOS - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2009-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +// NOTE: Many Cocoa enum values have been renamed and we need to build across +// SDK versions where one is unavailable or the other deprecated +// We use the newer names in code and these macros to handle compatibility +#if MAC_OS_X_VERSION_MAX_ALLOWED < 101400 + #define NSOpenGLContextParameterSwapInterval NSOpenGLCPSwapInterval + #define NSOpenGLContextParameterSurfaceOpacity NSOpenGLCPSurfaceOpacity +#endif + +#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextNSGL nsgl +#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryNSGL nsgl + +#include + + +// NSGL-specific per-context data +// +typedef struct _GLFWcontextNSGL +{ + id pixelFormat; + id object; + +} _GLFWcontextNSGL; + +// NSGL-specific global data +// +typedef struct _GLFWlibraryNSGL +{ + // dlopen handle for OpenGL.framework (for glfwGetProcAddress) + CFBundleRef framework; + +} _GLFWlibraryNSGL; + + +GLFWbool _glfwInitNSGL(void); +void _glfwTerminateNSGL(void); +GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig); +void _glfwDestroyContextNSGL(_GLFWwindow* window); + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/nsgl_context.m b/source/MaterialXGraphEditor/External/Glfw/src/nsgl_context.m new file mode 100644 index 0000000000..e011fa5e0b --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/nsgl_context.m @@ -0,0 +1,369 @@ +//======================================================================== +// GLFW 3.4 macOS - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2009-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#include "internal.h" + +#include +#include + +static void makeContextCurrentNSGL(_GLFWwindow* window) +{ + @autoreleasepool { + + if (window) + [window->context.nsgl.object makeCurrentContext]; + else + [NSOpenGLContext clearCurrentContext]; + + _glfwPlatformSetTls(&_glfw.contextSlot, window); + + } // autoreleasepool +} + +static void swapBuffersNSGL(_GLFWwindow* window) +{ + @autoreleasepool { + + // HACK: Simulate vsync with usleep as NSGL swap interval does not apply to + // windows with a non-visible occlusion state + if (!([window->ns.object occlusionState] & NSWindowOcclusionStateVisible)) + { + int interval = 0; + [window->context.nsgl.object getValues:&interval + forParameter:NSOpenGLContextParameterSwapInterval]; + + if (interval > 0) + { + const double framerate = 60.0; + const uint64_t frequency = _glfwPlatformGetTimerFrequency(); + const uint64_t value = _glfwPlatformGetTimerValue(); + + const double elapsed = value / (double) frequency; + const double period = 1.0 / framerate; + const double delay = period - fmod(elapsed, period); + + usleep(floorl(delay * 1e6)); + } + } + + [window->context.nsgl.object flushBuffer]; + + } // autoreleasepool +} + +static void swapIntervalNSGL(int interval) +{ + @autoreleasepool { + + _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); + if (window) + { + [window->context.nsgl.object setValues:&interval + forParameter:NSOpenGLContextParameterSwapInterval]; + } + + } // autoreleasepool +} + +static int extensionSupportedNSGL(const char* extension) +{ + // There are no NSGL extensions + return GLFW_FALSE; +} + +static GLFWglproc getProcAddressNSGL(const char* procname) +{ + CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, + procname, + kCFStringEncodingASCII); + + GLFWglproc symbol = CFBundleGetFunctionPointerForName(_glfw.nsgl.framework, + symbolName); + + CFRelease(symbolName); + + return symbol; +} + +static void destroyContextNSGL(_GLFWwindow* window) +{ + @autoreleasepool { + + [window->context.nsgl.pixelFormat release]; + window->context.nsgl.pixelFormat = nil; + + [window->context.nsgl.object release]; + window->context.nsgl.object = nil; + + } // autoreleasepool +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialize OpenGL support +// +GLFWbool _glfwInitNSGL(void) +{ + if (_glfw.nsgl.framework) + return GLFW_TRUE; + + _glfw.nsgl.framework = + CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); + if (_glfw.nsgl.framework == NULL) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "NSGL: Failed to locate OpenGL framework"); + return GLFW_FALSE; + } + + return GLFW_TRUE; +} + +// Terminate OpenGL support +// +void _glfwTerminateNSGL(void) +{ +} + +// Create the OpenGL context +// +GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "NSGL: OpenGL ES is not available on macOS"); + return GLFW_FALSE; + } + + if (ctxconfig->major > 2) + { + if (ctxconfig->major == 3 && ctxconfig->minor < 2) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "NSGL: The targeted version of macOS does not support OpenGL 3.0 or 3.1 but may support 3.2 and above"); + return GLFW_FALSE; + } + } + + // Context robustness modes (GL_KHR_robustness) are not yet supported by + // macOS but are not a hard constraint, so ignore and continue + + // Context release behaviors (GL_KHR_context_flush_control) are not yet + // supported by macOS but are not a hard constraint, so ignore and continue + + // Debug contexts (GL_KHR_debug) are not yet supported by macOS but are not + // a hard constraint, so ignore and continue + + // No-error contexts (GL_KHR_no_error) are not yet supported by macOS but + // are not a hard constraint, so ignore and continue + +#define addAttrib(a) \ +{ \ + 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) + { + addAttrib(NSOpenGLPFAAllowOfflineRenderers); + // NOTE: This replaces the NSSupportsAutomaticGraphicsSwitching key in + // Info.plist for unbundled applications + // HACK: This assumes that NSOpenGLPixelFormat will remain + // a straightforward wrapper of its CGL counterpart + addAttrib(kCGLPFASupportsAutomaticGraphicsSwitching); + } + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 + if (ctxconfig->major >= 4) + { + setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core); + } + else +#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ + if (ctxconfig->major >= 3) + { + setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); + } + + if (ctxconfig->major <= 2) + { + if (fbconfig->auxBuffers != GLFW_DONT_CARE) + setAttrib(NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers); + + if (fbconfig->accumRedBits != GLFW_DONT_CARE && + fbconfig->accumGreenBits != GLFW_DONT_CARE && + fbconfig->accumBlueBits != GLFW_DONT_CARE && + fbconfig->accumAlphaBits != GLFW_DONT_CARE) + { + const int accumBits = fbconfig->accumRedBits + + fbconfig->accumGreenBits + + fbconfig->accumBlueBits + + fbconfig->accumAlphaBits; + + setAttrib(NSOpenGLPFAAccumSize, accumBits); + } + } + + if (fbconfig->redBits != GLFW_DONT_CARE && + fbconfig->greenBits != GLFW_DONT_CARE && + fbconfig->blueBits != GLFW_DONT_CARE) + { + int colorBits = fbconfig->redBits + + fbconfig->greenBits + + fbconfig->blueBits; + + // macOS needs non-zero color size, so set reasonable values + if (colorBits == 0) + colorBits = 24; + else if (colorBits < 15) + colorBits = 15; + + setAttrib(NSOpenGLPFAColorSize, colorBits); + } + + if (fbconfig->alphaBits != GLFW_DONT_CARE) + setAttrib(NSOpenGLPFAAlphaSize, fbconfig->alphaBits); + + if (fbconfig->depthBits != GLFW_DONT_CARE) + setAttrib(NSOpenGLPFADepthSize, fbconfig->depthBits); + + if (fbconfig->stencilBits != GLFW_DONT_CARE) + setAttrib(NSOpenGLPFAStencilSize, fbconfig->stencilBits); + + if (fbconfig->stereo) + { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "NSGL: Stereo rendering is deprecated"); + return GLFW_FALSE; +#else + addAttrib(NSOpenGLPFAStereo); +#endif + } + + if (fbconfig->doublebuffer) + addAttrib(NSOpenGLPFADoubleBuffer); + + if (fbconfig->samples != GLFW_DONT_CARE) + { + if (fbconfig->samples == 0) + { + setAttrib(NSOpenGLPFASampleBuffers, 0); + } + else + { + setAttrib(NSOpenGLPFASampleBuffers, 1); + setAttrib(NSOpenGLPFASamples, fbconfig->samples); + } + } + + // NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB + // framebuffer, so there's no need (and no way) to request it + + addAttrib(0); + +#undef addAttrib +#undef setAttrib + + window->context.nsgl.pixelFormat = + [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; + if (window->context.nsgl.pixelFormat == nil) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "NSGL: Failed to find a suitable pixel format"); + return GLFW_FALSE; + } + + NSOpenGLContext* share = nil; + + if (ctxconfig->share) + share = ctxconfig->share->context.nsgl.object; + + window->context.nsgl.object = + [[NSOpenGLContext alloc] initWithFormat:window->context.nsgl.pixelFormat + shareContext:share]; + if (window->context.nsgl.object == nil) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "NSGL: Failed to create OpenGL context"); + return GLFW_FALSE; + } + + if (fbconfig->transparent) + { + GLint opaque = 0; + [window->context.nsgl.object setValues:&opaque + forParameter:NSOpenGLContextParameterSurfaceOpacity]; + } + + [window->ns.view setWantsBestResolutionOpenGLSurface:window->ns.retina]; + + [window->context.nsgl.object setView:window->ns.view]; + + window->context.makeCurrent = makeContextCurrentNSGL; + window->context.swapBuffers = swapBuffersNSGL; + window->context.swapInterval = swapIntervalNSGL; + window->context.extensionSupported = extensionSupportedNSGL; + window->context.getProcAddress = getProcAddressNSGL; + window->context.destroy = destroyContextNSGL; + + return GLFW_TRUE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI id glfwGetNSGLContext(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(nil); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return nil; + } + + return window->context.nsgl.object; +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/null_init.c b/source/MaterialXGraphEditor/External/Glfw/src/null_init.c new file mode 100644 index 0000000000..20c76dc5a4 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/null_init.c @@ -0,0 +1,52 @@ +//======================================================================== +// GLFW 3.4 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2016-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#include "internal.h" + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformInit(void) +{ + _glfwInitTimerPOSIX(); + return GLFW_TRUE; +} + +void _glfwPlatformTerminate(void) +{ + _glfwTerminateOSMesa(); +} + +const char* _glfwPlatformGetVersionString(void) +{ + return _GLFW_VERSION_NUMBER " null OSMesa"; +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/null_joystick.c b/source/MaterialXGraphEditor/External/Glfw/src/null_joystick.c new file mode 100644 index 0000000000..36c18aa228 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/null_joystick.c @@ -0,0 +1,44 @@ +//======================================================================== +// GLFW 3.4 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2016-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#include "internal.h" + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) +{ + return GLFW_FALSE; +} + +void _glfwPlatformUpdateGamepadGUID(char* guid) +{ +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/null_joystick.h b/source/MaterialXGraphEditor/External/Glfw/src/null_joystick.h new file mode 100644 index 0000000000..5d19a451bc --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/null_joystick.h @@ -0,0 +1,31 @@ +//======================================================================== +// GLFW 3.4 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2006-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#define _GLFW_PLATFORM_JOYSTICK_STATE struct { int dummyJoystick; } +#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE struct { int dummyLibraryJoystick; } + +#define _GLFW_PLATFORM_MAPPING_NAME "" + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/null_monitor.c b/source/MaterialXGraphEditor/External/Glfw/src/null_monitor.c new file mode 100644 index 0000000000..0a7fe0646f --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/null_monitor.c @@ -0,0 +1,77 @@ +//======================================================================== +// GLFW 3.4 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2016-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#include "internal.h" + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) +{ +} + +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; +} + +void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, + int* xpos, int* ypos, + int* width, int* height) +{ +} + +GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) +{ + return NULL; +} + +void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) +{ +} + +GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +{ + return GLFW_FALSE; +} + +void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) +{ +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/null_platform.h b/source/MaterialXGraphEditor/External/Glfw/src/null_platform.h new file mode 100644 index 0000000000..fdea9906da --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/null_platform.h @@ -0,0 +1,62 @@ +//======================================================================== +// GLFW 3.4 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2016-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include + +#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNull null + +#define _GLFW_PLATFORM_CONTEXT_STATE struct { int dummyContext; } +#define _GLFW_PLATFORM_MONITOR_STATE struct { int dummyMonitor; } +#define _GLFW_PLATFORM_CURSOR_STATE struct { int dummyCursor; } +#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE struct { int dummyLibraryWindow; } +#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE struct { int dummyLibraryContext; } +#define _GLFW_EGL_CONTEXT_STATE struct { int dummyEGLContext; } +#define _GLFW_EGL_LIBRARY_CONTEXT_STATE struct { int dummyEGLLibraryContext; } + +#include "osmesa_context.h" +#include "posix_time.h" +#include "posix_thread.h" +#include "null_joystick.h" + +#if defined(_GLFW_WIN32) + #define _glfw_dlopen(name) LoadLibraryA(name) + #define _glfw_dlclose(handle) FreeLibrary((HMODULE) handle) + #define _glfw_dlsym(handle, name) GetProcAddress((HMODULE) handle, name) +#else + #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) + #define _glfw_dlclose(handle) dlclose(handle) + #define _glfw_dlsym(handle, name) dlsym(handle, name) +#endif + +// Null-specific per-window data +// +typedef struct _GLFWwindowNull +{ + int width; + int height; +} _GLFWwindowNull; + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/null_window.c b/source/MaterialXGraphEditor/External/Glfw/src/null_window.c new file mode 100644 index 0000000000..936400d397 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/null_window.c @@ -0,0 +1,332 @@ +//======================================================================== +// GLFW 3.4 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2016-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#include "internal.h" + + +static int createNativeWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig) +{ + window->null.width = wndconfig->width; + window->null.height = wndconfig->height; + + return GLFW_TRUE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformCreateWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + if (!createNativeWindow(window, wndconfig)) + return GLFW_FALSE; + + if (ctxconfig->client != GLFW_NO_API) + { + if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API || + ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else + { + _glfwInputError(GLFW_API_UNAVAILABLE, "Null: EGL not available"); + return GLFW_FALSE; + } + } + + return GLFW_TRUE; +} + +void _glfwPlatformDestroyWindow(_GLFWwindow* window) +{ + if (window->context.destroy) + window->context.destroy(window); +} + +void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) +{ +} + +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count, + const GLFWimage* images) +{ +} + +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) +{ +} + +void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) +{ +} + +void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) +{ +} + +void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) +{ + if (width) + *width = window->null.width; + if (height) + *height = window->null.height; +} + +void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) +{ + window->null.width = width; + window->null.height = height; +} + +void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, + int minwidth, int minheight, + int maxwidth, int maxheight) +{ +} + +void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int n, int d) +{ +} + +void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) +{ + if (width) + *width = window->null.width; + if (height) + *height = window->null.height; +} + +void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, + int* left, int* top, + int* right, int* bottom) +{ +} + +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = 1.f; + if (yscale) + *yscale = 1.f; +} + +void _glfwPlatformIconifyWindow(_GLFWwindow* window) +{ +} + +void _glfwPlatformRestoreWindow(_GLFWwindow* window) +{ +} + +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ +} + +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + +int _glfwPlatformWindowHovered(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +{ +} + +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +{ +} + +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +{ +} + +float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) +{ + return 1.f; +} + +void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) +{ +} + +void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) +{ +} + +GLFWbool _glfwPlatformRawMouseMotionSupported(void) +{ + return GLFW_FALSE; +} + +void _glfwPlatformShowWindow(_GLFWwindow* window) +{ +} + + +void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) +{ +} + +void _glfwPlatformUnhideWindow(_GLFWwindow* window) +{ +} + +void _glfwPlatformHideWindow(_GLFWwindow* window) +{ +} + +void _glfwPlatformFocusWindow(_GLFWwindow* window) +{ +} + +int _glfwPlatformWindowFocused(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + +int _glfwPlatformWindowIconified(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + +int _glfwPlatformWindowVisible(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + +void _glfwPlatformPollEvents(void) +{ +} + +void _glfwPlatformWaitEvents(void) +{ +} + +void _glfwPlatformWaitEventsTimeout(double timeout) +{ +} + +void _glfwPlatformPostEmptyEvent(void) +{ +} + +void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) +{ +} + +void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) +{ +} + +void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) +{ +} + +int _glfwPlatformCreateCursor(_GLFWcursor* cursor, + const GLFWimage* image, + int xhot, int yhot) +{ + return GLFW_TRUE; +} + +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +{ + return GLFW_TRUE; +} + +void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) +{ +} + +void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) +{ +} + +void _glfwPlatformSetClipboardString(const char* string) +{ +} + +const char* _glfwPlatformGetClipboardString(void) +{ + return NULL; +} + +const char* _glfwPlatformGetScancodeName(int scancode) +{ + return ""; +} + +int _glfwPlatformGetKeyScancode(int key) +{ + return -1; +} + +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) +{ +} + +int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily) +{ + return GLFW_FALSE; +} + +VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, + _GLFWwindow* window, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) +{ + // This seems like the most appropriate error to return here + return VK_ERROR_INITIALIZATION_FAILED; +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/osmesa_context.c b/source/MaterialXGraphEditor/External/Glfw/src/osmesa_context.c new file mode 100644 index 0000000000..70e8675baa --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/osmesa_context.c @@ -0,0 +1,372 @@ +//======================================================================== +// GLFW 3.4 OSMesa - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2016-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// Please use C89 style variable declarations in this file because VS 2010 +//======================================================================== + +#include +#include +#include + +#include "internal.h" + + +static void makeContextCurrentOSMesa(_GLFWwindow* window) +{ + if (window) + { + int width, height; + _glfwPlatformGetFramebufferSize(window, &width, &height); + + // Check to see if we need to allocate a new buffer + if ((window->context.osmesa.buffer == NULL) || + (width != window->context.osmesa.width) || + (height != window->context.osmesa.height)) + { + free(window->context.osmesa.buffer); + + // Allocate the new buffer (width * height * 8-bit RGBA) + window->context.osmesa.buffer = calloc(4, (size_t) width * height); + window->context.osmesa.width = width; + window->context.osmesa.height = height; + } + + if (!OSMesaMakeCurrent(window->context.osmesa.handle, + window->context.osmesa.buffer, + GL_UNSIGNED_BYTE, + width, height)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OSMesa: Failed to make context current"); + return; + } + } + + _glfwPlatformSetTls(&_glfw.contextSlot, window); +} + +static GLFWglproc getProcAddressOSMesa(const char* procname) +{ + return (GLFWglproc) OSMesaGetProcAddress(procname); +} + +static void destroyContextOSMesa(_GLFWwindow* window) +{ + if (window->context.osmesa.handle) + { + OSMesaDestroyContext(window->context.osmesa.handle); + window->context.osmesa.handle = NULL; + } + + if (window->context.osmesa.buffer) + { + free(window->context.osmesa.buffer); + window->context.osmesa.width = 0; + window->context.osmesa.height = 0; + } +} + +static void swapBuffersOSMesa(_GLFWwindow* window) +{ + // No double buffering on OSMesa +} + +static void swapIntervalOSMesa(int interval) +{ + // No swap interval on OSMesa +} + +static int extensionSupportedOSMesa(const char* extension) +{ + // OSMesa does not have extensions + return GLFW_FALSE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWbool _glfwInitOSMesa(void) +{ + int i; + const char* sonames[] = + { +#if defined(_GLFW_OSMESA_LIBRARY) + _GLFW_OSMESA_LIBRARY, +#elif defined(_WIN32) + "libOSMesa.dll", + "OSMesa.dll", +#elif defined(__APPLE__) + "libOSMesa.8.dylib", +#elif defined(__CYGWIN__) + "libOSMesa-8.so", +#else + "libOSMesa.so.8", + "libOSMesa.so.6", +#endif + NULL + }; + + if (_glfw.osmesa.handle) + return GLFW_TRUE; + + for (i = 0; sonames[i]; i++) + { + _glfw.osmesa.handle = _glfw_dlopen(sonames[i]); + if (_glfw.osmesa.handle) + break; + } + + if (!_glfw.osmesa.handle) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "OSMesa: Library not found"); + return GLFW_FALSE; + } + + _glfw.osmesa.CreateContextExt = (PFN_OSMesaCreateContextExt) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContextExt"); + _glfw.osmesa.CreateContextAttribs = (PFN_OSMesaCreateContextAttribs) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContextAttribs"); + _glfw.osmesa.DestroyContext = (PFN_OSMesaDestroyContext) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaDestroyContext"); + _glfw.osmesa.MakeCurrent = (PFN_OSMesaMakeCurrent) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaMakeCurrent"); + _glfw.osmesa.GetColorBuffer = (PFN_OSMesaGetColorBuffer) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetColorBuffer"); + _glfw.osmesa.GetDepthBuffer = (PFN_OSMesaGetDepthBuffer) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetDepthBuffer"); + _glfw.osmesa.GetProcAddress = (PFN_OSMesaGetProcAddress) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetProcAddress"); + + if (!_glfw.osmesa.CreateContextExt || + !_glfw.osmesa.DestroyContext || + !_glfw.osmesa.MakeCurrent || + !_glfw.osmesa.GetColorBuffer || + !_glfw.osmesa.GetDepthBuffer || + !_glfw.osmesa.GetProcAddress) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OSMesa: Failed to load required entry points"); + + _glfwTerminateOSMesa(); + return GLFW_FALSE; + } + + return GLFW_TRUE; +} + +void _glfwTerminateOSMesa(void) +{ + if (_glfw.osmesa.handle) + { + _glfw_dlclose(_glfw.osmesa.handle); + _glfw.osmesa.handle = NULL; + } +} + +#define setAttrib(a, v) \ +{ \ + assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ + attribs[index++] = a; \ + attribs[index++] = v; \ +} + +GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + OSMesaContext share = NULL; + const int accumBits = fbconfig->accumRedBits + + fbconfig->accumGreenBits + + fbconfig->accumBlueBits + + fbconfig->accumAlphaBits; + + if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "OSMesa: OpenGL ES is not available on OSMesa"); + return GLFW_FALSE; + } + + if (ctxconfig->share) + share = ctxconfig->share->context.osmesa.handle; + + if (OSMesaCreateContextAttribs) + { + int index = 0, attribs[40]; + + setAttrib(OSMESA_FORMAT, OSMESA_RGBA); + setAttrib(OSMESA_DEPTH_BITS, fbconfig->depthBits); + setAttrib(OSMESA_STENCIL_BITS, fbconfig->stencilBits); + setAttrib(OSMESA_ACCUM_BITS, accumBits); + + if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) + { + setAttrib(OSMESA_PROFILE, OSMESA_CORE_PROFILE); + } + else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) + { + setAttrib(OSMESA_PROFILE, OSMESA_COMPAT_PROFILE); + } + + if (ctxconfig->major != 1 || ctxconfig->minor != 0) + { + setAttrib(OSMESA_CONTEXT_MAJOR_VERSION, ctxconfig->major); + setAttrib(OSMESA_CONTEXT_MINOR_VERSION, ctxconfig->minor); + } + + if (ctxconfig->forward) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "OSMesa: Forward-compatible contexts not supported"); + return GLFW_FALSE; + } + + setAttrib(0, 0); + + window->context.osmesa.handle = + OSMesaCreateContextAttribs(attribs, share); + } + else + { + if (ctxconfig->profile) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "OSMesa: OpenGL profiles unavailable"); + return GLFW_FALSE; + } + + window->context.osmesa.handle = + OSMesaCreateContextExt(OSMESA_RGBA, + fbconfig->depthBits, + fbconfig->stencilBits, + accumBits, + share); + } + + if (window->context.osmesa.handle == NULL) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "OSMesa: Failed to create context"); + return GLFW_FALSE; + } + + window->context.makeCurrent = makeContextCurrentOSMesa; + window->context.swapBuffers = swapBuffersOSMesa; + window->context.swapInterval = swapIntervalOSMesa; + window->context.extensionSupported = extensionSupportedOSMesa; + window->context.getProcAddress = getProcAddressOSMesa; + window->context.destroy = destroyContextOSMesa; + + return GLFW_TRUE; +} + +#undef setAttrib + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* handle, int* width, + int* height, int* format, void** buffer) +{ + void* mesaBuffer; + GLint mesaWidth, mesaHeight, mesaFormat; + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + if (!OSMesaGetColorBuffer(window->context.osmesa.handle, + &mesaWidth, &mesaHeight, + &mesaFormat, &mesaBuffer)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OSMesa: Failed to retrieve color buffer"); + return GLFW_FALSE; + } + + if (width) + *width = mesaWidth; + if (height) + *height = mesaHeight; + if (format) + *format = mesaFormat; + if (buffer) + *buffer = mesaBuffer; + + return GLFW_TRUE; +} + +GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* handle, + int* width, int* height, + int* bytesPerValue, + void** buffer) +{ + void* mesaBuffer; + GLint mesaWidth, mesaHeight, mesaBytes; + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + if (!OSMesaGetDepthBuffer(window->context.osmesa.handle, + &mesaWidth, &mesaHeight, + &mesaBytes, &mesaBuffer)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OSMesa: Failed to retrieve depth buffer"); + return GLFW_FALSE; + } + + if (width) + *width = mesaWidth; + if (height) + *height = mesaHeight; + if (bytesPerValue) + *bytesPerValue = mesaBytes; + if (buffer) + *buffer = mesaBuffer; + + return GLFW_TRUE; +} + +GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return NULL; + } + + return window->context.osmesa.handle; +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/osmesa_context.h b/source/MaterialXGraphEditor/External/Glfw/src/osmesa_context.h new file mode 100644 index 0000000000..a5de0d9061 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/osmesa_context.h @@ -0,0 +1,94 @@ +//======================================================================== +// GLFW 3.4 OSMesa - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2016-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#define OSMESA_RGBA 0x1908 +#define OSMESA_FORMAT 0x22 +#define OSMESA_DEPTH_BITS 0x30 +#define OSMESA_STENCIL_BITS 0x31 +#define OSMESA_ACCUM_BITS 0x32 +#define OSMESA_PROFILE 0x33 +#define OSMESA_CORE_PROFILE 0x34 +#define OSMESA_COMPAT_PROFILE 0x35 +#define OSMESA_CONTEXT_MAJOR_VERSION 0x36 +#define OSMESA_CONTEXT_MINOR_VERSION 0x37 + +typedef void* OSMesaContext; +typedef void (*OSMESAproc)(void); + +typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextExt)(GLenum,GLint,GLint,GLint,OSMesaContext); +typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextAttribs)(const int*,OSMesaContext); +typedef void (GLAPIENTRY * PFN_OSMesaDestroyContext)(OSMesaContext); +typedef int (GLAPIENTRY * PFN_OSMesaMakeCurrent)(OSMesaContext,void*,int,int,int); +typedef int (GLAPIENTRY * PFN_OSMesaGetColorBuffer)(OSMesaContext,int*,int*,int*,void**); +typedef int (GLAPIENTRY * PFN_OSMesaGetDepthBuffer)(OSMesaContext,int*,int*,int*,void**); +typedef GLFWglproc (GLAPIENTRY * PFN_OSMesaGetProcAddress)(const char*); +#define OSMesaCreateContextExt _glfw.osmesa.CreateContextExt +#define OSMesaCreateContextAttribs _glfw.osmesa.CreateContextAttribs +#define OSMesaDestroyContext _glfw.osmesa.DestroyContext +#define OSMesaMakeCurrent _glfw.osmesa.MakeCurrent +#define OSMesaGetColorBuffer _glfw.osmesa.GetColorBuffer +#define OSMesaGetDepthBuffer _glfw.osmesa.GetDepthBuffer +#define OSMesaGetProcAddress _glfw.osmesa.GetProcAddress + +#define _GLFW_OSMESA_CONTEXT_STATE _GLFWcontextOSMesa osmesa +#define _GLFW_OSMESA_LIBRARY_CONTEXT_STATE _GLFWlibraryOSMesa osmesa + + +// OSMesa-specific per-context data +// +typedef struct _GLFWcontextOSMesa +{ + OSMesaContext handle; + int width; + int height; + void* buffer; + +} _GLFWcontextOSMesa; + +// OSMesa-specific global data +// +typedef struct _GLFWlibraryOSMesa +{ + void* handle; + + PFN_OSMesaCreateContextExt CreateContextExt; + PFN_OSMesaCreateContextAttribs CreateContextAttribs; + PFN_OSMesaDestroyContext DestroyContext; + PFN_OSMesaMakeCurrent MakeCurrent; + PFN_OSMesaGetColorBuffer GetColorBuffer; + PFN_OSMesaGetDepthBuffer GetDepthBuffer; + PFN_OSMesaGetProcAddress GetProcAddress; + +} _GLFWlibraryOSMesa; + + +GLFWbool _glfwInitOSMesa(void); +void _glfwTerminateOSMesa(void); +GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig); + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/posix_thread.c b/source/MaterialXGraphEditor/External/Glfw/src/posix_thread.c new file mode 100644 index 0000000000..0236145781 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/posix_thread.c @@ -0,0 +1,105 @@ +//======================================================================== +// GLFW 3.4 POSIX - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#include "internal.h" + +#include +#include + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls) +{ + 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; +} + +void _glfwPlatformDestroyTls(_GLFWtls* tls) +{ + 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); +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/posix_thread.h b/source/MaterialXGraphEditor/External/Glfw/src/posix_thread.h new file mode 100644 index 0000000000..85ce596c95 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/posix_thread.h @@ -0,0 +1,51 @@ +//======================================================================== +// GLFW 3.4 POSIX - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include + +#define _GLFW_PLATFORM_TLS_STATE _GLFWtlsPOSIX posix +#define _GLFW_PLATFORM_MUTEX_STATE _GLFWmutexPOSIX posix + + +// POSIX-specific thread local storage data +// +typedef struct _GLFWtlsPOSIX +{ + GLFWbool allocated; + pthread_key_t key; + +} _GLFWtlsPOSIX; + +// POSIX-specific mutex data +// +typedef struct _GLFWmutexPOSIX +{ + GLFWbool allocated; + pthread_mutex_t handle; + +} _GLFWmutexPOSIX; + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/posix_time.c b/source/MaterialXGraphEditor/External/Glfw/src/posix_time.c new file mode 100644 index 0000000000..ae3d5c7893 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/posix_time.c @@ -0,0 +1,90 @@ +//======================================================================== +// GLFW 3.4 POSIX - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#define _POSIX_C_SOURCE 199309L + +#include "internal.h" + +#include +#include +#include + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialise timer +// +void _glfwInitTimerPOSIX(void) +{ +#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + { + _glfw.timer.posix.monotonic = GLFW_TRUE; + _glfw.timer.posix.frequency = 1000000000; + } + else +#endif + { + _glfw.timer.posix.monotonic = GLFW_FALSE; + _glfw.timer.posix.frequency = 1000000; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +uint64_t _glfwPlatformGetTimerValue(void) +{ +#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) + if (_glfw.timer.posix.monotonic) + { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t) ts.tv_sec * (uint64_t) 1000000000 + (uint64_t) ts.tv_nsec; + } + else +#endif + { + struct timeval tv; + gettimeofday(&tv, NULL); + return (uint64_t) tv.tv_sec * (uint64_t) 1000000 + (uint64_t) tv.tv_usec; + } +} + +uint64_t _glfwPlatformGetTimerFrequency(void) +{ + return _glfw.timer.posix.frequency; +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/posix_time.h b/source/MaterialXGraphEditor/External/Glfw/src/posix_time.h new file mode 100644 index 0000000000..9b59a1879b --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/posix_time.h @@ -0,0 +1,44 @@ +//======================================================================== +// GLFW 3.4 POSIX - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerPOSIX posix + +#include + + +// POSIX-specific global timer data +// +typedef struct _GLFWtimerPOSIX +{ + GLFWbool monotonic; + uint64_t frequency; + +} _GLFWtimerPOSIX; + + +void _glfwInitTimerPOSIX(void); + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/vulkan.c b/source/MaterialXGraphEditor/External/Glfw/src/vulkan.c new file mode 100644 index 0000000000..b5340520b1 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/vulkan.c @@ -0,0 +1,332 @@ +//======================================================================== +// GLFW 3.4 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2018 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// Please use C89 style variable declarations in this file because VS 2010 +//======================================================================== + +#include "internal.h" + +#include +#include +#include + +#define _GLFW_FIND_LOADER 1 +#define _GLFW_REQUIRE_LOADER 2 + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWbool _glfwInitVulkan(int mode) +{ + VkResult err; + VkExtensionProperties* ep; + uint32_t i, count; + + if (_glfw.vk.available) + return GLFW_TRUE; + +#if !defined(_GLFW_VULKAN_STATIC) +#if defined(_GLFW_VULKAN_LIBRARY) + _glfw.vk.handle = _glfw_dlopen(_GLFW_VULKAN_LIBRARY); +#elif defined(_GLFW_WIN32) + _glfw.vk.handle = _glfw_dlopen("vulkan-1.dll"); +#elif defined(_GLFW_COCOA) + _glfw.vk.handle = _glfw_dlopen("libvulkan.1.dylib"); + if (!_glfw.vk.handle) + _glfw.vk.handle = _glfwLoadLocalVulkanLoaderNS(); +#else + _glfw.vk.handle = _glfw_dlopen("libvulkan.so.1"); +#endif + if (!_glfw.vk.handle) + { + if (mode == _GLFW_REQUIRE_LOADER) + _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Loader not found"); + + return GLFW_FALSE; + } + + _glfw.vk.GetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) + _glfw_dlsym(_glfw.vk.handle, "vkGetInstanceProcAddr"); + if (!_glfw.vk.GetInstanceProcAddr) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Vulkan: Loader does not export vkGetInstanceProcAddr"); + + _glfwTerminateVulkan(); + return GLFW_FALSE; + } + + _glfw.vk.EnumerateInstanceExtensionProperties = (PFN_vkEnumerateInstanceExtensionProperties) + vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceExtensionProperties"); + if (!_glfw.vk.EnumerateInstanceExtensionProperties) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Vulkan: Failed to retrieve vkEnumerateInstanceExtensionProperties"); + + _glfwTerminateVulkan(); + return GLFW_FALSE; + } +#endif // _GLFW_VULKAN_STATIC + + err = vkEnumerateInstanceExtensionProperties(NULL, &count, NULL); + if (err) + { + // NOTE: This happens on systems with a loader but without any Vulkan ICD + if (mode == _GLFW_REQUIRE_LOADER) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Vulkan: Failed to query instance extension count: %s", + _glfwGetVulkanResultString(err)); + } + + _glfwTerminateVulkan(); + return GLFW_FALSE; + } + + ep = calloc(count, sizeof(VkExtensionProperties)); + + err = vkEnumerateInstanceExtensionProperties(NULL, &count, ep); + if (err) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Vulkan: Failed to query instance extensions: %s", + _glfwGetVulkanResultString(err)); + + free(ep); + _glfwTerminateVulkan(); + return GLFW_FALSE; + } + + for (i = 0; i < count; i++) + { + if (strcmp(ep[i].extensionName, "VK_KHR_surface") == 0) + _glfw.vk.KHR_surface = GLFW_TRUE; +#if defined(_GLFW_WIN32) + else if (strcmp(ep[i].extensionName, "VK_KHR_win32_surface") == 0) + _glfw.vk.KHR_win32_surface = GLFW_TRUE; +#elif defined(_GLFW_COCOA) + else if (strcmp(ep[i].extensionName, "VK_MVK_macos_surface") == 0) + _glfw.vk.MVK_macos_surface = GLFW_TRUE; + else if (strcmp(ep[i].extensionName, "VK_EXT_metal_surface") == 0) + _glfw.vk.EXT_metal_surface = GLFW_TRUE; +#elif defined(_GLFW_X11) + else if (strcmp(ep[i].extensionName, "VK_KHR_xlib_surface") == 0) + _glfw.vk.KHR_xlib_surface = GLFW_TRUE; + else if (strcmp(ep[i].extensionName, "VK_KHR_xcb_surface") == 0) + _glfw.vk.KHR_xcb_surface = GLFW_TRUE; +#elif defined(_GLFW_WAYLAND) + else if (strcmp(ep[i].extensionName, "VK_KHR_wayland_surface") == 0) + _glfw.vk.KHR_wayland_surface = GLFW_TRUE; +#endif + } + + free(ep); + + _glfw.vk.available = GLFW_TRUE; + + _glfwPlatformGetRequiredInstanceExtensions(_glfw.vk.extensions); + + return GLFW_TRUE; +} + +void _glfwTerminateVulkan(void) +{ +#if !defined(_GLFW_VULKAN_STATIC) + if (_glfw.vk.handle) + _glfw_dlclose(_glfw.vk.handle); +#endif +} + +const char* _glfwGetVulkanResultString(VkResult result) +{ + switch (result) + { + case VK_SUCCESS: + return "Success"; + case VK_NOT_READY: + return "A fence or query has not yet completed"; + case VK_TIMEOUT: + return "A wait operation has not completed in the specified time"; + case VK_EVENT_SET: + return "An event is signaled"; + case VK_EVENT_RESET: + return "An event is unsignaled"; + case VK_INCOMPLETE: + return "A return array was too small for the result"; + case VK_ERROR_OUT_OF_HOST_MEMORY: + return "A host memory allocation has failed"; + case VK_ERROR_OUT_OF_DEVICE_MEMORY: + return "A device memory allocation has failed"; + case VK_ERROR_INITIALIZATION_FAILED: + return "Initialization of an object could not be completed for implementation-specific reasons"; + case VK_ERROR_DEVICE_LOST: + return "The logical or physical device has been lost"; + case VK_ERROR_MEMORY_MAP_FAILED: + return "Mapping of a memory object has failed"; + case VK_ERROR_LAYER_NOT_PRESENT: + return "A requested layer is not present or could not be loaded"; + case VK_ERROR_EXTENSION_NOT_PRESENT: + return "A requested extension is not supported"; + case VK_ERROR_FEATURE_NOT_PRESENT: + return "A requested feature is not supported"; + case VK_ERROR_INCOMPATIBLE_DRIVER: + return "The requested version of Vulkan is not supported by the driver or is otherwise incompatible"; + case VK_ERROR_TOO_MANY_OBJECTS: + return "Too many objects of the type have already been created"; + case VK_ERROR_FORMAT_NOT_SUPPORTED: + return "A requested format is not supported on this device"; + case VK_ERROR_SURFACE_LOST_KHR: + return "A surface is no longer available"; + case VK_SUBOPTIMAL_KHR: + return "A swapchain no longer matches the surface properties exactly, but can still be used"; + case VK_ERROR_OUT_OF_DATE_KHR: + return "A surface has changed in such a way that it is no longer compatible with the swapchain"; + case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: + return "The display used by a swapchain does not use the same presentable image layout"; + case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: + return "The requested window is already connected to a VkSurfaceKHR, or to some other non-Vulkan API"; + case VK_ERROR_VALIDATION_FAILED_EXT: + return "A validation layer found an error"; + default: + return "ERROR: UNKNOWN VULKAN ERROR"; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW public API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI int glfwVulkanSupported(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + return _glfwInitVulkan(_GLFW_FIND_LOADER); +} + +GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count) +{ + assert(count != NULL); + + *count = 0; + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) + return NULL; + + if (!_glfw.vk.extensions[0]) + return NULL; + + *count = 2; + return (const char**) _glfw.vk.extensions; +} + +GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, + const char* procname) +{ + GLFWvkproc proc; + assert(procname != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) + return NULL; + + proc = (GLFWvkproc) vkGetInstanceProcAddr(instance, procname); +#if defined(_GLFW_VULKAN_STATIC) + if (!proc) + { + if (strcmp(procname, "vkGetInstanceProcAddr") == 0) + return (GLFWvkproc) vkGetInstanceProcAddr; + } +#else + if (!proc) + proc = (GLFWvkproc) _glfw_dlsym(_glfw.vk.handle, procname); +#endif + + return proc; +} + +GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily) +{ + assert(instance != VK_NULL_HANDLE); + assert(device != VK_NULL_HANDLE); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) + return GLFW_FALSE; + + if (!_glfw.vk.extensions[0]) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Vulkan: Window surface creation extensions not found"); + return GLFW_FALSE; + } + + return _glfwPlatformGetPhysicalDevicePresentationSupport(instance, + device, + queuefamily); +} + +GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, + GLFWwindow* handle, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(instance != VK_NULL_HANDLE); + assert(window != NULL); + assert(surface != NULL); + + *surface = VK_NULL_HANDLE; + + _GLFW_REQUIRE_INIT_OR_RETURN(VK_ERROR_INITIALIZATION_FAILED); + + if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) + return VK_ERROR_INITIALIZATION_FAILED; + + if (!_glfw.vk.extensions[0]) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Vulkan: Window surface creation extensions not found"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + if (window->context.client != GLFW_NO_API) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Vulkan: Window surface creation requires the window to have the client API set to GLFW_NO_API"); + return VK_ERROR_NATIVE_WINDOW_IN_USE_KHR; + } + + return _glfwPlatformCreateWindowSurface(instance, window, allocator, surface); +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/wgl_context.c b/source/MaterialXGraphEditor/External/Glfw/src/wgl_context.c new file mode 100644 index 0000000000..4f9a6ffe33 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/wgl_context.c @@ -0,0 +1,796 @@ +//======================================================================== +// GLFW 3.4 WGL - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// Please use C89 style variable declarations in this file because VS 2010 +//======================================================================== + +#include "internal.h" + +#include +#include +#include + +// Return the value corresponding to the specified attribute +// +static int findPixelFormatAttribValue(const int* attribs, + int attribCount, + const int* values, + int attrib) +{ + int i; + + for (i = 0; i < attribCount; i++) + { + if (attribs[i] == attrib) + return values[i]; + } + + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Unknown pixel format attribute requested"); + return 0; +} + +#define addAttrib(a) \ +{ \ + assert((size_t) attribCount < sizeof(attribs) / sizeof(attribs[0])); \ + attribs[attribCount++] = a; \ +} +#define findAttribValue(a) \ + findPixelFormatAttribValue(attribs, attribCount, values, a) + +// Return a list of available and usable framebuffer configs +// +static int choosePixelFormat(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + _GLFWfbconfig* usableConfigs; + const _GLFWfbconfig* closest; + int i, pixelFormat, nativeCount, usableCount = 0, attribCount = 0; + int attribs[40]; + int values[sizeof(attribs) / sizeof(attribs[0])]; + + if (_glfw.wgl.ARB_pixel_format) + { + const int attrib = WGL_NUMBER_PIXEL_FORMATS_ARB; + + if (!wglGetPixelFormatAttribivARB(window->context.wgl.dc, + 1, 0, 1, &attrib, &nativeCount)) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to retrieve pixel format attribute"); + return 0; + } + + addAttrib(WGL_SUPPORT_OPENGL_ARB); + addAttrib(WGL_DRAW_TO_WINDOW_ARB); + addAttrib(WGL_PIXEL_TYPE_ARB); + addAttrib(WGL_ACCELERATION_ARB); + addAttrib(WGL_RED_BITS_ARB); + addAttrib(WGL_RED_SHIFT_ARB); + addAttrib(WGL_GREEN_BITS_ARB); + addAttrib(WGL_GREEN_SHIFT_ARB); + addAttrib(WGL_BLUE_BITS_ARB); + addAttrib(WGL_BLUE_SHIFT_ARB); + addAttrib(WGL_ALPHA_BITS_ARB); + addAttrib(WGL_ALPHA_SHIFT_ARB); + addAttrib(WGL_DEPTH_BITS_ARB); + addAttrib(WGL_STENCIL_BITS_ARB); + addAttrib(WGL_ACCUM_BITS_ARB); + addAttrib(WGL_ACCUM_RED_BITS_ARB); + addAttrib(WGL_ACCUM_GREEN_BITS_ARB); + addAttrib(WGL_ACCUM_BLUE_BITS_ARB); + addAttrib(WGL_ACCUM_ALPHA_BITS_ARB); + addAttrib(WGL_AUX_BUFFERS_ARB); + addAttrib(WGL_STEREO_ARB); + addAttrib(WGL_DOUBLE_BUFFER_ARB); + + if (_glfw.wgl.ARB_multisample) + addAttrib(WGL_SAMPLES_ARB); + + if (ctxconfig->client == GLFW_OPENGL_API) + { + if (_glfw.wgl.ARB_framebuffer_sRGB || _glfw.wgl.EXT_framebuffer_sRGB) + addAttrib(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB); + } + else + { + if (_glfw.wgl.EXT_colorspace) + addAttrib(WGL_COLORSPACE_EXT); + } + } + else + { + nativeCount = DescribePixelFormat(window->context.wgl.dc, + 1, + sizeof(PIXELFORMATDESCRIPTOR), + NULL); + } + + usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); + + for (i = 0; i < nativeCount; i++) + { + _GLFWfbconfig* u = usableConfigs + usableCount; + pixelFormat = i + 1; + + if (_glfw.wgl.ARB_pixel_format) + { + // Get pixel format attributes through "modern" extension + + if (!wglGetPixelFormatAttribivARB(window->context.wgl.dc, + pixelFormat, 0, + attribCount, + attribs, values)) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to retrieve pixel format attributes"); + + free(usableConfigs); + return 0; + } + + if (!findAttribValue(WGL_SUPPORT_OPENGL_ARB) || + !findAttribValue(WGL_DRAW_TO_WINDOW_ARB)) + { + continue; + } + + if (findAttribValue(WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB) + continue; + + if (findAttribValue(WGL_ACCELERATION_ARB) == WGL_NO_ACCELERATION_ARB) + continue; + + u->redBits = findAttribValue(WGL_RED_BITS_ARB); + u->greenBits = findAttribValue(WGL_GREEN_BITS_ARB); + u->blueBits = findAttribValue(WGL_BLUE_BITS_ARB); + u->alphaBits = findAttribValue(WGL_ALPHA_BITS_ARB); + + u->depthBits = findAttribValue(WGL_DEPTH_BITS_ARB); + u->stencilBits = findAttribValue(WGL_STENCIL_BITS_ARB); + + u->accumRedBits = findAttribValue(WGL_ACCUM_RED_BITS_ARB); + u->accumGreenBits = findAttribValue(WGL_ACCUM_GREEN_BITS_ARB); + u->accumBlueBits = findAttribValue(WGL_ACCUM_BLUE_BITS_ARB); + u->accumAlphaBits = findAttribValue(WGL_ACCUM_ALPHA_BITS_ARB); + + u->auxBuffers = findAttribValue(WGL_AUX_BUFFERS_ARB); + + if (findAttribValue(WGL_STEREO_ARB)) + u->stereo = GLFW_TRUE; + if (findAttribValue(WGL_DOUBLE_BUFFER_ARB)) + u->doublebuffer = GLFW_TRUE; + + if (_glfw.wgl.ARB_multisample) + u->samples = findAttribValue(WGL_SAMPLES_ARB); + + if (ctxconfig->client == GLFW_OPENGL_API) + { + if (_glfw.wgl.ARB_framebuffer_sRGB || + _glfw.wgl.EXT_framebuffer_sRGB) + { + if (findAttribValue(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB)) + u->sRGB = GLFW_TRUE; + } + } + else + { + if (_glfw.wgl.EXT_colorspace) + { + if (findAttribValue(WGL_COLORSPACE_EXT) == WGL_COLORSPACE_SRGB_EXT) + u->sRGB = GLFW_TRUE; + } + } + } + else + { + // Get pixel format attributes through legacy PFDs + + PIXELFORMATDESCRIPTOR pfd; + + if (!DescribePixelFormat(window->context.wgl.dc, + pixelFormat, + sizeof(PIXELFORMATDESCRIPTOR), + &pfd)) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to describe pixel format"); + + free(usableConfigs); + return 0; + } + + if (!(pfd.dwFlags & PFD_DRAW_TO_WINDOW) || + !(pfd.dwFlags & PFD_SUPPORT_OPENGL)) + { + continue; + } + + if (!(pfd.dwFlags & PFD_GENERIC_ACCELERATED) && + (pfd.dwFlags & PFD_GENERIC_FORMAT)) + { + continue; + } + + if (pfd.iPixelType != PFD_TYPE_RGBA) + continue; + + u->redBits = pfd.cRedBits; + u->greenBits = pfd.cGreenBits; + u->blueBits = pfd.cBlueBits; + u->alphaBits = pfd.cAlphaBits; + + u->depthBits = pfd.cDepthBits; + u->stencilBits = pfd.cStencilBits; + + u->accumRedBits = pfd.cAccumRedBits; + u->accumGreenBits = pfd.cAccumGreenBits; + u->accumBlueBits = pfd.cAccumBlueBits; + u->accumAlphaBits = pfd.cAccumAlphaBits; + + u->auxBuffers = pfd.cAuxBuffers; + + if (pfd.dwFlags & PFD_STEREO) + u->stereo = GLFW_TRUE; + if (pfd.dwFlags & PFD_DOUBLEBUFFER) + u->doublebuffer = GLFW_TRUE; + } + + u->handle = pixelFormat; + usableCount++; + } + + if (!usableCount) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "WGL: The driver does not appear to support OpenGL"); + + free(usableConfigs); + return 0; + } + + closest = _glfwChooseFBConfig(fbconfig, usableConfigs, usableCount); + if (!closest) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "WGL: Failed to find a suitable pixel format"); + + free(usableConfigs); + return 0; + } + + pixelFormat = (int) closest->handle; + free(usableConfigs); + + return pixelFormat; +} + +#undef addAttrib +#undef findAttribValue + +static void makeContextCurrentWGL(_GLFWwindow* window) +{ + if (window) + { + if (wglMakeCurrent(window->context.wgl.dc, window->context.wgl.handle)) + _glfwPlatformSetTls(&_glfw.contextSlot, window); + else + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to make context current"); + _glfwPlatformSetTls(&_glfw.contextSlot, NULL); + } + } + else + { + if (!wglMakeCurrent(NULL, NULL)) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to clear current context"); + } + + _glfwPlatformSetTls(&_glfw.contextSlot, NULL); + } +} + +static void swapBuffersWGL(_GLFWwindow* window) +{ + if (!window->monitor) + { + if (IsWindowsVistaOrGreater()) + { + // DWM Composition is always enabled on Win8+ + BOOL enabled = IsWindows8OrGreater(); + + // HACK: Use DwmFlush when desktop composition is enabled + if (enabled || + (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled)) + { + int count = abs(window->context.wgl.interval); + while (count--) + DwmFlush(); + } + } + } + + SwapBuffers(window->context.wgl.dc); +} + +static void swapIntervalWGL(int interval) +{ + _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); + + window->context.wgl.interval = interval; + + if (!window->monitor) + { + if (IsWindowsVistaOrGreater()) + { + // DWM Composition is always enabled on Win8+ + BOOL enabled = IsWindows8OrGreater(); + + // HACK: Disable WGL swap interval when desktop composition is enabled to + // avoid interfering with DWM vsync + if (enabled || + (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled)) + interval = 0; + } + } + + if (_glfw.wgl.EXT_swap_control) + wglSwapIntervalEXT(interval); +} + +static int extensionSupportedWGL(const char* extension) +{ + const char* extensions = NULL; + + if (_glfw.wgl.GetExtensionsStringARB) + extensions = wglGetExtensionsStringARB(wglGetCurrentDC()); + else if (_glfw.wgl.GetExtensionsStringEXT) + extensions = wglGetExtensionsStringEXT(); + + if (!extensions) + return GLFW_FALSE; + + return _glfwStringInExtensionString(extension, extensions); +} + +static GLFWglproc getProcAddressWGL(const char* procname) +{ + const GLFWglproc proc = (GLFWglproc) wglGetProcAddress(procname); + if (proc) + return proc; + + return (GLFWglproc) GetProcAddress(_glfw.wgl.instance, procname); +} + +static void destroyContextWGL(_GLFWwindow* window) +{ + if (window->context.wgl.handle) + { + wglDeleteContext(window->context.wgl.handle); + window->context.wgl.handle = NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialize WGL +// +GLFWbool _glfwInitWGL(void) +{ + PIXELFORMATDESCRIPTOR pfd; + HGLRC prc, rc; + HDC pdc, dc; + + if (_glfw.wgl.instance) + return GLFW_TRUE; + + _glfw.wgl.instance = LoadLibraryA("opengl32.dll"); + if (!_glfw.wgl.instance) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to load opengl32.dll"); + return GLFW_FALSE; + } + + _glfw.wgl.CreateContext = (PFN_wglCreateContext) + GetProcAddress(_glfw.wgl.instance, "wglCreateContext"); + _glfw.wgl.DeleteContext = (PFN_wglDeleteContext) + GetProcAddress(_glfw.wgl.instance, "wglDeleteContext"); + _glfw.wgl.GetProcAddress = (PFN_wglGetProcAddress) + GetProcAddress(_glfw.wgl.instance, "wglGetProcAddress"); + _glfw.wgl.GetCurrentDC = (PFN_wglGetCurrentDC) + GetProcAddress(_glfw.wgl.instance, "wglGetCurrentDC"); + _glfw.wgl.GetCurrentContext = (PFN_wglGetCurrentContext) + GetProcAddress(_glfw.wgl.instance, "wglGetCurrentContext"); + _glfw.wgl.MakeCurrent = (PFN_wglMakeCurrent) + GetProcAddress(_glfw.wgl.instance, "wglMakeCurrent"); + _glfw.wgl.ShareLists = (PFN_wglShareLists) + GetProcAddress(_glfw.wgl.instance, "wglShareLists"); + + // NOTE: A dummy context has to be created for opengl32.dll to load the + // OpenGL ICD, from which we can then query WGL extensions + // NOTE: This code will accept the Microsoft GDI ICD; accelerated context + // creation failure occurs during manual pixel format enumeration + + dc = GetDC(_glfw.win32.helperWindowHandle); + + ZeroMemory(&pfd, sizeof(pfd)); + pfd.nSize = sizeof(pfd); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 24; + + if (!SetPixelFormat(dc, ChoosePixelFormat(dc, &pfd), &pfd)) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to set pixel format for dummy context"); + return GLFW_FALSE; + } + + rc = wglCreateContext(dc); + if (!rc) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to create dummy context"); + return GLFW_FALSE; + } + + pdc = wglGetCurrentDC(); + prc = wglGetCurrentContext(); + + if (!wglMakeCurrent(dc, rc)) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to make dummy context current"); + wglMakeCurrent(pdc, prc); + wglDeleteContext(rc); + return GLFW_FALSE; + } + + // NOTE: Functions must be loaded first as they're needed to retrieve the + // extension string that tells us whether the functions are supported + _glfw.wgl.GetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC) + wglGetProcAddress("wglGetExtensionsStringEXT"); + _glfw.wgl.GetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC) + wglGetProcAddress("wglGetExtensionsStringARB"); + _glfw.wgl.CreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) + wglGetProcAddress("wglCreateContextAttribsARB"); + _glfw.wgl.SwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) + wglGetProcAddress("wglSwapIntervalEXT"); + _glfw.wgl.GetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC) + wglGetProcAddress("wglGetPixelFormatAttribivARB"); + + // NOTE: WGL_ARB_extensions_string and WGL_EXT_extensions_string are not + // checked below as we are already using them + _glfw.wgl.ARB_multisample = + extensionSupportedWGL("WGL_ARB_multisample"); + _glfw.wgl.ARB_framebuffer_sRGB = + extensionSupportedWGL("WGL_ARB_framebuffer_sRGB"); + _glfw.wgl.EXT_framebuffer_sRGB = + extensionSupportedWGL("WGL_EXT_framebuffer_sRGB"); + _glfw.wgl.ARB_create_context = + extensionSupportedWGL("WGL_ARB_create_context"); + _glfw.wgl.ARB_create_context_profile = + extensionSupportedWGL("WGL_ARB_create_context_profile"); + _glfw.wgl.EXT_create_context_es2_profile = + extensionSupportedWGL("WGL_EXT_create_context_es2_profile"); + _glfw.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 = + extensionSupportedWGL("WGL_EXT_swap_control"); + _glfw.wgl.EXT_colorspace = + extensionSupportedWGL("WGL_EXT_colorspace"); + _glfw.wgl.ARB_pixel_format = + extensionSupportedWGL("WGL_ARB_pixel_format"); + _glfw.wgl.ARB_context_flush_control = + extensionSupportedWGL("WGL_ARB_context_flush_control"); + + wglMakeCurrent(pdc, prc); + wglDeleteContext(rc); + return GLFW_TRUE; +} + +// Terminate WGL +// +void _glfwTerminateWGL(void) +{ + if (_glfw.wgl.instance) + FreeLibrary(_glfw.wgl.instance); +} + +#define setAttrib(a, v) \ +{ \ + assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ + attribs[index++] = a; \ + attribs[index++] = v; \ +} + +// Create the OpenGL or OpenGL ES context +// +GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + int attribs[40]; + int pixelFormat; + PIXELFORMATDESCRIPTOR pfd; + HGLRC share = NULL; + + if (ctxconfig->share) + share = ctxconfig->share->context.wgl.handle; + + window->context.wgl.dc = GetDC(window->win32.handle); + if (!window->context.wgl.dc) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "WGL: Failed to retrieve DC for window"); + return GLFW_FALSE; + } + + pixelFormat = choosePixelFormat(window, ctxconfig, fbconfig); + if (!pixelFormat) + return GLFW_FALSE; + + if (!DescribePixelFormat(window->context.wgl.dc, + pixelFormat, sizeof(pfd), &pfd)) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to retrieve PFD for selected pixel format"); + return GLFW_FALSE; + } + + if (!SetPixelFormat(window->context.wgl.dc, pixelFormat, &pfd)) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to set selected pixel format"); + return GLFW_FALSE; + } + + if (ctxconfig->client == GLFW_OPENGL_API) + { + if (ctxconfig->forward) + { + if (!_glfw.wgl.ARB_create_context) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "WGL: A forward compatible OpenGL context requested but WGL_ARB_create_context is unavailable"); + return GLFW_FALSE; + } + } + + if (ctxconfig->profile) + { + if (!_glfw.wgl.ARB_create_context_profile) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "WGL: OpenGL profile requested but WGL_ARB_create_context_profile is unavailable"); + return GLFW_FALSE; + } + } + } + else + { + if (!_glfw.wgl.ARB_create_context || + !_glfw.wgl.ARB_create_context_profile || + !_glfw.wgl.EXT_create_context_es2_profile) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "WGL: OpenGL ES requested but WGL_ARB_create_context_es2_profile is unavailable"); + return GLFW_FALSE; + } + } + + if (_glfw.wgl.ARB_create_context) + { + int index = 0, mask = 0, flags = 0; + + if (ctxconfig->client == GLFW_OPENGL_API) + { + if (ctxconfig->forward) + flags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; + + if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) + mask |= WGL_CONTEXT_CORE_PROFILE_BIT_ARB; + else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) + mask |= WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; + } + else + mask |= WGL_CONTEXT_ES2_PROFILE_BIT_EXT; + + if (ctxconfig->debug) + flags |= WGL_CONTEXT_DEBUG_BIT_ARB; + + if (ctxconfig->robustness) + { + if (_glfw.wgl.ARB_create_context_robustness) + { + if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) + { + setAttrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + WGL_NO_RESET_NOTIFICATION_ARB); + } + else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) + { + setAttrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + WGL_LOSE_CONTEXT_ON_RESET_ARB); + } + + flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB; + } + } + + if (ctxconfig->release) + { + if (_glfw.wgl.ARB_context_flush_control) + { + if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) + { + setAttrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, + WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); + } + else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) + { + setAttrib(WGL_CONTEXT_RELEASE_BEHAVIOR_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 + // explicitly requesting version 1.0 does not always return the + // highest version supported by the driver + if (ctxconfig->major != 1 || ctxconfig->minor != 0) + { + setAttrib(WGL_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); + setAttrib(WGL_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); + } + + if (flags) + setAttrib(WGL_CONTEXT_FLAGS_ARB, flags); + + if (mask) + setAttrib(WGL_CONTEXT_PROFILE_MASK_ARB, mask); + + setAttrib(0, 0); + + window->context.wgl.handle = + wglCreateContextAttribsARB(window->context.wgl.dc, share, attribs); + if (!window->context.wgl.handle) + { + const DWORD error = GetLastError(); + + if (error == (0xc0070000 | ERROR_INVALID_VERSION_ARB)) + { + if (ctxconfig->client == GLFW_OPENGL_API) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "WGL: Driver does not support OpenGL version %i.%i", + ctxconfig->major, + ctxconfig->minor); + } + else + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "WGL: Driver does not support OpenGL ES version %i.%i", + ctxconfig->major, + ctxconfig->minor); + } + } + else if (error == (0xc0070000 | ERROR_INVALID_PROFILE_ARB)) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "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 + { + if (ctxconfig->client == GLFW_OPENGL_API) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "WGL: Failed to create OpenGL context"); + } + else + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "WGL: Failed to create OpenGL ES context"); + } + } + + return GLFW_FALSE; + } + } + else + { + window->context.wgl.handle = wglCreateContext(window->context.wgl.dc); + if (!window->context.wgl.handle) + { + _glfwInputErrorWin32(GLFW_VERSION_UNAVAILABLE, + "WGL: Failed to create OpenGL context"); + return GLFW_FALSE; + } + + if (share) + { + if (!wglShareLists(share, window->context.wgl.handle)) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to enable sharing with specified OpenGL context"); + return GLFW_FALSE; + } + } + } + + window->context.makeCurrent = makeContextCurrentWGL; + window->context.swapBuffers = swapBuffersWGL; + window->context.swapInterval = swapIntervalWGL; + window->context.extensionSupported = extensionSupportedWGL; + window->context.getProcAddress = getProcAddressWGL; + window->context.destroy = destroyContextWGL; + + return GLFW_TRUE; +} + +#undef setAttrib + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return NULL; + } + + return window->context.wgl.handle; +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/wgl_context.h b/source/MaterialXGraphEditor/External/Glfw/src/wgl_context.h new file mode 100644 index 0000000000..df983e91ca --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/wgl_context.h @@ -0,0 +1,164 @@ +//======================================================================== +// GLFW 3.4 WGL - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2018 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_TYPE_RGBA_ARB 0x202b +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_NO_ACCELERATION_ARB 0x2025 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_RED_SHIFT_ARB 0x2016 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_GREEN_SHIFT_ARB 0x2018 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_BLUE_SHIFT_ARB 0x201a +#define WGL_ALPHA_BITS_ARB 0x201b +#define WGL_ALPHA_SHIFT_ARB 0x201c +#define WGL_ACCUM_BITS_ARB 0x201d +#define WGL_ACCUM_RED_BITS_ARB 0x201e +#define WGL_ACCUM_GREEN_BITS_ARB 0x201f +#define WGL_ACCUM_BLUE_BITS_ARB 0x2020 +#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_AUX_BUFFERS_ARB 0x2024 +#define WGL_STEREO_ARB 0x2012 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_SAMPLES_ARB 0x2042 +#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9 +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 +#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define WGL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define WGL_NO_RESET_NOTIFICATION_ARB 0x8261 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 +#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_PROFILE_ARB 0x2096 +#define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054 + +// WGL extension pointer typedefs +typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int); +typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC,int,int,UINT,const int*,int*); +typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void); +typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC); +typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC,HGLRC,const int*); +#define wglSwapIntervalEXT _glfw.wgl.SwapIntervalEXT +#define wglGetPixelFormatAttribivARB _glfw.wgl.GetPixelFormatAttribivARB +#define wglGetExtensionsStringEXT _glfw.wgl.GetExtensionsStringEXT +#define wglGetExtensionsStringARB _glfw.wgl.GetExtensionsStringARB +#define wglCreateContextAttribsARB _glfw.wgl.CreateContextAttribsARB + +// opengl32.dll function pointer typedefs +typedef HGLRC (WINAPI * PFN_wglCreateContext)(HDC); +typedef BOOL (WINAPI * PFN_wglDeleteContext)(HGLRC); +typedef PROC (WINAPI * PFN_wglGetProcAddress)(LPCSTR); +typedef HDC (WINAPI * PFN_wglGetCurrentDC)(void); +typedef HGLRC (WINAPI * PFN_wglGetCurrentContext)(void); +typedef BOOL (WINAPI * PFN_wglMakeCurrent)(HDC,HGLRC); +typedef BOOL (WINAPI * PFN_wglShareLists)(HGLRC,HGLRC); +#define wglCreateContext _glfw.wgl.CreateContext +#define wglDeleteContext _glfw.wgl.DeleteContext +#define wglGetProcAddress _glfw.wgl.GetProcAddress +#define wglGetCurrentDC _glfw.wgl.GetCurrentDC +#define wglGetCurrentContext _glfw.wgl.GetCurrentContext +#define wglMakeCurrent _glfw.wgl.MakeCurrent +#define wglShareLists _glfw.wgl.ShareLists + +#define _GLFW_RECREATION_NOT_NEEDED 0 +#define _GLFW_RECREATION_REQUIRED 1 +#define _GLFW_RECREATION_IMPOSSIBLE 2 + +#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextWGL wgl +#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryWGL wgl + + +// WGL-specific per-context data +// +typedef struct _GLFWcontextWGL +{ + HDC dc; + HGLRC handle; + int interval; + +} _GLFWcontextWGL; + +// WGL-specific global data +// +typedef struct _GLFWlibraryWGL +{ + HINSTANCE instance; + PFN_wglCreateContext CreateContext; + PFN_wglDeleteContext DeleteContext; + PFN_wglGetProcAddress GetProcAddress; + PFN_wglGetCurrentDC GetCurrentDC; + PFN_wglGetCurrentContext GetCurrentContext; + PFN_wglMakeCurrent MakeCurrent; + PFN_wglShareLists ShareLists; + + PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT; + PFNWGLGETPIXELFORMATATTRIBIVARBPROC GetPixelFormatAttribivARB; + PFNWGLGETEXTENSIONSSTRINGEXTPROC GetExtensionsStringEXT; + PFNWGLGETEXTENSIONSSTRINGARBPROC GetExtensionsStringARB; + PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; + GLFWbool EXT_swap_control; + GLFWbool EXT_colorspace; + GLFWbool ARB_multisample; + GLFWbool ARB_framebuffer_sRGB; + GLFWbool EXT_framebuffer_sRGB; + GLFWbool ARB_pixel_format; + GLFWbool ARB_create_context; + GLFWbool ARB_create_context_profile; + GLFWbool EXT_create_context_es2_profile; + GLFWbool ARB_create_context_robustness; + GLFWbool ARB_create_context_no_error; + GLFWbool ARB_context_flush_control; + +} _GLFWlibraryWGL; + + +GLFWbool _glfwInitWGL(void); +void _glfwTerminateWGL(void); +GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig); + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/win32_init.c b/source/MaterialXGraphEditor/External/Glfw/src/win32_init.c new file mode 100644 index 0000000000..260e888e95 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/win32_init.c @@ -0,0 +1,631 @@ +//======================================================================== +// GLFW 3.4 Win32 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// Please use C89 style variable declarations in this file because VS 2010 +//======================================================================== + +#include "internal.h" + +#include +#include + +static const GUID _glfw_GUID_DEVINTERFACE_HID = + {0x4d1e55b2,0xf16f,0x11cf,{0x88,0xcb,0x00,0x11,0x11,0x00,0x00,0x30}}; + +#define GUID_DEVINTERFACE_HID _glfw_GUID_DEVINTERFACE_HID + +#if defined(_GLFW_USE_HYBRID_HPG) || defined(_GLFW_USE_OPTIMUS_HPG) + +// Executables (but not DLLs) exporting this symbol with this value will be +// automatically directed to the high-performance GPU on Nvidia Optimus systems +// with up-to-date drivers +// +__declspec(dllexport) DWORD NvOptimusEnablement = 1; + +// Executables (but not DLLs) exporting this symbol with this value will be +// automatically directed to the high-performance GPU on AMD PowerXpress systems +// with up-to-date drivers +// +__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; + +#endif // _GLFW_USE_HYBRID_HPG + +#if defined(_GLFW_BUILD_DLL) + +// GLFW DLL entry point +// +BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) +{ + return TRUE; +} + +#endif // _GLFW_BUILD_DLL + +// Load necessary libraries (DLLs) +// +static GLFWbool loadLibraries(void) +{ + _glfw.win32.winmm.instance = LoadLibraryA("winmm.dll"); + if (!_glfw.win32.winmm.instance) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to load winmm.dll"); + return GLFW_FALSE; + } + + _glfw.win32.winmm.GetTime = (PFN_timeGetTime) + GetProcAddress(_glfw.win32.winmm.instance, "timeGetTime"); + + _glfw.win32.user32.instance = LoadLibraryA("user32.dll"); + if (!_glfw.win32.user32.instance) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to load user32.dll"); + return GLFW_FALSE; + } + + _glfw.win32.user32.SetProcessDPIAware_ = (PFN_SetProcessDPIAware) + GetProcAddress(_glfw.win32.user32.instance, "SetProcessDPIAware"); + _glfw.win32.user32.ChangeWindowMessageFilterEx_ = (PFN_ChangeWindowMessageFilterEx) + GetProcAddress(_glfw.win32.user32.instance, "ChangeWindowMessageFilterEx"); + _glfw.win32.user32.EnableNonClientDpiScaling_ = (PFN_EnableNonClientDpiScaling) + GetProcAddress(_glfw.win32.user32.instance, "EnableNonClientDpiScaling"); + _glfw.win32.user32.SetProcessDpiAwarenessContext_ = (PFN_SetProcessDpiAwarenessContext) + GetProcAddress(_glfw.win32.user32.instance, "SetProcessDpiAwarenessContext"); + _glfw.win32.user32.GetDpiForWindow_ = (PFN_GetDpiForWindow) + GetProcAddress(_glfw.win32.user32.instance, "GetDpiForWindow"); + _glfw.win32.user32.AdjustWindowRectExForDpi_ = (PFN_AdjustWindowRectExForDpi) + GetProcAddress(_glfw.win32.user32.instance, "AdjustWindowRectExForDpi"); + + _glfw.win32.dinput8.instance = LoadLibraryA("dinput8.dll"); + if (_glfw.win32.dinput8.instance) + { + _glfw.win32.dinput8.Create = (PFN_DirectInput8Create) + GetProcAddress(_glfw.win32.dinput8.instance, "DirectInput8Create"); + } + + { + int i; + const char* names[] = + { + "xinput1_4.dll", + "xinput1_3.dll", + "xinput9_1_0.dll", + "xinput1_2.dll", + "xinput1_1.dll", + NULL + }; + + for (i = 0; names[i]; i++) + { + _glfw.win32.xinput.instance = LoadLibraryA(names[i]); + if (_glfw.win32.xinput.instance) + { + _glfw.win32.xinput.GetCapabilities = (PFN_XInputGetCapabilities) + GetProcAddress(_glfw.win32.xinput.instance, "XInputGetCapabilities"); + _glfw.win32.xinput.GetState = (PFN_XInputGetState) + GetProcAddress(_glfw.win32.xinput.instance, "XInputGetState"); + + break; + } + } + } + + _glfw.win32.dwmapi.instance = LoadLibraryA("dwmapi.dll"); + if (_glfw.win32.dwmapi.instance) + { + _glfw.win32.dwmapi.IsCompositionEnabled = (PFN_DwmIsCompositionEnabled) + GetProcAddress(_glfw.win32.dwmapi.instance, "DwmIsCompositionEnabled"); + _glfw.win32.dwmapi.Flush = (PFN_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"); + if (_glfw.win32.shcore.instance) + { + _glfw.win32.shcore.SetProcessDpiAwareness_ = (PFN_SetProcessDpiAwareness) + GetProcAddress(_glfw.win32.shcore.instance, "SetProcessDpiAwareness"); + _glfw.win32.shcore.GetDpiForMonitor_ = (PFN_GetDpiForMonitor) + GetProcAddress(_glfw.win32.shcore.instance, "GetDpiForMonitor"); + } + + _glfw.win32.ntdll.instance = LoadLibraryA("ntdll.dll"); + if (_glfw.win32.ntdll.instance) + { + _glfw.win32.ntdll.RtlVerifyVersionInfo_ = (PFN_RtlVerifyVersionInfo) + GetProcAddress(_glfw.win32.ntdll.instance, "RtlVerifyVersionInfo"); + } + + return GLFW_TRUE; +} + +// Unload used libraries (DLLs) +// +static void freeLibraries(void) +{ + if (_glfw.win32.xinput.instance) + FreeLibrary(_glfw.win32.xinput.instance); + + if (_glfw.win32.dinput8.instance) + FreeLibrary(_glfw.win32.dinput8.instance); + + if (_glfw.win32.winmm.instance) + FreeLibrary(_glfw.win32.winmm.instance); + + if (_glfw.win32.user32.instance) + FreeLibrary(_glfw.win32.user32.instance); + + if (_glfw.win32.dwmapi.instance) + FreeLibrary(_glfw.win32.dwmapi.instance); + + if (_glfw.win32.shcore.instance) + FreeLibrary(_glfw.win32.shcore.instance); + + if (_glfw.win32.ntdll.instance) + FreeLibrary(_glfw.win32.ntdll.instance); +} + +// Create key code translation tables +// +static void createKeyTables(void) +{ + int scancode; + + memset(_glfw.win32.keycodes, -1, sizeof(_glfw.win32.keycodes)); + memset(_glfw.win32.scancodes, -1, sizeof(_glfw.win32.scancodes)); + + _glfw.win32.keycodes[0x00B] = GLFW_KEY_0; + _glfw.win32.keycodes[0x002] = GLFW_KEY_1; + _glfw.win32.keycodes[0x003] = GLFW_KEY_2; + _glfw.win32.keycodes[0x004] = GLFW_KEY_3; + _glfw.win32.keycodes[0x005] = GLFW_KEY_4; + _glfw.win32.keycodes[0x006] = GLFW_KEY_5; + _glfw.win32.keycodes[0x007] = GLFW_KEY_6; + _glfw.win32.keycodes[0x008] = GLFW_KEY_7; + _glfw.win32.keycodes[0x009] = GLFW_KEY_8; + _glfw.win32.keycodes[0x00A] = GLFW_KEY_9; + _glfw.win32.keycodes[0x01E] = GLFW_KEY_A; + _glfw.win32.keycodes[0x030] = GLFW_KEY_B; + _glfw.win32.keycodes[0x02E] = GLFW_KEY_C; + _glfw.win32.keycodes[0x020] = GLFW_KEY_D; + _glfw.win32.keycodes[0x012] = GLFW_KEY_E; + _glfw.win32.keycodes[0x021] = GLFW_KEY_F; + _glfw.win32.keycodes[0x022] = GLFW_KEY_G; + _glfw.win32.keycodes[0x023] = GLFW_KEY_H; + _glfw.win32.keycodes[0x017] = GLFW_KEY_I; + _glfw.win32.keycodes[0x024] = GLFW_KEY_J; + _glfw.win32.keycodes[0x025] = GLFW_KEY_K; + _glfw.win32.keycodes[0x026] = GLFW_KEY_L; + _glfw.win32.keycodes[0x032] = GLFW_KEY_M; + _glfw.win32.keycodes[0x031] = GLFW_KEY_N; + _glfw.win32.keycodes[0x018] = GLFW_KEY_O; + _glfw.win32.keycodes[0x019] = GLFW_KEY_P; + _glfw.win32.keycodes[0x010] = GLFW_KEY_Q; + _glfw.win32.keycodes[0x013] = GLFW_KEY_R; + _glfw.win32.keycodes[0x01F] = GLFW_KEY_S; + _glfw.win32.keycodes[0x014] = GLFW_KEY_T; + _glfw.win32.keycodes[0x016] = GLFW_KEY_U; + _glfw.win32.keycodes[0x02F] = GLFW_KEY_V; + _glfw.win32.keycodes[0x011] = GLFW_KEY_W; + _glfw.win32.keycodes[0x02D] = GLFW_KEY_X; + _glfw.win32.keycodes[0x015] = GLFW_KEY_Y; + _glfw.win32.keycodes[0x02C] = GLFW_KEY_Z; + + _glfw.win32.keycodes[0x028] = GLFW_KEY_APOSTROPHE; + _glfw.win32.keycodes[0x02B] = GLFW_KEY_BACKSLASH; + _glfw.win32.keycodes[0x033] = GLFW_KEY_COMMA; + _glfw.win32.keycodes[0x00D] = GLFW_KEY_EQUAL; + _glfw.win32.keycodes[0x029] = GLFW_KEY_GRAVE_ACCENT; + _glfw.win32.keycodes[0x01A] = GLFW_KEY_LEFT_BRACKET; + _glfw.win32.keycodes[0x00C] = GLFW_KEY_MINUS; + _glfw.win32.keycodes[0x034] = GLFW_KEY_PERIOD; + _glfw.win32.keycodes[0x01B] = GLFW_KEY_RIGHT_BRACKET; + _glfw.win32.keycodes[0x027] = GLFW_KEY_SEMICOLON; + _glfw.win32.keycodes[0x035] = GLFW_KEY_SLASH; + _glfw.win32.keycodes[0x056] = GLFW_KEY_WORLD_2; + + _glfw.win32.keycodes[0x00E] = GLFW_KEY_BACKSPACE; + _glfw.win32.keycodes[0x153] = GLFW_KEY_DELETE; + _glfw.win32.keycodes[0x14F] = GLFW_KEY_END; + _glfw.win32.keycodes[0x01C] = GLFW_KEY_ENTER; + _glfw.win32.keycodes[0x001] = GLFW_KEY_ESCAPE; + _glfw.win32.keycodes[0x147] = GLFW_KEY_HOME; + _glfw.win32.keycodes[0x152] = GLFW_KEY_INSERT; + _glfw.win32.keycodes[0x15D] = GLFW_KEY_MENU; + _glfw.win32.keycodes[0x151] = GLFW_KEY_PAGE_DOWN; + _glfw.win32.keycodes[0x149] = GLFW_KEY_PAGE_UP; + _glfw.win32.keycodes[0x045] = GLFW_KEY_PAUSE; + _glfw.win32.keycodes[0x146] = GLFW_KEY_PAUSE; + _glfw.win32.keycodes[0x039] = GLFW_KEY_SPACE; + _glfw.win32.keycodes[0x00F] = GLFW_KEY_TAB; + _glfw.win32.keycodes[0x03A] = GLFW_KEY_CAPS_LOCK; + _glfw.win32.keycodes[0x145] = GLFW_KEY_NUM_LOCK; + _glfw.win32.keycodes[0x046] = GLFW_KEY_SCROLL_LOCK; + _glfw.win32.keycodes[0x03B] = GLFW_KEY_F1; + _glfw.win32.keycodes[0x03C] = GLFW_KEY_F2; + _glfw.win32.keycodes[0x03D] = GLFW_KEY_F3; + _glfw.win32.keycodes[0x03E] = GLFW_KEY_F4; + _glfw.win32.keycodes[0x03F] = GLFW_KEY_F5; + _glfw.win32.keycodes[0x040] = GLFW_KEY_F6; + _glfw.win32.keycodes[0x041] = GLFW_KEY_F7; + _glfw.win32.keycodes[0x042] = GLFW_KEY_F8; + _glfw.win32.keycodes[0x043] = GLFW_KEY_F9; + _glfw.win32.keycodes[0x044] = GLFW_KEY_F10; + _glfw.win32.keycodes[0x057] = GLFW_KEY_F11; + _glfw.win32.keycodes[0x058] = GLFW_KEY_F12; + _glfw.win32.keycodes[0x064] = GLFW_KEY_F13; + _glfw.win32.keycodes[0x065] = GLFW_KEY_F14; + _glfw.win32.keycodes[0x066] = GLFW_KEY_F15; + _glfw.win32.keycodes[0x067] = GLFW_KEY_F16; + _glfw.win32.keycodes[0x068] = GLFW_KEY_F17; + _glfw.win32.keycodes[0x069] = GLFW_KEY_F18; + _glfw.win32.keycodes[0x06A] = GLFW_KEY_F19; + _glfw.win32.keycodes[0x06B] = GLFW_KEY_F20; + _glfw.win32.keycodes[0x06C] = GLFW_KEY_F21; + _glfw.win32.keycodes[0x06D] = GLFW_KEY_F22; + _glfw.win32.keycodes[0x06E] = GLFW_KEY_F23; + _glfw.win32.keycodes[0x076] = GLFW_KEY_F24; + _glfw.win32.keycodes[0x038] = GLFW_KEY_LEFT_ALT; + _glfw.win32.keycodes[0x01D] = GLFW_KEY_LEFT_CONTROL; + _glfw.win32.keycodes[0x02A] = GLFW_KEY_LEFT_SHIFT; + _glfw.win32.keycodes[0x15B] = GLFW_KEY_LEFT_SUPER; + _glfw.win32.keycodes[0x137] = GLFW_KEY_PRINT_SCREEN; + _glfw.win32.keycodes[0x138] = GLFW_KEY_RIGHT_ALT; + _glfw.win32.keycodes[0x11D] = GLFW_KEY_RIGHT_CONTROL; + _glfw.win32.keycodes[0x036] = GLFW_KEY_RIGHT_SHIFT; + _glfw.win32.keycodes[0x15C] = GLFW_KEY_RIGHT_SUPER; + _glfw.win32.keycodes[0x150] = GLFW_KEY_DOWN; + _glfw.win32.keycodes[0x14B] = GLFW_KEY_LEFT; + _glfw.win32.keycodes[0x14D] = GLFW_KEY_RIGHT; + _glfw.win32.keycodes[0x148] = GLFW_KEY_UP; + + _glfw.win32.keycodes[0x052] = GLFW_KEY_KP_0; + _glfw.win32.keycodes[0x04F] = GLFW_KEY_KP_1; + _glfw.win32.keycodes[0x050] = GLFW_KEY_KP_2; + _glfw.win32.keycodes[0x051] = GLFW_KEY_KP_3; + _glfw.win32.keycodes[0x04B] = GLFW_KEY_KP_4; + _glfw.win32.keycodes[0x04C] = GLFW_KEY_KP_5; + _glfw.win32.keycodes[0x04D] = GLFW_KEY_KP_6; + _glfw.win32.keycodes[0x047] = GLFW_KEY_KP_7; + _glfw.win32.keycodes[0x048] = GLFW_KEY_KP_8; + _glfw.win32.keycodes[0x049] = GLFW_KEY_KP_9; + _glfw.win32.keycodes[0x04E] = GLFW_KEY_KP_ADD; + _glfw.win32.keycodes[0x053] = GLFW_KEY_KP_DECIMAL; + _glfw.win32.keycodes[0x135] = GLFW_KEY_KP_DIVIDE; + _glfw.win32.keycodes[0x11C] = GLFW_KEY_KP_ENTER; + _glfw.win32.keycodes[0x059] = GLFW_KEY_KP_EQUAL; + _glfw.win32.keycodes[0x037] = GLFW_KEY_KP_MULTIPLY; + _glfw.win32.keycodes[0x04A] = GLFW_KEY_KP_SUBTRACT; + + for (scancode = 0; scancode < 512; scancode++) + { + if (_glfw.win32.keycodes[scancode] > 0) + _glfw.win32.scancodes[_glfw.win32.keycodes[scancode]] = scancode; + } +} + +// Creates a dummy window for behind-the-scenes work +// +static GLFWbool createHelperWindow(void) +{ + MSG msg; + + _glfw.win32.helperWindowHandle = + CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, + _GLFW_WNDCLASSNAME, + L"GLFW message window", + WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + 0, 0, 1, 1, + NULL, NULL, + GetModuleHandleW(NULL), + NULL); + + if (!_glfw.win32.helperWindowHandle) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to create helper window"); + return GLFW_FALSE; + } + + // HACK: The command to the first ShowWindow call is ignored if the parent + // process passed along a STARTUPINFO, so clear that with a no-op call + ShowWindow(_glfw.win32.helperWindowHandle, SW_HIDE); + + // Register for HID device notifications + { + DEV_BROADCAST_DEVICEINTERFACE_W dbi; + ZeroMemory(&dbi, sizeof(dbi)); + dbi.dbcc_size = sizeof(dbi); + dbi.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + dbi.dbcc_classguid = GUID_DEVINTERFACE_HID; + + _glfw.win32.deviceNotificationHandle = + RegisterDeviceNotificationW(_glfw.win32.helperWindowHandle, + (DEV_BROADCAST_HDR*) &dbi, + DEVICE_NOTIFY_WINDOW_HANDLE); + } + + while (PeekMessageW(&msg, _glfw.win32.helperWindowHandle, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + return GLFW_TRUE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Returns a wide string version of the specified UTF-8 string +// +WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source) +{ + WCHAR* target; + int count; + + count = MultiByteToWideChar(CP_UTF8, 0, source, -1, NULL, 0); + if (!count) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert string from UTF-8"); + return NULL; + } + + target = calloc(count, sizeof(WCHAR)); + + if (!MultiByteToWideChar(CP_UTF8, 0, source, -1, target, count)) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert string from UTF-8"); + free(target); + return NULL; + } + + return target; +} + +// Returns a UTF-8 string version of the specified wide string +// +char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source) +{ + char* target; + int size; + + size = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL); + if (!size) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert string to UTF-8"); + return NULL; + } + + target = calloc(size, 1); + + if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, size, NULL, NULL)) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert string to UTF-8"); + free(target); + return NULL; + } + + return target; +} + +// Reports the specified error, appending information about the last Win32 error +// +void _glfwInputErrorWin32(int error, const char* description) +{ + WCHAR buffer[_GLFW_MESSAGE_SIZE] = L""; + char message[_GLFW_MESSAGE_SIZE] = ""; + + FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, + GetLastError() & 0xffff, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + buffer, + sizeof(buffer) / sizeof(WCHAR), + NULL); + WideCharToMultiByte(CP_UTF8, 0, buffer, -1, message, sizeof(message), NULL, NULL); + + _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); + } +} + +// Replacement for IsWindowsVersionOrGreater as MinGW lacks versionhelpers.h +// +BOOL _glfwIsWindowsVersionOrGreaterWin32(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); + // HACK: Use RtlVerifyVersionInfo instead of VerifyVersionInfoW as the + // latter lies unless the user knew to embed a non-default manifest + // announcing support for Windows 10 via supportedOS GUID + return RtlVerifyVersionInfo(&osvi, mask, cond) == 0; +} + +// Checks whether we are on at least the specified build of Windows 10 +// +BOOL _glfwIsWindows10BuildOrGreaterWin32(WORD build) +{ + OSVERSIONINFOEXW osvi = { sizeof(osvi), 10, 0, build }; + DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER; + ULONGLONG cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL); + cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL); + cond = VerSetConditionMask(cond, VER_BUILDNUMBER, VER_GREATER_EQUAL); + // HACK: Use RtlVerifyVersionInfo instead of VerifyVersionInfoW as the + // latter lies unless the user knew to embed a non-default manifest + // announcing support for Windows 10 via supportedOS GUID + return RtlVerifyVersionInfo(&osvi, mask, cond) == 0; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformInit(void) +{ + // To make SetForegroundWindow work as we want, we need to fiddle + // with the FOREGROUNDLOCKTIMEOUT system setting (we do this as early + // as possible in the hope of still being the foreground process) + SystemParametersInfoW(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, + &_glfw.win32.foregroundLockTimeout, 0); + SystemParametersInfoW(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, UIntToPtr(0), + SPIF_SENDCHANGE); + + if (!loadLibraries()) + return GLFW_FALSE; + + createKeyTables(); + _glfwUpdateKeyNamesWin32(); + + if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32()) + SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + else if (IsWindows8Point1OrGreater()) + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); + else if (IsWindowsVistaOrGreater()) + SetProcessDPIAware(); + + if (!_glfwRegisterWindowClassWin32()) + return GLFW_FALSE; + + if (!createHelperWindow()) + return GLFW_FALSE; + + _glfwInitTimerWin32(); + _glfwInitJoysticksWin32(); + + _glfwPollMonitorsWin32(); + return GLFW_TRUE; +} + +void _glfwPlatformTerminate(void) +{ + if (_glfw.win32.deviceNotificationHandle) + UnregisterDeviceNotification(_glfw.win32.deviceNotificationHandle); + + if (_glfw.win32.helperWindowHandle) + DestroyWindow(_glfw.win32.helperWindowHandle); + + _glfwUnregisterWindowClassWin32(); + + // Restore previous foreground lock timeout system setting + SystemParametersInfoW(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, + UIntToPtr(_glfw.win32.foregroundLockTimeout), + SPIF_SENDCHANGE); + + free(_glfw.win32.clipboardString); + free(_glfw.win32.rawInput); + + _glfwTerminateWGL(); + _glfwTerminateEGL(); + + _glfwTerminateJoysticksWin32(); + + freeLibraries(); +} + +const char* _glfwPlatformGetVersionString(void) +{ + return _GLFW_VERSION_NUMBER " Win32 WGL EGL OSMesa" +#if defined(__MINGW32__) + " MinGW" +#elif defined(_MSC_VER) + " VisualC" +#endif +#if defined(_GLFW_USE_HYBRID_HPG) || defined(_GLFW_USE_OPTIMUS_HPG) + " hybrid-GPU" +#endif +#if defined(_GLFW_BUILD_DLL) + " DLL" +#endif + ; +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/win32_joystick.c b/source/MaterialXGraphEditor/External/Glfw/src/win32_joystick.c new file mode 100644 index 0000000000..c19f77c5d0 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/win32_joystick.c @@ -0,0 +1,755 @@ +//======================================================================== +// GLFW 3.4 Win32 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// Please use C89 style variable declarations in this file because VS 2010 +//======================================================================== + +#include "internal.h" + +#include +#include + +#define _GLFW_TYPE_AXIS 0 +#define _GLFW_TYPE_SLIDER 1 +#define _GLFW_TYPE_BUTTON 2 +#define _GLFW_TYPE_POV 3 + +// Data produced with DirectInput device object enumeration +// +typedef struct _GLFWobjenumWin32 +{ + IDirectInputDevice8W* device; + _GLFWjoyobjectWin32* objects; + int objectCount; + int axisCount; + int sliderCount; + int buttonCount; + int povCount; +} _GLFWobjenumWin32; + +// Define local copies of the necessary GUIDs +// +static const GUID _glfw_IID_IDirectInput8W = + {0xbf798031,0x483a,0x4da2,{0xaa,0x99,0x5d,0x64,0xed,0x36,0x97,0x00}}; +static const GUID _glfw_GUID_XAxis = + {0xa36d02e0,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_YAxis = + {0xa36d02e1,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_ZAxis = + {0xa36d02e2,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_RxAxis = + {0xa36d02f4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_RyAxis = + {0xa36d02f5,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_RzAxis = + {0xa36d02e3,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_Slider = + {0xa36d02e4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_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 +// Generated with https://github.com/elmindreda/c_dfDIJoystick2 +// +static DIOBJECTDATAFORMAT _glfwObjectDataFormats[] = +{ + { &GUID_XAxis,DIJOFS_X,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_YAxis,DIJOFS_Y,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_ZAxis,DIJOFS_Z,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_RxAxis,DIJOFS_RX,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_RyAxis,DIJOFS_RY,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_RzAxis,DIJOFS_RZ,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_Slider,DIJOFS_SLIDER(0),DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_Slider,DIJOFS_SLIDER(1),DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_POV,DIJOFS_POV(0),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { &GUID_POV,DIJOFS_POV(1),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { &GUID_POV,DIJOFS_POV(2),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { &GUID_POV,DIJOFS_POV(3),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(0),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(1),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(2),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(3),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(4),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(5),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(6),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(7),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(8),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(9),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(10),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(11),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(12),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(13),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(14),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(15),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(16),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(17),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(18),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(19),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(20),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(21),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(22),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(23),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(24),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(25),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(26),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(27),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(28),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(29),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(30),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(31),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, +}; + +// Our clone of c_dfDIJoystick +// +static const DIDATAFORMAT _glfwDataFormat = +{ + sizeof(DIDATAFORMAT), + sizeof(DIOBJECTDATAFORMAT), + DIDFT_ABSAXIS, + sizeof(DIJOYSTATE), + sizeof(_glfwObjectDataFormats) / sizeof(DIOBJECTDATAFORMAT), + _glfwObjectDataFormats +}; + +// Returns a description fitting the specified XInput capabilities +// +static const char* getDeviceDescription(const XINPUT_CAPABILITIES* xic) +{ + switch (xic->SubType) + { + case XINPUT_DEVSUBTYPE_WHEEL: + return "XInput Wheel"; + case XINPUT_DEVSUBTYPE_ARCADE_STICK: + return "XInput Arcade Stick"; + case XINPUT_DEVSUBTYPE_FLIGHT_STICK: + return "XInput Flight Stick"; + case XINPUT_DEVSUBTYPE_DANCE_PAD: + return "XInput Dance Pad"; + case XINPUT_DEVSUBTYPE_GUITAR: + return "XInput Guitar"; + case XINPUT_DEVSUBTYPE_DRUM_KIT: + return "XInput Drum Kit"; + case XINPUT_DEVSUBTYPE_GAMEPAD: + { + if (xic->Flags & XINPUT_CAPS_WIRELESS) + return "Wireless Xbox Controller"; + else + return "Xbox Controller"; + } + } + + return "Unknown XInput Device"; +} + +// Lexically compare device objects +// +static int compareJoystickObjects(const void* first, const void* second) +{ + const _GLFWjoyobjectWin32* fo = first; + const _GLFWjoyobjectWin32* so = second; + + if (fo->type != so->type) + return fo->type - so->type; + + return fo->offset - so->offset; +} + +// Checks whether the specified device supports XInput +// Technique from FDInputJoystickManager::IsXInputDeviceFast in ZDoom +// +static GLFWbool supportsXInput(const GUID* guid) +{ + UINT i, count = 0; + RAWINPUTDEVICELIST* ridl; + GLFWbool result = GLFW_FALSE; + + if (GetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST)) != 0) + return GLFW_FALSE; + + ridl = calloc(count, sizeof(RAWINPUTDEVICELIST)); + + if (GetRawInputDeviceList(ridl, &count, sizeof(RAWINPUTDEVICELIST)) == (UINT) -1) + { + free(ridl); + return GLFW_FALSE; + } + + for (i = 0; i < count; i++) + { + RID_DEVICE_INFO rdi; + char name[256]; + UINT size; + + if (ridl[i].dwType != RIM_TYPEHID) + continue; + + ZeroMemory(&rdi, sizeof(rdi)); + rdi.cbSize = sizeof(rdi); + size = sizeof(rdi); + + if ((INT) GetRawInputDeviceInfoA(ridl[i].hDevice, + RIDI_DEVICEINFO, + &rdi, &size) == -1) + { + continue; + } + + if (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) != (LONG) guid->Data1) + continue; + + memset(name, 0, sizeof(name)); + size = sizeof(name); + + if ((INT) GetRawInputDeviceInfoA(ridl[i].hDevice, + RIDI_DEVICENAME, + name, &size) == -1) + { + break; + } + + name[sizeof(name) - 1] = '\0'; + if (strstr(name, "IG_")) + { + result = GLFW_TRUE; + break; + } + } + + free(ridl); + return result; +} + +// Frees all resources associated with the specified joystick +// +static void closeJoystick(_GLFWjoystick* js) +{ + if (js->win32.device) + { + IDirectInputDevice8_Unacquire(js->win32.device); + IDirectInputDevice8_Release(js->win32.device); + } + + free(js->win32.objects); + + _glfwFreeJoystick(js); + _glfwInputJoystick(js, GLFW_DISCONNECTED); +} + +// DirectInput device object enumeration callback +// Insights gleaned from SDL +// +static BOOL CALLBACK deviceObjectCallback(const DIDEVICEOBJECTINSTANCEW* doi, + void* user) +{ + _GLFWobjenumWin32* data = user; + _GLFWjoyobjectWin32* object = data->objects + data->objectCount; + + if (DIDFT_GETTYPE(doi->dwType) & DIDFT_AXIS) + { + DIPROPRANGE dipr; + + if (memcmp(&doi->guidType, &GUID_Slider, sizeof(GUID)) == 0) + object->offset = DIJOFS_SLIDER(data->sliderCount); + else if (memcmp(&doi->guidType, &GUID_XAxis, sizeof(GUID)) == 0) + object->offset = DIJOFS_X; + else if (memcmp(&doi->guidType, &GUID_YAxis, sizeof(GUID)) == 0) + object->offset = DIJOFS_Y; + else if (memcmp(&doi->guidType, &GUID_ZAxis, sizeof(GUID)) == 0) + object->offset = DIJOFS_Z; + else if (memcmp(&doi->guidType, &GUID_RxAxis, sizeof(GUID)) == 0) + object->offset = DIJOFS_RX; + else if (memcmp(&doi->guidType, &GUID_RyAxis, sizeof(GUID)) == 0) + object->offset = DIJOFS_RY; + else if (memcmp(&doi->guidType, &GUID_RzAxis, sizeof(GUID)) == 0) + object->offset = DIJOFS_RZ; + else + return DIENUM_CONTINUE; + + ZeroMemory(&dipr, sizeof(dipr)); + dipr.diph.dwSize = sizeof(dipr); + dipr.diph.dwHeaderSize = sizeof(dipr.diph); + dipr.diph.dwObj = doi->dwType; + dipr.diph.dwHow = DIPH_BYID; + dipr.lMin = -32768; + dipr.lMax = 32767; + + if (FAILED(IDirectInputDevice8_SetProperty(data->device, + DIPROP_RANGE, + &dipr.diph))) + { + return DIENUM_CONTINUE; + } + + if (memcmp(&doi->guidType, &GUID_Slider, sizeof(GUID)) == 0) + { + object->type = _GLFW_TYPE_SLIDER; + data->sliderCount++; + } + else + { + object->type = _GLFW_TYPE_AXIS; + data->axisCount++; + } + } + else if (DIDFT_GETTYPE(doi->dwType) & DIDFT_BUTTON) + { + object->offset = DIJOFS_BUTTON(data->buttonCount); + object->type = _GLFW_TYPE_BUTTON; + data->buttonCount++; + } + else if (DIDFT_GETTYPE(doi->dwType) & DIDFT_POV) + { + object->offset = DIJOFS_POV(data->povCount); + object->type = _GLFW_TYPE_POV; + data->povCount++; + } + + data->objectCount++; + return DIENUM_CONTINUE; +} + +// DirectInput device enumeration callback +// +static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) +{ + int jid = 0; + DIDEVCAPS dc; + DIPROPDWORD dipd; + IDirectInputDevice8* device; + _GLFWobjenumWin32 data; + _GLFWjoystick* js; + char guid[33]; + char name[256]; + + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + _GLFWjoystick* js = _glfw.joysticks + jid; + if (js->present) + { + if (memcmp(&js->win32.guid, &di->guidInstance, sizeof(GUID)) == 0) + return DIENUM_CONTINUE; + } + } + + if (supportsXInput(&di->guidProduct)) + return DIENUM_CONTINUE; + + if (FAILED(IDirectInput8_CreateDevice(_glfw.win32.dinput8.api, + &di->guidInstance, + &device, + NULL))) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create device"); + return DIENUM_CONTINUE; + } + + if (FAILED(IDirectInputDevice8_SetDataFormat(device, &_glfwDataFormat))) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to set device data format"); + + IDirectInputDevice8_Release(device); + return DIENUM_CONTINUE; + } + + ZeroMemory(&dc, sizeof(dc)); + dc.dwSize = sizeof(dc); + + if (FAILED(IDirectInputDevice8_GetCapabilities(device, &dc))) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to query device capabilities"); + + IDirectInputDevice8_Release(device); + return DIENUM_CONTINUE; + } + + ZeroMemory(&dipd, sizeof(dipd)); + dipd.diph.dwSize = sizeof(dipd); + dipd.diph.dwHeaderSize = sizeof(dipd.diph); + dipd.diph.dwHow = DIPH_DEVICE; + dipd.dwData = DIPROPAXISMODE_ABS; + + if (FAILED(IDirectInputDevice8_SetProperty(device, + DIPROP_AXISMODE, + &dipd.diph))) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to set device axis mode"); + + IDirectInputDevice8_Release(device); + return DIENUM_CONTINUE; + } + + memset(&data, 0, sizeof(data)); + data.device = device; + data.objects = calloc(dc.dwAxes + (size_t) dc.dwButtons + dc.dwPOVs, + sizeof(_GLFWjoyobjectWin32)); + + if (FAILED(IDirectInputDevice8_EnumObjects(device, + deviceObjectCallback, + &data, + DIDFT_AXIS | DIDFT_BUTTON | DIDFT_POV))) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to enumerate device objects"); + + IDirectInputDevice8_Release(device); + free(data.objects); + return DIENUM_CONTINUE; + } + + qsort(data.objects, data.objectCount, + sizeof(_GLFWjoyobjectWin32), + compareJoystickObjects); + + if (!WideCharToMultiByte(CP_UTF8, 0, + di->tszInstanceName, -1, + name, sizeof(name), + NULL, NULL)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert joystick name to UTF-8"); + + IDirectInputDevice8_Release(device); + free(data.objects); + return DIENUM_STOP; + } + + // 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.buttonCount, + data.povCount); + if (!js) + { + IDirectInputDevice8_Release(device); + free(data.objects); + return DIENUM_STOP; + } + + js->win32.device = device; + js->win32.guid = di->guidInstance; + js->win32.objects = data.objects; + js->win32.objectCount = data.objectCount; + + _glfwInputJoystick(js, GLFW_CONNECTED); + return DIENUM_CONTINUE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialize joystick interface +// +void _glfwInitJoysticksWin32(void) +{ + if (_glfw.win32.dinput8.instance) + { + if (FAILED(DirectInput8Create(GetModuleHandle(NULL), + DIRECTINPUT_VERSION, + &IID_IDirectInput8W, + (void**) &_glfw.win32.dinput8.api, + NULL))) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to create interface"); + } + } + + _glfwDetectJoystickConnectionWin32(); +} + +// Close all opened joystick handles +// +void _glfwTerminateJoysticksWin32(void) +{ + int jid; + + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) + closeJoystick(_glfw.joysticks + jid); + + if (_glfw.win32.dinput8.api) + IDirectInput8_Release(_glfw.win32.dinput8.api); +} + +// Checks for new joysticks after DBT_DEVICEARRIVAL +// +void _glfwDetectJoystickConnectionWin32(void) +{ + if (_glfw.win32.xinput.instance) + { + DWORD index; + + for (index = 0; index < XUSER_MAX_COUNT; index++) + { + int jid; + char guid[33]; + XINPUT_CAPABILITIES xic; + _GLFWjoystick* js; + + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (_glfw.joysticks[jid].present && + _glfw.joysticks[jid].win32.device == NULL && + _glfw.joysticks[jid].win32.index == index) + { + break; + } + } + + if (jid <= GLFW_JOYSTICK_LAST) + continue; + + if (XInputGetCapabilities(index, 0, &xic) != ERROR_SUCCESS) + continue; + + // 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) + continue; + + js->win32.index = index; + + _glfwInputJoystick(js, GLFW_CONNECTED); + } + } + + if (_glfw.win32.dinput8.api) + { + if (FAILED(IDirectInput8_EnumDevices(_glfw.win32.dinput8.api, + DI8DEVCLASS_GAMECTRL, + deviceCallback, + NULL, + DIEDFL_ALLDEVICES))) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Failed to enumerate DirectInput8 devices"); + return; + } + } +} + +// Checks for joystick disconnection after DBT_DEVICEREMOVECOMPLETE +// +void _glfwDetectJoystickDisconnectionWin32(void) +{ + int jid; + + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + _GLFWjoystick* js = _glfw.joysticks + jid; + if (js->present) + _glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE); + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) +{ + if (js->win32.device) + { + int i, ai = 0, bi = 0, pi = 0; + HRESULT result; + DIJOYSTATE state; + + IDirectInputDevice8_Poll(js->win32.device); + result = IDirectInputDevice8_GetDeviceState(js->win32.device, + sizeof(state), + &state); + if (result == DIERR_NOTACQUIRED || result == DIERR_INPUTLOST) + { + IDirectInputDevice8_Acquire(js->win32.device); + IDirectInputDevice8_Poll(js->win32.device); + result = IDirectInputDevice8_GetDeviceState(js->win32.device, + sizeof(state), + &state); + } + + if (FAILED(result)) + { + closeJoystick(js); + return GLFW_FALSE; + } + + if (mode == _GLFW_POLL_PRESENCE) + return GLFW_TRUE; + + for (i = 0; i < js->win32.objectCount; i++) + { + const void* data = (char*) &state + js->win32.objects[i].offset; + + switch (js->win32.objects[i].type) + { + case _GLFW_TYPE_AXIS: + case _GLFW_TYPE_SLIDER: + { + const float value = (*((LONG*) data) + 0.5f) / 32767.5f; + _glfwInputJoystickAxis(js, ai, value); + ai++; + break; + } + + case _GLFW_TYPE_BUTTON: + { + const char value = (*((BYTE*) data) & 0x80) != 0; + _glfwInputJoystickButton(js, bi, value); + bi++; + break; + } + + case _GLFW_TYPE_POV: + { + const int states[9] = + { + GLFW_HAT_UP, + GLFW_HAT_RIGHT_UP, + GLFW_HAT_RIGHT, + GLFW_HAT_RIGHT_DOWN, + GLFW_HAT_DOWN, + GLFW_HAT_LEFT_DOWN, + GLFW_HAT_LEFT, + GLFW_HAT_LEFT_UP, + GLFW_HAT_CENTERED + }; + + // Screams of horror are appropriate at this point + int state = LOWORD(*(DWORD*) data) / (45 * DI_DEGREES); + if (state < 0 || state > 8) + state = 8; + + _glfwInputJoystickHat(js, pi, states[state]); + pi++; + break; + } + } + } + } + else + { + int i, dpad = 0; + DWORD result; + XINPUT_STATE xis; + const WORD buttons[10] = + { + XINPUT_GAMEPAD_A, + XINPUT_GAMEPAD_B, + XINPUT_GAMEPAD_X, + XINPUT_GAMEPAD_Y, + XINPUT_GAMEPAD_LEFT_SHOULDER, + XINPUT_GAMEPAD_RIGHT_SHOULDER, + XINPUT_GAMEPAD_BACK, + XINPUT_GAMEPAD_START, + XINPUT_GAMEPAD_LEFT_THUMB, + XINPUT_GAMEPAD_RIGHT_THUMB + }; + + result = XInputGetState(js->win32.index, &xis); + if (result != ERROR_SUCCESS) + { + if (result == ERROR_DEVICE_NOT_CONNECTED) + closeJoystick(js); + + return GLFW_FALSE; + } + + if (mode == _GLFW_POLL_PRESENCE) + return GLFW_TRUE; + + _glfwInputJoystickAxis(js, 0, (xis.Gamepad.sThumbLX + 0.5f) / 32767.5f); + _glfwInputJoystickAxis(js, 1, -(xis.Gamepad.sThumbLY + 0.5f) / 32767.5f); + _glfwInputJoystickAxis(js, 2, (xis.Gamepad.sThumbRX + 0.5f) / 32767.5f); + _glfwInputJoystickAxis(js, 3, -(xis.Gamepad.sThumbRY + 0.5f) / 32767.5f); + _glfwInputJoystickAxis(js, 4, xis.Gamepad.bLeftTrigger / 127.5f - 1.f); + _glfwInputJoystickAxis(js, 5, xis.Gamepad.bRightTrigger / 127.5f - 1.f); + + for (i = 0; i < 10; i++) + { + const char value = (xis.Gamepad.wButtons & buttons[i]) ? 1 : 0; + _glfwInputJoystickButton(js, i, value); + } + + if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) + dpad |= GLFW_HAT_UP; + if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) + dpad |= GLFW_HAT_RIGHT; + if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) + dpad |= GLFW_HAT_DOWN; + if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) + dpad |= GLFW_HAT_LEFT; + + _glfwInputJoystickHat(js, 0, dpad); + } + + return GLFW_TRUE; +} + +void _glfwPlatformUpdateGamepadGUID(char* guid) +{ + if (strcmp(guid + 20, "504944564944") == 0) + { + char original[33]; + strncpy(original, guid, sizeof(original) - 1); + sprintf(guid, "03000000%.4s0000%.4s000000000000", + original, original + 4); + } +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/win32_joystick.h b/source/MaterialXGraphEditor/External/Glfw/src/win32_joystick.h new file mode 100644 index 0000000000..f593274e7a --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/win32_joystick.h @@ -0,0 +1,56 @@ +//======================================================================== +// GLFW 3.4 Win32 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2006-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickWin32 win32 +#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE struct { int dummyLibraryJoystick; } + +#define _GLFW_PLATFORM_MAPPING_NAME "Windows" + +// Joystick element (axis, button or slider) +// +typedef struct _GLFWjoyobjectWin32 +{ + int offset; + int type; +} _GLFWjoyobjectWin32; + +// Win32-specific per-joystick data +// +typedef struct _GLFWjoystickWin32 +{ + _GLFWjoyobjectWin32* objects; + int objectCount; + IDirectInputDevice8W* device; + DWORD index; + GUID guid; +} _GLFWjoystickWin32; + + +void _glfwInitJoysticksWin32(void); +void _glfwTerminateJoysticksWin32(void); +void _glfwDetectJoystickConnectionWin32(void); +void _glfwDetectJoystickDisconnectionWin32(void); + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/win32_monitor.c b/source/MaterialXGraphEditor/External/Glfw/src/win32_monitor.c new file mode 100644 index 0000000000..5f91c579be --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/win32_monitor.c @@ -0,0 +1,535 @@ +//======================================================================== +// GLFW 3.4 Win32 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// Please use C89 style variable declarations in this file because VS 2010 +//======================================================================== + +#include "internal.h" + +#include +#include +#include +#include +#include + + +// 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 +// +static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, + DISPLAY_DEVICEW* display) +{ + _GLFWmonitor* monitor; + int widthMM, heightMM; + char* name; + HDC dc; + DEVMODEW dm; + RECT rect; + + if (display) + name = _glfwCreateUTF8FromWideStringWin32(display->DeviceString); + else + name = _glfwCreateUTF8FromWideStringWin32(adapter->DeviceString); + if (!name) + 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); + + if (IsWindows8Point1OrGreater()) + { + 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); + + monitor = _glfwAllocMonitor(name, widthMM, heightMM); + free(name); + + if (adapter->StateFlags & DISPLAY_DEVICE_MODESPRUNED) + monitor->win32.modesPruned = GLFW_TRUE; + + wcscpy(monitor->win32.adapterName, adapter->DeviceName); + WideCharToMultiByte(CP_UTF8, 0, + adapter->DeviceName, -1, + monitor->win32.publicAdapterName, + sizeof(monitor->win32.publicAdapterName), + NULL, NULL); + + if (display) + { + wcscpy(monitor->win32.displayName, display->DeviceName); + WideCharToMultiByte(CP_UTF8, 0, + display->DeviceName, -1, + monitor->win32.publicDisplayName, + sizeof(monitor->win32.publicDisplayName), + NULL, NULL); + } + + 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; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Poll for changes in the set of connected monitors +// +void _glfwPollMonitorsWin32(void) +{ + int i, disconnectedCount; + _GLFWmonitor** disconnected = NULL; + DWORD adapterIndex, displayIndex; + DISPLAY_DEVICEW adapter, display; + _GLFWmonitor* monitor; + + disconnectedCount = _glfw.monitorCount; + if (disconnectedCount) + { + disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); + memcpy(disconnected, + _glfw.monitors, + _glfw.monitorCount * sizeof(_GLFWmonitor*)); + } + + for (adapterIndex = 0; ; adapterIndex++) + { + int type = _GLFW_INSERT_LAST; + + ZeroMemory(&adapter, sizeof(adapter)); + adapter.cb = sizeof(adapter); + + if (!EnumDisplayDevicesW(NULL, adapterIndex, &adapter, 0)) + break; + + if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE)) + continue; + + if (adapter.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) + type = _GLFW_INSERT_FIRST; + + for (displayIndex = 0; ; displayIndex++) + { + ZeroMemory(&display, sizeof(display)); + display.cb = sizeof(display); + + if (!EnumDisplayDevicesW(adapter.DeviceName, displayIndex, &display, 0)) + break; + + if (!(display.StateFlags & DISPLAY_DEVICE_ACTIVE)) + continue; + + for (i = 0; i < disconnectedCount; i++) + { + if (disconnected[i] && + wcscmp(disconnected[i]->win32.displayName, + display.DeviceName) == 0) + { + disconnected[i] = NULL; + break; + } + } + + if (i < disconnectedCount) + continue; + + monitor = createMonitor(&adapter, &display); + if (!monitor) + { + free(disconnected); + return; + } + + _glfwInputMonitor(monitor, GLFW_CONNECTED, type); + + type = _GLFW_INSERT_LAST; + } + + // HACK: If an active adapter does not have any display devices + // (as sometimes happens), add it directly as a monitor + if (displayIndex == 0) + { + for (i = 0; i < disconnectedCount; i++) + { + if (disconnected[i] && + wcscmp(disconnected[i]->win32.adapterName, + adapter.DeviceName) == 0) + { + disconnected[i] = NULL; + break; + } + } + + if (i < disconnectedCount) + continue; + + monitor = createMonitor(&adapter, NULL); + if (!monitor) + { + free(disconnected); + return; + } + + _glfwInputMonitor(monitor, GLFW_CONNECTED, type); + } + } + + for (i = 0; i < disconnectedCount; i++) + { + if (disconnected[i]) + _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); + } + + free(disconnected); +} + +// Change the current video mode +// +void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired) +{ + GLFWvidmode current; + const GLFWvidmode* best; + DEVMODEW dm; + LONG result; + + best = _glfwChooseVideoMode(monitor, desired); + _glfwPlatformGetVideoMode(monitor, ¤t); + if (_glfwCompareVideoModes(¤t, best) == 0) + return; + + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | + DM_DISPLAYFREQUENCY; + dm.dmPelsWidth = best->width; + dm.dmPelsHeight = best->height; + dm.dmBitsPerPel = best->redBits + best->greenBits + best->blueBits; + dm.dmDisplayFrequency = best->refreshRate; + + if (dm.dmBitsPerPel < 15 || dm.dmBitsPerPel >= 24) + dm.dmBitsPerPel = 32; + + result = ChangeDisplaySettingsExW(monitor->win32.adapterName, + &dm, + NULL, + CDS_FULLSCREEN, + NULL); + if (result == DISP_CHANGE_SUCCESSFUL) + monitor->win32.modeChanged = GLFW_TRUE; + else + { + const char* description = "Unknown error"; + + if (result == DISP_CHANGE_BADDUALVIEW) + description = "The system uses DualView"; + else if (result == DISP_CHANGE_BADFLAGS) + description = "Invalid flags"; + else if (result == DISP_CHANGE_BADMODE) + description = "Graphics mode not supported"; + else if (result == DISP_CHANGE_BADPARAM) + description = "Invalid parameter"; + else if (result == DISP_CHANGE_FAILED) + description = "Graphics mode failed"; + else if (result == DISP_CHANGE_NOTUPDATED) + description = "Failed to write to registry"; + else if (result == DISP_CHANGE_RESTART) + description = "Computer restart required"; + + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to set video mode: %s", + description); + } +} + +// Restore the previously saved (original) video mode +// +void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor) +{ + if (monitor->win32.modeChanged) + { + ChangeDisplaySettingsExW(monitor->win32.adapterName, + NULL, NULL, CDS_FULLSCREEN, NULL); + monitor->win32.modeChanged = GLFW_FALSE; + } +} + +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 / (float) USER_DEFAULT_SCREEN_DPI; + if (yscale) + *yscale = ydpi / (float) USER_DEFAULT_SCREEN_DPI; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) +{ +} + +void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) +{ + DEVMODEW dm; + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + + EnumDisplaySettingsExW(monitor->win32.adapterName, + ENUM_CURRENT_SETTINGS, + &dm, + EDS_ROTATEDMODE); + + if (xpos) + *xpos = dm.dmPosition.x; + if (ypos) + *ypos = dm.dmPosition.y; +} + +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + _glfwGetMonitorContentScaleWin32(monitor->win32.handle, xscale, yscale); +} + +void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, + int* xpos, int* ypos, + int* width, int* height) +{ + MONITORINFO mi = { sizeof(mi) }; + GetMonitorInfo(monitor->win32.handle, &mi); + + if (xpos) + *xpos = mi.rcWork.left; + if (ypos) + *ypos = mi.rcWork.top; + if (width) + *width = mi.rcWork.right - mi.rcWork.left; + if (height) + *height = mi.rcWork.bottom - mi.rcWork.top; +} + +GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) +{ + int modeIndex = 0, size = 0; + GLFWvidmode* result = NULL; + + *count = 0; + + for (;;) + { + int i; + GLFWvidmode mode; + DEVMODEW dm; + + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + + if (!EnumDisplaySettingsW(monitor->win32.adapterName, modeIndex, &dm)) + break; + + modeIndex++; + + // Skip modes with less than 15 BPP + if (dm.dmBitsPerPel < 15) + continue; + + mode.width = dm.dmPelsWidth; + mode.height = dm.dmPelsHeight; + mode.refreshRate = dm.dmDisplayFrequency; + _glfwSplitBPP(dm.dmBitsPerPel, + &mode.redBits, + &mode.greenBits, + &mode.blueBits); + + for (i = 0; i < *count; i++) + { + if (_glfwCompareVideoModes(result + i, &mode) == 0) + break; + } + + // Skip duplicate modes + if (i < *count) + continue; + + if (monitor->win32.modesPruned) + { + // Skip modes not supported by the connected displays + if (ChangeDisplaySettingsExW(monitor->win32.adapterName, + &dm, + NULL, + CDS_TEST, + NULL) != DISP_CHANGE_SUCCESSFUL) + { + continue; + } + } + + if (*count == size) + { + size += 128; + result = (GLFWvidmode*) realloc(result, size * sizeof(GLFWvidmode)); + } + + (*count)++; + result[*count - 1] = mode; + } + + if (!*count) + { + // HACK: Report the current mode if no valid modes were found + result = calloc(1, sizeof(GLFWvidmode)); + _glfwPlatformGetVideoMode(monitor, result); + *count = 1; + } + + return result; +} + +void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) +{ + DEVMODEW dm; + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + + EnumDisplaySettingsW(monitor->win32.adapterName, ENUM_CURRENT_SETTINGS, &dm); + + mode->width = dm.dmPelsWidth; + mode->height = dm.dmPelsHeight; + mode->refreshRate = dm.dmDisplayFrequency; + _glfwSplitBPP(dm.dmBitsPerPel, + &mode->redBits, + &mode->greenBits, + &mode->blueBits); +} + +GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +{ + HDC dc; + WORD values[3][256]; + + dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL); + GetDeviceGammaRamp(dc, values); + DeleteDC(dc); + + _glfwAllocGammaArrays(ramp, 256); + + memcpy(ramp->red, values[0], sizeof(values[0])); + memcpy(ramp->green, values[1], sizeof(values[1])); + memcpy(ramp->blue, values[2], sizeof(values[2])); + + return GLFW_TRUE; +} + +void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) +{ + HDC dc; + WORD values[3][256]; + + if (ramp->size != 256) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Gamma ramp size must be 256"); + return; + } + + memcpy(values[0], ramp->red, sizeof(values[0])); + memcpy(values[1], ramp->green, sizeof(values[1])); + memcpy(values[2], ramp->blue, sizeof(values[2])); + + dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL); + SetDeviceGammaRamp(dc, values); + DeleteDC(dc); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return monitor->win32.publicAdapterName; +} + +GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return monitor->win32.publicDisplayName; +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/win32_platform.h b/source/MaterialXGraphEditor/External/Glfw/src/win32_platform.h new file mode 100644 index 0000000000..2b00b0018a --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/win32_platform.h @@ -0,0 +1,459 @@ +//======================================================================== +// GLFW 3.4 Win32 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +// We don't need all the fancy stuff +#ifndef NOMINMAX + #define NOMINMAX +#endif + +#ifndef VC_EXTRALEAN + #define VC_EXTRALEAN +#endif + +#ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN +#endif + +// 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 +// callback) but windows.h assumes no one will define APIENTRY before it does +#undef APIENTRY + +// GLFW on Windows is Unicode only and does not work in MBCS mode +#ifndef UNICODE + #define UNICODE +#endif + +// GLFW requires Windows XP or later +#if WINVER < 0x0501 + #undef WINVER + #define WINVER 0x0501 +#endif +#if _WIN32_WINNT < 0x0501 + #undef _WIN32_WINNT + #define _WIN32_WINNT 0x0501 +#endif + +// GLFW uses DirectInput8 interfaces +#define DIRECTINPUT_VERSION 0x0800 + +// GLFW uses OEM cursor resources +#define OEMRESOURCE + +#include +#include +#include +#include +#include + +// HACK: Define macros that some windows.h variants don't +#ifndef WM_MOUSEHWHEEL + #define WM_MOUSEHWHEEL 0x020E +#endif +#ifndef WM_DWMCOMPOSITIONCHANGED + #define WM_DWMCOMPOSITIONCHANGED 0x031E +#endif +#ifndef WM_COPYGLOBALDATA + #define WM_COPYGLOBALDATA 0x0049 +#endif +#ifndef WM_UNICHAR + #define WM_UNICHAR 0x0109 +#endif +#ifndef UNICODE_NOCHAR + #define UNICODE_NOCHAR 0xFFFF +#endif +#ifndef WM_DPICHANGED + #define WM_DPICHANGED 0x02E0 +#endif +#ifndef GET_XBUTTON_WPARAM + #define GET_XBUTTON_WPARAM(w) (HIWORD(w)) +#endif +#ifndef EDS_ROTATEDMODE + #define EDS_ROTATEDMODE 0x00000004 +#endif +#ifndef DISPLAY_DEVICE_ACTIVE + #define DISPLAY_DEVICE_ACTIVE 0x00000001 +#endif +#ifndef _WIN32_WINNT_WINBLUE + #define _WIN32_WINNT_WINBLUE 0x0602 +#endif +#ifndef _WIN32_WINNT_WIN8 + #define _WIN32_WINNT_WIN8 0x0602 +#endif +#ifndef WM_GETDPISCALEDSIZE + #define WM_GETDPISCALEDSIZE 0x02e4 +#endif +#ifndef USER_DEFAULT_SCREEN_DPI + #define USER_DEFAULT_SCREEN_DPI 96 +#endif +#ifndef OCR_HAND + #define OCR_HAND 32649 +#endif + +#if WINVER < 0x0601 +typedef struct +{ + DWORD cbSize; + DWORD ExtStatus; +} CHANGEFILTERSTRUCT; +#ifndef MSGFLT_ALLOW + #define MSGFLT_ALLOW 1 +#endif +#endif /*Windows 7*/ + +#if WINVER < 0x0600 +#define DWM_BB_ENABLE 0x00000001 +#define DWM_BB_BLURREGION 0x00000002 +typedef struct +{ + DWORD dwFlags; + BOOL fEnable; + HRGN hRgnBlur; + BOOL fTransitionOnMaximized; +} DWM_BLURBEHIND; +#else + #include +#endif /*Windows Vista*/ + +#ifndef DPI_ENUMS_DECLARED +typedef enum +{ + PROCESS_DPI_UNAWARE = 0, + PROCESS_SYSTEM_DPI_AWARE = 1, + PROCESS_PER_MONITOR_DPI_AWARE = 2 +} 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*/ + +#ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 +#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((HANDLE) -4) +#endif /*DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2*/ + +// HACK: Define versionhelpers.h functions manually as MinGW lacks the header +#define IsWindowsXPOrGreater() \ + _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WINXP), \ + LOBYTE(_WIN32_WINNT_WINXP), 0) +#define IsWindowsVistaOrGreater() \ + _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_VISTA), \ + LOBYTE(_WIN32_WINNT_VISTA), 0) +#define IsWindows7OrGreater() \ + _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WIN7), \ + LOBYTE(_WIN32_WINNT_WIN7), 0) +#define IsWindows8OrGreater() \ + _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WIN8), \ + LOBYTE(_WIN32_WINNT_WIN8), 0) +#define IsWindows8Point1OrGreater() \ + _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WINBLUE), \ + LOBYTE(_WIN32_WINNT_WINBLUE), 0) + +#define _glfwIsWindows10AnniversaryUpdateOrGreaterWin32() \ + _glfwIsWindows10BuildOrGreaterWin32(14393) +#define _glfwIsWindows10CreatorsUpdateOrGreaterWin32() \ + _glfwIsWindows10BuildOrGreaterWin32(15063) + +// HACK: Define macros that some xinput.h variants don't +#ifndef XINPUT_CAPS_WIRELESS + #define XINPUT_CAPS_WIRELESS 0x0002 +#endif +#ifndef XINPUT_DEVSUBTYPE_WHEEL + #define XINPUT_DEVSUBTYPE_WHEEL 0x02 +#endif +#ifndef XINPUT_DEVSUBTYPE_ARCADE_STICK + #define XINPUT_DEVSUBTYPE_ARCADE_STICK 0x03 +#endif +#ifndef XINPUT_DEVSUBTYPE_FLIGHT_STICK + #define XINPUT_DEVSUBTYPE_FLIGHT_STICK 0x04 +#endif +#ifndef XINPUT_DEVSUBTYPE_DANCE_PAD + #define XINPUT_DEVSUBTYPE_DANCE_PAD 0x05 +#endif +#ifndef XINPUT_DEVSUBTYPE_GUITAR + #define XINPUT_DEVSUBTYPE_GUITAR 0x06 +#endif +#ifndef XINPUT_DEVSUBTYPE_DRUM_KIT + #define XINPUT_DEVSUBTYPE_DRUM_KIT 0x08 +#endif +#ifndef XINPUT_DEVSUBTYPE_ARCADE_PAD + #define XINPUT_DEVSUBTYPE_ARCADE_PAD 0x13 +#endif +#ifndef XUSER_MAX_COUNT + #define XUSER_MAX_COUNT 4 +#endif + +// HACK: Define macros that some dinput.h variants don't +#ifndef DIDFT_OPTIONAL + #define DIDFT_OPTIONAL 0x80000000 +#endif + +// winmm.dll function pointer typedefs +typedef DWORD (WINAPI * PFN_timeGetTime)(void); +#define timeGetTime _glfw.win32.winmm.GetTime + +// xinput.dll function pointer typedefs +typedef DWORD (WINAPI * PFN_XInputGetCapabilities)(DWORD,DWORD,XINPUT_CAPABILITIES*); +typedef DWORD (WINAPI * PFN_XInputGetState)(DWORD,XINPUT_STATE*); +#define XInputGetCapabilities _glfw.win32.xinput.GetCapabilities +#define XInputGetState _glfw.win32.xinput.GetState + +// dinput8.dll function pointer typedefs +typedef HRESULT (WINAPI * PFN_DirectInput8Create)(HINSTANCE,DWORD,REFIID,LPVOID*,LPUNKNOWN); +#define DirectInput8Create _glfw.win32.dinput8.Create + +// user32.dll function pointer typedefs +typedef BOOL (WINAPI * PFN_SetProcessDPIAware)(void); +typedef BOOL (WINAPI * PFN_ChangeWindowMessageFilterEx)(HWND,UINT,DWORD,CHANGEFILTERSTRUCT*); +typedef BOOL (WINAPI * PFN_EnableNonClientDpiScaling)(HWND); +typedef BOOL (WINAPI * PFN_SetProcessDpiAwarenessContext)(HANDLE); +typedef UINT (WINAPI * PFN_GetDpiForWindow)(HWND); +typedef BOOL (WINAPI * PFN_AdjustWindowRectExForDpi)(LPRECT,DWORD,BOOL,DWORD,UINT); +#define SetProcessDPIAware _glfw.win32.user32.SetProcessDPIAware_ +#define ChangeWindowMessageFilterEx _glfw.win32.user32.ChangeWindowMessageFilterEx_ +#define EnableNonClientDpiScaling _glfw.win32.user32.EnableNonClientDpiScaling_ +#define SetProcessDpiAwarenessContext _glfw.win32.user32.SetProcessDpiAwarenessContext_ +#define GetDpiForWindow _glfw.win32.user32.GetDpiForWindow_ +#define AdjustWindowRectExForDpi _glfw.win32.user32.AdjustWindowRectExForDpi_ + +// dwmapi.dll function pointer typedefs +typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*); +typedef HRESULT (WINAPI * PFN_DwmFlush)(VOID); +typedef HRESULT(WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND,const DWM_BLURBEHIND*); +#define DwmIsCompositionEnabled _glfw.win32.dwmapi.IsCompositionEnabled +#define DwmFlush _glfw.win32.dwmapi.Flush +#define DwmEnableBlurBehindWindow _glfw.win32.dwmapi.EnableBlurBehindWindow + +// shcore.dll function pointer typedefs +typedef HRESULT (WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); +typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*); +#define SetProcessDpiAwareness _glfw.win32.shcore.SetProcessDpiAwareness_ +#define GetDpiForMonitor _glfw.win32.shcore.GetDpiForMonitor_ + +// ntdll.dll function pointer typedefs +typedef LONG (WINAPI * PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*,ULONG,ULONGLONG); +#define RtlVerifyVersionInfo _glfw.win32.ntdll.RtlVerifyVersionInfo_ + +typedef VkFlags VkWin32SurfaceCreateFlagsKHR; + +typedef struct VkWin32SurfaceCreateInfoKHR +{ + VkStructureType sType; + const void* pNext; + VkWin32SurfaceCreateFlagsKHR flags; + HINSTANCE hinstance; + HWND hwnd; +} VkWin32SurfaceCreateInfoKHR; + +typedef VkResult (APIENTRY *PFN_vkCreateWin32SurfaceKHR)(VkInstance,const VkWin32SurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); +typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)(VkPhysicalDevice,uint32_t); + +#include "win32_joystick.h" +#include "wgl_context.h" +#include "egl_context.h" +#include "osmesa_context.h" + +#if !defined(_GLFW_WNDCLASSNAME) + #define _GLFW_WNDCLASSNAME L"GLFW30" +#endif + +#define _glfw_dlopen(name) LoadLibraryA(name) +#define _glfw_dlclose(handle) FreeLibrary((HMODULE) handle) +#define _glfw_dlsym(handle, name) GetProcAddress((HMODULE) handle, name) + +#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->win32.handle) +#define _GLFW_EGL_NATIVE_DISPLAY EGL_DEFAULT_DISPLAY + +#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowWin32 win32 +#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryWin32 win32 +#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerWin32 win32 +#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorWin32 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 +// +typedef struct _GLFWwindowWin32 +{ + HWND handle; + HICON bigIcon; + HICON smallIcon; + + GLFWbool cursorTracked; + GLFWbool frameAction; + GLFWbool iconified; + GLFWbool maximized; + // Whether to enable framebuffer transparency on DWM + GLFWbool transparent; + GLFWbool scaleToMonitor; + GLFWbool keymenu; + + // The last received cursor position, regardless of source + int lastCursorPosX, lastCursorPosY; + +} _GLFWwindowWin32; + +// Win32-specific global data +// +typedef struct _GLFWlibraryWin32 +{ + HWND helperWindowHandle; + HDEVNOTIFY deviceNotificationHandle; + DWORD foregroundLockTimeout; + int acquiredMonitorCount; + char* clipboardString; + short int keycodes[512]; + short int scancodes[GLFW_KEY_LAST + 1]; + char keynames[GLFW_KEY_LAST + 1][5]; + // Where to place the cursor when re-enabled + double restoreCursorPosX, restoreCursorPosY; + // The window whose disabled cursor mode is active + _GLFWwindow* disabledCursorWindow; + RAWINPUT* rawInput; + int rawInputSize; + UINT mouseTrailSize; + + struct { + HINSTANCE instance; + PFN_timeGetTime GetTime; + } winmm; + + struct { + HINSTANCE instance; + PFN_DirectInput8Create Create; + IDirectInput8W* api; + } dinput8; + + struct { + HINSTANCE instance; + PFN_XInputGetCapabilities GetCapabilities; + PFN_XInputGetState GetState; + } xinput; + + struct { + HINSTANCE instance; + PFN_SetProcessDPIAware SetProcessDPIAware_; + PFN_ChangeWindowMessageFilterEx ChangeWindowMessageFilterEx_; + PFN_EnableNonClientDpiScaling EnableNonClientDpiScaling_; + PFN_SetProcessDpiAwarenessContext SetProcessDpiAwarenessContext_; + PFN_GetDpiForWindow GetDpiForWindow_; + PFN_AdjustWindowRectExForDpi AdjustWindowRectExForDpi_; + } user32; + + struct { + HINSTANCE instance; + PFN_DwmIsCompositionEnabled IsCompositionEnabled; + PFN_DwmFlush Flush; + PFN_DwmEnableBlurBehindWindow EnableBlurBehindWindow; + } dwmapi; + + struct { + HINSTANCE instance; + PFN_SetProcessDpiAwareness SetProcessDpiAwareness_; + PFN_GetDpiForMonitor GetDpiForMonitor_; + } shcore; + + struct { + HINSTANCE instance; + PFN_RtlVerifyVersionInfo RtlVerifyVersionInfo_; + } ntdll; + +} _GLFWlibraryWin32; + +// Win32-specific per-monitor data +// +typedef struct _GLFWmonitorWin32 +{ + HMONITOR handle; + // This size matches the static size of DISPLAY_DEVICE.DeviceName + WCHAR adapterName[32]; + WCHAR displayName[32]; + char publicAdapterName[32]; + char publicDisplayName[32]; + GLFWbool modesPruned; + GLFWbool modeChanged; + +} _GLFWmonitorWin32; + +// Win32-specific per-cursor data +// +typedef struct _GLFWcursorWin32 +{ + HCURSOR handle; + +} _GLFWcursorWin32; + +// Win32-specific global timer data +// +typedef struct _GLFWtimerWin32 +{ + GLFWbool hasPC; + uint64_t frequency; + +} _GLFWtimerWin32; + +// Win32-specific thread local storage data +// +typedef struct _GLFWtlsWin32 +{ + GLFWbool allocated; + DWORD index; + +} _GLFWtlsWin32; + +// Win32-specific mutex data +// +typedef struct _GLFWmutexWin32 +{ + GLFWbool allocated; + CRITICAL_SECTION section; + +} _GLFWmutexWin32; + + +GLFWbool _glfwRegisterWindowClassWin32(void); +void _glfwUnregisterWindowClassWin32(void); + +WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source); +char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source); +BOOL _glfwIsWindowsVersionOrGreaterWin32(WORD major, WORD minor, WORD sp); +BOOL _glfwIsWindows10BuildOrGreaterWin32(WORD build); +void _glfwInputErrorWin32(int error, const char* description); +void _glfwUpdateKeyNamesWin32(void); + +void _glfwInitTimerWin32(void); + +void _glfwPollMonitorsWin32(void); +void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired); +void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor); +void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale); + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/win32_thread.c b/source/MaterialXGraphEditor/External/Glfw/src/win32_thread.c new file mode 100644 index 0000000000..53b34af263 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/win32_thread.c @@ -0,0 +1,99 @@ +//======================================================================== +// GLFW 3.4 Win32 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// Please use C89 style variable declarations in this file because VS 2010 +//======================================================================== + +#include "internal.h" + +#include + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls) +{ + 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; +} + +void _glfwPlatformDestroyTls(_GLFWtls* tls) +{ + 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); +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/win32_time.c b/source/MaterialXGraphEditor/External/Glfw/src/win32_time.c new file mode 100644 index 0000000000..721b0d0dff --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/win32_time.c @@ -0,0 +1,76 @@ +//======================================================================== +// GLFW 3.4 Win32 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// Please use C89 style variable declarations in this file because VS 2010 +//======================================================================== + +#include "internal.h" + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialise timer +// +void _glfwInitTimerWin32(void) +{ + uint64_t frequency; + + if (QueryPerformanceFrequency((LARGE_INTEGER*) &frequency)) + { + _glfw.timer.win32.hasPC = GLFW_TRUE; + _glfw.timer.win32.frequency = frequency; + } + else + { + _glfw.timer.win32.hasPC = GLFW_FALSE; + _glfw.timer.win32.frequency = 1000; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +uint64_t _glfwPlatformGetTimerValue(void) +{ + if (_glfw.timer.win32.hasPC) + { + uint64_t value; + QueryPerformanceCounter((LARGE_INTEGER*) &value); + return value; + } + else + return (uint64_t) timeGetTime(); +} + +uint64_t _glfwPlatformGetTimerFrequency(void) +{ + return _glfw.timer.win32.frequency; +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/win32_window.c b/source/MaterialXGraphEditor/External/Glfw/src/win32_window.c new file mode 100644 index 0000000000..0ae0998a36 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/win32_window.c @@ -0,0 +1,2265 @@ +//======================================================================== +// GLFW 3.4 Win32 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// Please use C89 style variable declarations in this file because VS 2010 +//======================================================================== + +#include "internal.h" + +#include +#include +#include +#include +#include +#include + +// Returns the window style for the specified window +// +static DWORD getWindowStyle(const _GLFWwindow* window) +{ + DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + + if (window->monitor) + style |= WS_POPUP; + else + { + style |= WS_SYSMENU | WS_MINIMIZEBOX; + + if (window->decorated) + { + style |= WS_CAPTION; + + if (window->resizable) + style |= WS_MAXIMIZEBOX | WS_THICKFRAME; + } + else + style |= WS_POPUP; + } + + return style; +} + +// Returns the extended window style for the specified window +// +static DWORD getWindowExStyle(const _GLFWwindow* window) +{ + DWORD style = WS_EX_APPWINDOW; + + if (window->monitor || window->floating) + style |= WS_EX_TOPMOST; + + return style; +} + +// Returns the image whose area most closely matches the desired one +// +static const GLFWimage* chooseImage(int count, const GLFWimage* images, + int width, int height) +{ + int i, leastDiff = INT_MAX; + const GLFWimage* closest = NULL; + + for (i = 0; i < count; i++) + { + const int currDiff = abs(images[i].width * images[i].height - + width * height); + if (currDiff < leastDiff) + { + closest = images + i; + leastDiff = currDiff; + } + } + + return closest; +} + +// Creates an RGBA icon or cursor +// +static HICON createIcon(const GLFWimage* image, + int xhot, int yhot, GLFWbool icon) +{ + int i; + HDC dc; + HICON handle; + HBITMAP color, mask; + BITMAPV5HEADER bi; + ICONINFO ii; + unsigned char* target = NULL; + unsigned char* source = image->pixels; + + ZeroMemory(&bi, sizeof(bi)); + bi.bV5Size = sizeof(bi); + bi.bV5Width = image->width; + bi.bV5Height = -image->height; + bi.bV5Planes = 1; + bi.bV5BitCount = 32; + bi.bV5Compression = BI_BITFIELDS; + bi.bV5RedMask = 0x00ff0000; + bi.bV5GreenMask = 0x0000ff00; + bi.bV5BlueMask = 0x000000ff; + bi.bV5AlphaMask = 0xff000000; + + dc = GetDC(NULL); + color = CreateDIBSection(dc, + (BITMAPINFO*) &bi, + DIB_RGB_COLORS, + (void**) &target, + NULL, + (DWORD) 0); + ReleaseDC(NULL, dc); + + if (!color) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to create RGBA bitmap"); + return NULL; + } + + mask = CreateBitmap(image->width, image->height, 1, 1, NULL); + if (!mask) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to create mask bitmap"); + DeleteObject(color); + return NULL; + } + + for (i = 0; i < image->width * image->height; i++) + { + target[0] = source[2]; + target[1] = source[1]; + target[2] = source[0]; + target[3] = source[3]; + target += 4; + source += 4; + } + + ZeroMemory(&ii, sizeof(ii)); + ii.fIcon = icon; + ii.xHotspot = xhot; + ii.yHotspot = yhot; + ii.hbmMask = mask; + ii.hbmColor = color; + + handle = CreateIconIndirect(&ii); + + DeleteObject(color); + DeleteObject(mask); + + if (!handle) + { + if (icon) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to create icon"); + } + else + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to create cursor"); + } + } + + return handle; +} + +// Translate content area size to full window size according to styles and DPI +// +static void getFullWindowSize(DWORD style, DWORD exStyle, + int contentWidth, int contentHeight, + int* fullWidth, int* fullHeight, + UINT dpi) +{ + RECT rect = { 0, 0, contentWidth, contentHeight }; + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, dpi); + else + AdjustWindowRectEx(&rect, style, FALSE, exStyle); + + *fullWidth = rect.right - rect.left; + *fullHeight = rect.bottom - rect.top; +} + +// Enforce the content area aspect ratio based on which edge is being dragged +// +static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area) +{ + int xoff, yoff; + UINT dpi = USER_DEFAULT_SCREEN_DPI; + const float ratio = (float) window->numer / (float) window->denom; + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + dpi = GetDpiForWindow(window->win32.handle); + + getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), + 0, 0, &xoff, &yoff, dpi); + + if (edge == WMSZ_LEFT || edge == WMSZ_BOTTOMLEFT || + edge == WMSZ_RIGHT || edge == WMSZ_BOTTOMRIGHT) + { + area->bottom = area->top + yoff + + (int) ((area->right - area->left - xoff) / ratio); + } + else if (edge == WMSZ_TOPLEFT || edge == WMSZ_TOPRIGHT) + { + area->top = area->bottom - yoff - + (int) ((area->right - area->left - xoff) / ratio); + } + else if (edge == WMSZ_TOP || edge == WMSZ_BOTTOM) + { + area->right = area->left + xoff + + (int) ((area->bottom - area->top - yoff) * ratio); + } +} + +// Updates the cursor image according to its cursor mode +// +static void updateCursorImage(_GLFWwindow* window) +{ + if (window->cursorMode == GLFW_CURSOR_NORMAL) + { + if (window->cursor) + SetCursor(window->cursor->win32.handle); + else + SetCursor(LoadCursorW(NULL, IDC_ARROW)); + } + else + SetCursor(NULL); +} + +// Updates the cursor clip rect +// +static void updateClipRect(_GLFWwindow* window) +{ + if (window) + { + RECT clipRect; + GetClientRect(window->win32.handle, &clipRect); + ClientToScreen(window->win32.handle, (POINT*) &clipRect.left); + ClientToScreen(window->win32.handle, (POINT*) &clipRect.right); + ClipCursor(&clipRect); + } + else + ClipCursor(NULL); +} + +// Enables WM_INPUT messages for the mouse for the specified window +// +static void enableRawMouseMotion(_GLFWwindow* window) +{ + const RAWINPUTDEVICE rid = { 0x01, 0x02, 0, window->win32.handle }; + + if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to register raw input device"); + } +} + +// Disables WM_INPUT messages for the mouse +// +static void disableRawMouseMotion(_GLFWwindow* window) +{ + const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL }; + + if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to remove raw input device"); + } +} + +// Apply disabled cursor mode to a focused window +// +static void disableCursor(_GLFWwindow* window) +{ + _glfw.win32.disabledCursorWindow = window; + _glfwPlatformGetCursorPos(window, + &_glfw.win32.restoreCursorPosX, + &_glfw.win32.restoreCursorPosY); + updateCursorImage(window); + _glfwCenterCursorInContentArea(window); + updateClipRect(window); + + if (window->rawMouseMotion) + enableRawMouseMotion(window); +} + +// Exit disabled cursor mode for the specified window +// +static void enableCursor(_GLFWwindow* window) +{ + if (window->rawMouseMotion) + disableRawMouseMotion(window); + + _glfw.win32.disabledCursorWindow = NULL; + updateClipRect(NULL); + _glfwPlatformSetCursorPos(window, + _glfw.win32.restoreCursorPosX, + _glfw.win32.restoreCursorPosY); + updateCursorImage(window); +} + +// Returns whether the cursor is in the content area of the specified window +// +static GLFWbool cursorInContentArea(_GLFWwindow* window) +{ + RECT area; + POINT pos; + + if (!GetCursorPos(&pos)) + return GLFW_FALSE; + + if (WindowFromPoint(pos) != window->win32.handle) + return GLFW_FALSE; + + GetClientRect(window->win32.handle, &area); + ClientToScreen(window->win32.handle, (POINT*) &area.left); + ClientToScreen(window->win32.handle, (POINT*) &area.right); + + return PtInRect(&area, pos); +} + +// Update native window styles to match attributes +// +static void updateWindowStyles(const _GLFWwindow* window) +{ + RECT rect; + DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); + style &= ~(WS_OVERLAPPEDWINDOW | WS_POPUP); + style |= getWindowStyle(window); + + GetClientRect(window->win32.handle, &rect); + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + { + AdjustWindowRectExForDpi(&rect, style, FALSE, + getWindowExStyle(window), + GetDpiForWindow(window->win32.handle)); + } + else + AdjustWindowRectEx(&rect, style, FALSE, getWindowExStyle(window)); + + ClientToScreen(window->win32.handle, (POINT*) &rect.left); + ClientToScreen(window->win32.handle, (POINT*) &rect.right); + SetWindowLongW(window->win32.handle, GWL_STYLE, style); + SetWindowPos(window->win32.handle, HWND_TOP, + rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, + SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER); +} + +// Update window framebuffer transparency +// +static void updateFramebufferTransparency(const _GLFWwindow* window) +{ + BOOL enabled; + + if (!IsWindowsVistaOrGreater()) + return; + + if (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled) + { + 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(255, 0, 255), 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); + } +} + +// Retrieves and translates modifier keys +// +static int getKeyMods(void) +{ + int mods = 0; + + if (GetKeyState(VK_SHIFT) & 0x8000) + mods |= GLFW_MOD_SHIFT; + if (GetKeyState(VK_CONTROL) & 0x8000) + mods |= GLFW_MOD_CONTROL; + if (GetKeyState(VK_MENU) & 0x8000) + mods |= GLFW_MOD_ALT; + if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x8000) + 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; +} + +static void fitToMonitor(_GLFWwindow* window) +{ + MONITORINFO mi = { sizeof(mi) }; + GetMonitorInfo(window->monitor->win32.handle, &mi); + SetWindowPos(window->win32.handle, HWND_TOPMOST, + mi.rcMonitor.left, + mi.rcMonitor.top, + mi.rcMonitor.right - mi.rcMonitor.left, + mi.rcMonitor.bottom - mi.rcMonitor.top, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS); +} + +// Make the specified window and its video mode active on its monitor +// +static void acquireMonitor(_GLFWwindow* window) +{ + if (!_glfw.win32.acquiredMonitorCount) + { + SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED); + + // HACK: When mouse trails are enabled the cursor becomes invisible when + // the OpenGL ICD switches to page flipping + if (IsWindowsXPOrGreater()) + { + SystemParametersInfo(SPI_GETMOUSETRAILS, 0, &_glfw.win32.mouseTrailSize, 0); + SystemParametersInfo(SPI_SETMOUSETRAILS, 0, 0, 0); + } + } + + if (!window->monitor->window) + _glfw.win32.acquiredMonitorCount++; + + _glfwSetVideoModeWin32(window->monitor, &window->videoMode); + _glfwInputMonitorWindow(window->monitor, window); +} + +// Remove the window and restore the original video mode +// +static void releaseMonitor(_GLFWwindow* window) +{ + if (window->monitor->window != window) + return; + + _glfw.win32.acquiredMonitorCount--; + if (!_glfw.win32.acquiredMonitorCount) + { + SetThreadExecutionState(ES_CONTINUOUS); + + // HACK: Restore mouse trail length saved in acquireMonitor + if (IsWindowsXPOrGreater()) + SystemParametersInfo(SPI_SETMOUSETRAILS, _glfw.win32.mouseTrailSize, 0, 0); + } + + _glfwInputMonitorWindow(window->monitor, NULL); + _glfwRestoreVideoModeWin32(window->monitor); +} + +// Window callback function (handles window messages) +// +static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, + WPARAM wParam, LPARAM lParam) +{ + _GLFWwindow* window = GetPropW(hWnd, L"GLFW"); + if (!window) + { + // This is the message handling for the hidden helper window + // and for a regular window during its initial creation + + switch (uMsg) + { + case WM_NCCREATE: + { + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + EnableNonClientDpiScaling(hWnd); + + break; + } + + case WM_DISPLAYCHANGE: + _glfwPollMonitorsWin32(); + break; + + case WM_DEVICECHANGE: + { + if (wParam == DBT_DEVICEARRIVAL) + { + DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; + if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) + _glfwDetectJoystickConnectionWin32(); + } + else if (wParam == DBT_DEVICEREMOVECOMPLETE) + { + DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; + if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) + _glfwDetectJoystickDisconnectionWin32(); + } + + break; + } + } + + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } + + 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) != HTCLIENT) + 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) + disableCursor(window); + + window->win32.frameAction = GLFW_FALSE; + } + + break; + } + + case WM_SETFOCUS: + { + _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) + disableCursor(window); + + return 0; + } + + case WM_KILLFOCUS: + { + if (window->cursorMode == GLFW_CURSOR_DISABLED) + enableCursor(window); + + if (window->monitor && window->autoIconify) + _glfwPlatformIconifyWindow(window); + + _glfwInputWindowFocus(window, GLFW_FALSE); + return 0; + } + + case WM_SYSCOMMAND: + { + switch (wParam & 0xfff0) + { + case SC_SCREENSAVE: + case SC_MONITORPOWER: + { + if (window->monitor) + { + // We are running in full screen mode, so disallow + // screen saver and screen blanking + return 0; + } + else + break; + } + + // User trying to access application menu using ALT? + case SC_KEYMENU: + { + if (!window->win32.keymenu) + return 0; + + break; + } + } + break; + } + + case WM_CLOSE: + { + _glfwInputWindowCloseRequest(window); + return 0; + } + + case WM_INPUTLANGCHANGE: + { + _glfwUpdateKeyNamesWin32(); + break; + } + + case WM_CHAR: + case WM_SYSCHAR: + case WM_UNICHAR: + { + const GLFWbool plain = (uMsg != WM_SYSCHAR); + + if (uMsg == WM_UNICHAR && wParam == UNICODE_NOCHAR) + { + // WM_UNICHAR is not sent by Windows, but is sent by some + // third-party input method engine + // Returning TRUE here announces support for this message + return TRUE; + } + + _glfwInputChar(window, (unsigned int) wParam, getKeyMods(), plain); + + if (uMsg == WM_SYSCHAR && window->win32.keymenu) + break; + + return 0; + } + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + case WM_KEYUP: + case WM_SYSKEYUP: + { + int key, scancode; + const int action = (HIWORD(lParam) & KF_UP) ? GLFW_RELEASE : GLFW_PRESS; + const int mods = getKeyMods(); + + scancode = (HIWORD(lParam) & (KF_EXTENDED | 0xff)); + if (!scancode) + { + // NOTE: Some synthetic key messages have a scancode of zero + // HACK: Map the virtual key back to a usable scancode + scancode = MapVirtualKeyW((UINT) wParam, MAPVK_VK_TO_VSC); + } + + key = _glfw.win32.keycodes[scancode]; + + // The Ctrl keys require special handling + if (wParam == VK_CONTROL) + { + if (HIWORD(lParam) & KF_EXTENDED) + { + // Right side keys have the extended key bit set + key = GLFW_KEY_RIGHT_CONTROL; + } + else + { + // NOTE: Alt Gr sends Left Ctrl followed by Right Alt + // HACK: We only want one event for Alt Gr, so if we detect + // this sequence we discard this Left Ctrl message now + // and later report Right Alt normally + MSG next; + const DWORD time = GetMessageTime(); + + if (PeekMessageW(&next, NULL, 0, 0, PM_NOREMOVE)) + { + if (next.message == WM_KEYDOWN || + next.message == WM_SYSKEYDOWN || + next.message == WM_KEYUP || + next.message == WM_SYSKEYUP) + { + if (next.wParam == VK_MENU && + (HIWORD(next.lParam) & KF_EXTENDED) && + next.time == time) + { + // Next message is Right Alt down so discard this + break; + } + } + } + + // This is a regular Left Ctrl message + key = GLFW_KEY_LEFT_CONTROL; + } + } + else if (wParam == VK_PROCESSKEY) + { + // IME notifies that keys have been filtered by setting the + // virtual key-code to VK_PROCESSKEY + break; + } + + if (action == GLFW_RELEASE && wParam == VK_SHIFT) + { + // HACK: Release both Shift keys on Shift up event, as when both + // are pressed the first release does not emit any event + // NOTE: The other half of this is in _glfwPlatformPollEvents + _glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, scancode, action, mods); + _glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, scancode, action, mods); + } + else if (wParam == VK_SNAPSHOT) + { + // HACK: Key down is not reported for the Print Screen key + _glfwInputKey(window, key, scancode, GLFW_PRESS, mods); + _glfwInputKey(window, key, scancode, GLFW_RELEASE, mods); + } + else + _glfwInputKey(window, key, scancode, action, mods); + + break; + } + + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_XBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: + case WM_XBUTTONUP: + { + int i, button, action; + + if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP) + button = GLFW_MOUSE_BUTTON_LEFT; + else if (uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONUP) + button = GLFW_MOUSE_BUTTON_RIGHT; + else if (uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP) + button = GLFW_MOUSE_BUTTON_MIDDLE; + else if (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) + button = GLFW_MOUSE_BUTTON_4; + else + button = GLFW_MOUSE_BUTTON_5; + + if (uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDOWN || + uMsg == WM_MBUTTONDOWN || uMsg == WM_XBUTTONDOWN) + { + action = GLFW_PRESS; + } + else + action = GLFW_RELEASE; + + for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) + { + if (window->mouseButtons[i] == GLFW_PRESS) + break; + } + + if (i > GLFW_MOUSE_BUTTON_LAST) + SetCapture(hWnd); + + _glfwInputMouseClick(window, button, action, getKeyMods()); + + for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) + { + if (window->mouseButtons[i] == GLFW_PRESS) + break; + } + + if (i > GLFW_MOUSE_BUTTON_LAST) + ReleaseCapture(); + + if (uMsg == WM_XBUTTONDOWN || uMsg == WM_XBUTTONUP) + return TRUE; + + return 0; + } + + case WM_MOUSEMOVE: + { + const int x = GET_X_LPARAM(lParam); + const int y = GET_Y_LPARAM(lParam); + + if (!window->win32.cursorTracked) + { + TRACKMOUSEEVENT tme; + ZeroMemory(&tme, sizeof(tme)); + tme.cbSize = sizeof(tme); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = window->win32.handle; + TrackMouseEvent(&tme); + + window->win32.cursorTracked = GLFW_TRUE; + _glfwInputCursorEnter(window, GLFW_TRUE); + } + + if (window->cursorMode == GLFW_CURSOR_DISABLED) + { + const int dx = x - window->win32.lastCursorPosX; + const int dy = y - window->win32.lastCursorPosY; + + if (_glfw.win32.disabledCursorWindow != window) + break; + if (window->rawMouseMotion) + break; + + _glfwInputCursorPos(window, + window->virtualCursorPosX + dx, + window->virtualCursorPosY + dy); + } + else + _glfwInputCursorPos(window, x, y); + + window->win32.lastCursorPosX = x; + window->win32.lastCursorPosY = y; + + return 0; + } + + case WM_INPUT: + { + UINT size = 0; + HRAWINPUT ri = (HRAWINPUT) lParam; + RAWINPUT* data = NULL; + int dx, dy; + + if (_glfw.win32.disabledCursorWindow != window) + break; + if (!window->rawMouseMotion) + break; + + GetRawInputData(ri, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)); + if (size > (UINT) _glfw.win32.rawInputSize) + { + free(_glfw.win32.rawInput); + _glfw.win32.rawInput = calloc(size, 1); + _glfw.win32.rawInputSize = size; + } + + size = _glfw.win32.rawInputSize; + if (GetRawInputData(ri, RID_INPUT, + _glfw.win32.rawInput, &size, + sizeof(RAWINPUTHEADER)) == (UINT) -1) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to retrieve raw input data"); + break; + } + + data = _glfw.win32.rawInput; + if (data->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) + { + dx = data->data.mouse.lLastX - window->win32.lastCursorPosX; + dy = data->data.mouse.lLastY - window->win32.lastCursorPosY; + } + else + { + dx = data->data.mouse.lLastX; + dy = data->data.mouse.lLastY; + } + + _glfwInputCursorPos(window, + window->virtualCursorPosX + dx, + window->virtualCursorPosY + dy); + + window->win32.lastCursorPosX += dx; + window->win32.lastCursorPosY += dy; + break; + } + + case WM_MOUSELEAVE: + { + window->win32.cursorTracked = GLFW_FALSE; + _glfwInputCursorEnter(window, GLFW_FALSE); + return 0; + } + + case WM_MOUSEWHEEL: + { + _glfwInputScroll(window, 0.0, (SHORT) HIWORD(wParam) / (double) WHEEL_DELTA); + return 0; + } + + case WM_MOUSEHWHEEL: + { + // This message is only sent on Windows Vista and later + // NOTE: The X-axis is inverted for consistency with macOS and X11 + _glfwInputScroll(window, -((SHORT) HIWORD(wParam) / (double) WHEEL_DELTA), 0.0); + return 0; + } + + case WM_ENTERSIZEMOVE: + case WM_ENTERMENULOOP: + { + if (window->win32.frameAction) + break; + + // HACK: Enable the cursor while the user is moving or + // resizing the window or using the window menu + if (window->cursorMode == GLFW_CURSOR_DISABLED) + enableCursor(window); + + break; + } + + case WM_EXITSIZEMOVE: + case WM_EXITMENULOOP: + { + if (window->win32.frameAction) + break; + + // HACK: Disable the cursor once the user is done moving or + // resizing the window or using the menu + if (window->cursorMode == GLFW_CURSOR_DISABLED) + disableCursor(window); + + break; + } + + case WM_SIZE: + { + const GLFWbool iconified = wParam == SIZE_MINIMIZED; + const GLFWbool maximized = wParam == SIZE_MAXIMIZED || + (window->win32.maximized && + wParam != SIZE_RESTORED); + + if (_glfw.win32.disabledCursorWindow == window) + updateClipRect(window); + + if (window->win32.iconified != iconified) + _glfwInputWindowIconify(window, iconified); + + if (window->win32.maximized != maximized) + _glfwInputWindowMaximize(window, maximized); + + _glfwInputFramebufferSize(window, LOWORD(lParam), HIWORD(lParam)); + _glfwInputWindowSize(window, LOWORD(lParam), HIWORD(lParam)); + + if (window->monitor && window->win32.iconified != iconified) + { + if (iconified) + releaseMonitor(window); + else + { + acquireMonitor(window); + fitToMonitor(window); + } + } + + window->win32.iconified = iconified; + window->win32.maximized = maximized; + return 0; + } + + case WM_MOVE: + { + if (_glfw.win32.disabledCursorWindow == window) + updateClipRect(window); + + // NOTE: This cannot use LOWORD/HIWORD recommended by MSDN, as + // those macros do not handle negative window positions correctly + _glfwInputWindowPos(window, + GET_X_LPARAM(lParam), + GET_Y_LPARAM(lParam)); + return 0; + } + + case WM_SIZING: + { + if (window->numer == GLFW_DONT_CARE || + window->denom == GLFW_DONT_CARE) + { + break; + } + + applyAspectRatio(window, (int) wParam, (RECT*) lParam); + return TRUE; + } + + case WM_GETMINMAXINFO: + { + int xoff, yoff; + UINT dpi = USER_DEFAULT_SCREEN_DPI; + MINMAXINFO* mmi = (MINMAXINFO*) lParam; + + if (window->monitor) + break; + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + dpi = GetDpiForWindow(window->win32.handle); + + getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), + 0, 0, &xoff, &yoff, dpi); + + if (window->minwidth != GLFW_DONT_CARE && + window->minheight != GLFW_DONT_CARE) + { + mmi->ptMinTrackSize.x = window->minwidth + xoff; + mmi->ptMinTrackSize.y = window->minheight + yoff; + } + + if (window->maxwidth != GLFW_DONT_CARE && + window->maxheight != GLFW_DONT_CARE) + { + mmi->ptMaxTrackSize.x = window->maxwidth + xoff; + 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; + } + + case WM_PAINT: + { + _glfwInputWindowDamage(window); + break; + } + + case WM_ERASEBKGND: + { + return TRUE; + } + + case WM_NCACTIVATE: + case WM_NCPAINT: + { + // Prevent title bar from being drawn after restoring a minimized + // undecorated window + if (!window->decorated) + return TRUE; + + break; + } + + case WM_DWMCOMPOSITIONCHANGED: + { + if (window->win32.transparent) + updateFramebufferTransparency(window); + return 0; + } + + case WM_GETDPISCALEDSIZE: + { + if (window->win32.scaleToMonitor) + break; + + // Adjust the window size to keep the content area size constant + if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32()) + { + RECT source = {0}, target = {0}; + SIZE* size = (SIZE*) lParam; + + AdjustWindowRectExForDpi(&source, getWindowStyle(window), + FALSE, getWindowExStyle(window), + GetDpiForWindow(window->win32.handle)); + AdjustWindowRectExForDpi(&target, getWindowStyle(window), + FALSE, getWindowExStyle(window), + LOWORD(wParam)); + + size->cx += (target.right - target.left) - + (source.right - source.left); + size->cy += (target.bottom - target.top) - + (source.bottom - source.top); + return TRUE; + } + + break; + } + + case WM_DPICHANGED: + { + const float xscale = HIWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI; + const float yscale = LOWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI; + + // Only apply the suggested size if the OS is new enough to have + // sent a WM_GETDPISCALEDSIZE before this + if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32()) + { + RECT* suggested = (RECT*) lParam; + SetWindowPos(window->win32.handle, HWND_TOP, + suggested->left, + suggested->top, + suggested->right - suggested->left, + suggested->bottom - suggested->top, + SWP_NOACTIVATE | SWP_NOZORDER); + } + + _glfwInputWindowContentScale(window, xscale, yscale); + break; + } + + case WM_SETCURSOR: + { + if (LOWORD(lParam) == HTCLIENT) + { + updateCursorImage(window); + return TRUE; + } + + break; + } + + case WM_DROPFILES: + { + HDROP drop = (HDROP) wParam; + POINT pt; + int i; + + const int count = DragQueryFileW(drop, 0xffffffff, NULL, 0); + char** paths = calloc(count, sizeof(char*)); + + // Move the mouse to the position of the drop + DragQueryPoint(drop, &pt); + _glfwInputCursorPos(window, pt.x, pt.y); + + for (i = 0; i < count; i++) + { + const UINT length = DragQueryFileW(drop, i, NULL, 0); + WCHAR* buffer = calloc((size_t) length + 1, sizeof(WCHAR)); + + DragQueryFileW(drop, i, buffer, length + 1); + paths[i] = _glfwCreateUTF8FromWideStringWin32(buffer); + + free(buffer); + } + + _glfwInputDrop(window, count, (const char**) paths); + + for (i = 0; i < count; i++) + free(paths[i]); + free(paths); + + DragFinish(drop); + return 0; + } + } + + return DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + +// Creates the GLFW window +// +static int createNativeWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWfbconfig* fbconfig) +{ + int xpos, ypos, fullWidth, fullHeight; + WCHAR* wideTitle; + DWORD style = getWindowStyle(window); + DWORD exStyle = getWindowExStyle(window); + + if (window->monitor) + { + GLFWvidmode mode; + + // NOTE: This window placement is temporary and approximate, as the + // correct position and size cannot be known until the monitor + // video mode has been picked in _glfwSetVideoModeWin32 + _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); + _glfwPlatformGetVideoMode(window->monitor, &mode); + fullWidth = mode.width; + fullHeight = mode.height; + } + else + { + xpos = CW_USEDEFAULT; + ypos = CW_USEDEFAULT; + + window->win32.maximized = wndconfig->maximized; + if (wndconfig->maximized) + style |= WS_MAXIMIZE; + + getFullWindowSize(style, exStyle, + wndconfig->width, wndconfig->height, + &fullWidth, &fullHeight, + USER_DEFAULT_SCREEN_DPI); + } + + wideTitle = _glfwCreateWideStringFromUTF8Win32(wndconfig->title); + if (!wideTitle) + return GLFW_FALSE; + + window->win32.handle = CreateWindowExW(exStyle, + _GLFW_WNDCLASSNAME, + wideTitle, + style, + xpos, ypos, + fullWidth, fullHeight, + NULL, // No parent window + NULL, // No window menu + GetModuleHandleW(NULL), + NULL); + + free(wideTitle); + + if (!window->win32.handle) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to create window"); + return GLFW_FALSE; + } + + SetPropW(window->win32.handle, L"GLFW", window); + + if (IsWindows7OrGreater()) + { + ChangeWindowMessageFilterEx(window->win32.handle, + WM_DROPFILES, MSGFLT_ALLOW, NULL); + ChangeWindowMessageFilterEx(window->win32.handle, + WM_COPYDATA, MSGFLT_ALLOW, NULL); + ChangeWindowMessageFilterEx(window->win32.handle, + WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL); + } + + window->win32.scaleToMonitor = wndconfig->scaleToMonitor; + window->win32.keymenu = wndconfig->win32.keymenu; + + // Adjust window rect to account for DPI scaling of the window frame and + // (if enabled) DPI scaling of the content area + // This cannot be done until we know what monitor the window was placed on + if (!window->monitor) + { + RECT rect = { 0, 0, wndconfig->width, wndconfig->height }; + WINDOWPLACEMENT wp = { sizeof(wp) }; + + if (wndconfig->scaleToMonitor) + { + float xscale, yscale; + _glfwPlatformGetWindowContentScale(window, &xscale, &yscale); + rect.right = (int) (rect.right * xscale); + rect.bottom = (int) (rect.bottom * yscale); + } + + ClientToScreen(window->win32.handle, (POINT*) &rect.left); + ClientToScreen(window->win32.handle, (POINT*) &rect.right); + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + { + AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, + GetDpiForWindow(window->win32.handle)); + } + else + AdjustWindowRectEx(&rect, style, FALSE, exStyle); + + // Only update the restored window rect as the window may be maximized + GetWindowPlacement(window->win32.handle, &wp); + wp.rcNormalPosition = rect; + wp.showCmd = SW_HIDE; + SetWindowPlacement(window->win32.handle, &wp); + } + + DragAcceptFiles(window->win32.handle, TRUE); + + if (fbconfig->transparent) + { + updateFramebufferTransparency(window); + window->win32.transparent = GLFW_TRUE; + } + + return GLFW_TRUE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Registers the GLFW window class +// +GLFWbool _glfwRegisterWindowClassWin32(void) +{ + WNDCLASSEXW wc; + + ZeroMemory(&wc, sizeof(wc)); + wc.cbSize = sizeof(wc); + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wc.lpfnWndProc = (WNDPROC) windowProc; + wc.hInstance = GetModuleHandleW(NULL); + wc.hCursor = LoadCursorW(NULL, IDC_ARROW); + wc.lpszClassName = _GLFW_WNDCLASSNAME; + + // Load user-provided icon if available + wc.hIcon = LoadImageW(GetModuleHandleW(NULL), + L"GLFW_ICON", IMAGE_ICON, + 0, 0, LR_DEFAULTSIZE | LR_SHARED); + if (!wc.hIcon) + { + // No user-provided icon found, load default icon + wc.hIcon = LoadImageW(NULL, + IDI_APPLICATION, IMAGE_ICON, + 0, 0, LR_DEFAULTSIZE | LR_SHARED); + } + + if (!RegisterClassExW(&wc)) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to register window class"); + return GLFW_FALSE; + } + + return GLFW_TRUE; +} + +// Unregisters the GLFW window class +// +void _glfwUnregisterWindowClassWin32(void) +{ + UnregisterClassW(_GLFW_WNDCLASSNAME, GetModuleHandleW(NULL)); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformCreateWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + if (!createNativeWindow(window, wndconfig, fbconfig)) + return GLFW_FALSE; + + if (ctxconfig->client != GLFW_NO_API) + { + if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) + { + if (!_glfwInitWGL()) + return GLFW_FALSE; + if (!_glfwCreateContextWGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) + { + if (!_glfwInitEGL()) + return GLFW_FALSE; + if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + } + + if (window->monitor) + { + _glfwPlatformShowWindow(window); + _glfwPlatformFocusWindow(window); + acquireMonitor(window); + fitToMonitor(window); + } + + return GLFW_TRUE; +} + +void _glfwPlatformDestroyWindow(_GLFWwindow* window) +{ + if (window->monitor) + releaseMonitor(window); + + if (window->context.destroy) + window->context.destroy(window); + + if (_glfw.win32.disabledCursorWindow == window) + _glfw.win32.disabledCursorWindow = NULL; + + if (window->win32.handle) + { + RemovePropW(window->win32.handle, L"GLFW"); + DestroyWindow(window->win32.handle); + window->win32.handle = NULL; + } + + if (window->win32.bigIcon) + DestroyIcon(window->win32.bigIcon); + + if (window->win32.smallIcon) + DestroyIcon(window->win32.smallIcon); +} + +void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) +{ + WCHAR* wideTitle = _glfwCreateWideStringFromUTF8Win32(title); + if (!wideTitle) + return; + + SetWindowTextW(window->win32.handle, wideTitle); + free(wideTitle); +} + +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, + int count, const GLFWimage* images) +{ + HICON bigIcon = NULL, smallIcon = NULL; + + if (count) + { + const GLFWimage* bigImage = chooseImage(count, images, + GetSystemMetrics(SM_CXICON), + GetSystemMetrics(SM_CYICON)); + const GLFWimage* smallImage = chooseImage(count, images, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON)); + + bigIcon = createIcon(bigImage, 0, 0, GLFW_TRUE); + smallIcon = createIcon(smallImage, 0, 0, GLFW_TRUE); + } + else + { + bigIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICON); + smallIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICONSM); + } + + SendMessage(window->win32.handle, WM_SETICON, ICON_BIG, (LPARAM) bigIcon); + SendMessage(window->win32.handle, WM_SETICON, ICON_SMALL, (LPARAM) smallIcon); + + if (window->win32.bigIcon) + DestroyIcon(window->win32.bigIcon); + + if (window->win32.smallIcon) + DestroyIcon(window->win32.smallIcon); + + if (count) + { + window->win32.bigIcon = bigIcon; + window->win32.smallIcon = smallIcon; + } +} + +void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) +{ + POINT pos = { 0, 0 }; + ClientToScreen(window->win32.handle, &pos); + + if (xpos) + *xpos = pos.x; + if (ypos) + *ypos = pos.y; +} + +void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) +{ + RECT rect = { xpos, ypos, xpos, ypos }; + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + { + AdjustWindowRectExForDpi(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window), + GetDpiForWindow(window->win32.handle)); + } + else + { + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); + } + + SetWindowPos(window->win32.handle, NULL, rect.left, rect.top, 0, 0, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE); +} + +void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) +{ + RECT area; + GetClientRect(window->win32.handle, &area); + + if (width) + *width = area.right; + if (height) + *height = area.bottom; +} + +void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) +{ + if (window->monitor) + { + if (window->monitor->window == window) + { + acquireMonitor(window); + fitToMonitor(window); + } + } + else + { + RECT rect = { 0, 0, width, height }; + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + { + AdjustWindowRectExForDpi(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window), + GetDpiForWindow(window->win32.handle)); + } + else + { + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); + } + + SetWindowPos(window->win32.handle, HWND_TOP, + 0, 0, rect.right - rect.left, rect.bottom - rect.top, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER); + } +} + +void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, + int minwidth, int minheight, + int maxwidth, int maxheight) +{ + RECT area; + + if ((minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) && + (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE)) + { + return; + } + + GetWindowRect(window->win32.handle, &area); + MoveWindow(window->win32.handle, + area.left, area.top, + area.right - area.left, + area.bottom - area.top, TRUE); +} + +void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) +{ + RECT area; + + if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE) + return; + + GetWindowRect(window->win32.handle, &area); + applyAspectRatio(window, WMSZ_BOTTOMRIGHT, &area); + MoveWindow(window->win32.handle, + area.left, area.top, + area.right - area.left, + area.bottom - area.top, TRUE); +} + +void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) +{ + _glfwPlatformGetWindowSize(window, width, height); +} + +void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, + int* left, int* top, + int* right, int* bottom) +{ + RECT rect; + int width, height; + + _glfwPlatformGetWindowSize(window, &width, &height); + SetRect(&rect, 0, 0, width, height); + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + { + AdjustWindowRectExForDpi(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window), + GetDpiForWindow(window->win32.handle)); + } + else + { + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); + } + + if (left) + *left = -rect.left; + if (top) + *top = -rect.top; + if (right) + *right = rect.right - width; + if (bottom) + *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) +{ + ShowWindow(window->win32.handle, SW_MINIMIZE); +} + +void _glfwPlatformRestoreWindow(_GLFWwindow* window) +{ + ShowWindow(window->win32.handle, SW_RESTORE); +} + +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ + ShowWindow(window->win32.handle, SW_MAXIMIZE); +} + +void _glfwPlatformShowWindow(_GLFWwindow* window) +{ + ShowWindow(window->win32.handle, SW_SHOWNA); +} + +void _glfwPlatformHideWindow(_GLFWwindow* window) +{ + ShowWindow(window->win32.handle, SW_HIDE); +} + +void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) +{ + FlashWindow(window->win32.handle, TRUE); +} + +void _glfwPlatformFocusWindow(_GLFWwindow* window) +{ + BringWindowToTop(window->win32.handle); + SetForegroundWindow(window->win32.handle); + SetFocus(window->win32.handle); +} + +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) +{ + if (window->monitor == monitor) + { + if (monitor) + { + if (monitor->window == window) + { + acquireMonitor(window); + fitToMonitor(window); + } + } + else + { + RECT rect = { xpos, ypos, xpos + width, ypos + height }; + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + { + AdjustWindowRectExForDpi(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window), + GetDpiForWindow(window->win32.handle)); + } + else + { + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); + } + + SetWindowPos(window->win32.handle, HWND_TOP, + rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, + SWP_NOCOPYBITS | SWP_NOACTIVATE | SWP_NOZORDER); + } + + return; + } + + if (window->monitor) + releaseMonitor(window); + + _glfwInputWindowMonitor(window, monitor); + + if (window->monitor) + { + MONITORINFO mi = { sizeof(mi) }; + UINT flags = SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOCOPYBITS; + + if (window->decorated) + { + DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); + style &= ~WS_OVERLAPPEDWINDOW; + style |= getWindowStyle(window); + SetWindowLongW(window->win32.handle, GWL_STYLE, style); + flags |= SWP_FRAMECHANGED; + } + + acquireMonitor(window); + + GetMonitorInfo(window->monitor->win32.handle, &mi); + SetWindowPos(window->win32.handle, HWND_TOPMOST, + mi.rcMonitor.left, + mi.rcMonitor.top, + mi.rcMonitor.right - mi.rcMonitor.left, + mi.rcMonitor.bottom - mi.rcMonitor.top, + flags); + } + else + { + HWND after; + RECT rect = { xpos, ypos, xpos + width, ypos + height }; + DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); + UINT flags = SWP_NOACTIVATE | SWP_NOCOPYBITS; + + if (window->decorated) + { + style &= ~WS_POPUP; + style |= getWindowStyle(window); + SetWindowLongW(window->win32.handle, GWL_STYLE, style); + + flags |= SWP_FRAMECHANGED; + } + + if (window->floating) + after = HWND_TOPMOST; + else + after = HWND_NOTOPMOST; + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + { + AdjustWindowRectExForDpi(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window), + GetDpiForWindow(window->win32.handle)); + } + else + { + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); + } + + SetWindowPos(window->win32.handle, after, + rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, + flags); + } +} + +int _glfwPlatformWindowFocused(_GLFWwindow* window) +{ + return window->win32.handle == GetActiveWindow(); +} + +int _glfwPlatformWindowIconified(_GLFWwindow* window) +{ + return IsIconic(window->win32.handle); +} + +int _glfwPlatformWindowVisible(_GLFWwindow* window) +{ + return IsWindowVisible(window->win32.handle); +} + +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + return IsZoomed(window->win32.handle); +} + +int _glfwPlatformWindowHovered(_GLFWwindow* window) +{ + return cursorInContentArea(window); +} + +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + BOOL enabled; + + if (!window->win32.transparent) + return GLFW_FALSE; + + if (!IsWindowsVistaOrGreater()) + return GLFW_FALSE; + + return SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled; +} + +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +{ + updateWindowStyles(window); +} + +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +{ + updateWindowStyles(window); +} + +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +{ + const HWND after = enabled ? HWND_TOPMOST : HWND_NOTOPMOST; + SetWindowPos(window->win32.handle, after, 0, 0, 0, 0, + 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 _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) +{ + if (_glfw.win32.disabledCursorWindow != window) + return; + + if (enabled) + enableRawMouseMotion(window); + else + disableRawMouseMotion(window); +} + +GLFWbool _glfwPlatformRawMouseMotionSupported(void) +{ + return GLFW_TRUE; +} + +void _glfwPlatformPollEvents(void) +{ + MSG msg; + HWND handle; + _GLFWwindow* window; + + while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) + { + if (msg.message == WM_QUIT) + { + // NOTE: While GLFW does not itself post WM_QUIT, other processes + // may post it to this one, for example Task Manager + // HACK: Treat WM_QUIT as a close on all windows + + window = _glfw.windowListHead; + while (window) + { + _glfwInputWindowCloseRequest(window); + window = window->next; + } + } + else + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + + // HACK: Release modifier keys that the system did not emit KEYUP for + // NOTE: Shift keys on Windows tend to "stick" when both are pressed as + // no key up message is generated by the first key release + // NOTE: Windows key is not reported as released by the Win+V hotkey + // Other Win hotkeys are handled implicitly by _glfwInputWindowFocus + // because they change the input focus + // NOTE: The other half of this is in the WM_*KEY* handler in windowProc + handle = GetActiveWindow(); + if (handle) + { + window = GetPropW(handle, L"GLFW"); + if (window) + { + int i; + const int keys[4][2] = + { + { VK_LSHIFT, GLFW_KEY_LEFT_SHIFT }, + { VK_RSHIFT, GLFW_KEY_RIGHT_SHIFT }, + { VK_LWIN, GLFW_KEY_LEFT_SUPER }, + { VK_RWIN, GLFW_KEY_RIGHT_SUPER } + }; + + for (i = 0; i < 4; i++) + { + const int vk = keys[i][0]; + const int key = keys[i][1]; + const int scancode = _glfw.win32.scancodes[key]; + + if ((GetKeyState(vk) & 0x8000)) + continue; + if (window->keys[key] != GLFW_PRESS) + continue; + + _glfwInputKey(window, key, scancode, GLFW_RELEASE, getKeyMods()); + } + } + } + + window = _glfw.win32.disabledCursorWindow; + if (window) + { + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + + // NOTE: Re-center the cursor only if it has moved since the last call, + // to avoid breaking glfwWaitEvents with WM_MOUSEMOVE + if (window->win32.lastCursorPosX != width / 2 || + window->win32.lastCursorPosY != height / 2) + { + _glfwPlatformSetCursorPos(window, width / 2, height / 2); + } + } +} + +void _glfwPlatformWaitEvents(void) +{ + WaitMessage(); + + _glfwPlatformPollEvents(); +} + +void _glfwPlatformWaitEventsTimeout(double timeout) +{ + MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD) (timeout * 1e3), QS_ALLEVENTS); + + _glfwPlatformPollEvents(); +} + +void _glfwPlatformPostEmptyEvent(void) +{ + PostMessage(_glfw.win32.helperWindowHandle, WM_NULL, 0, 0); +} + +void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) +{ + POINT pos; + + if (GetCursorPos(&pos)) + { + ScreenToClient(window->win32.handle, &pos); + + if (xpos) + *xpos = pos.x; + if (ypos) + *ypos = pos.y; + } +} + +void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos) +{ + POINT pos = { (int) xpos, (int) ypos }; + + // Store the new position so it can be recognized later + window->win32.lastCursorPosX = pos.x; + window->win32.lastCursorPosY = pos.y; + + ClientToScreen(window->win32.handle, &pos); + SetCursorPos(pos.x, pos.y); +} + +void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) +{ + if (mode == GLFW_CURSOR_DISABLED) + { + if (_glfwPlatformWindowFocused(window)) + disableCursor(window); + } + else if (_glfw.win32.disabledCursorWindow == window) + enableCursor(window); + else if (cursorInContentArea(window)) + updateCursorImage(window); +} + +const char* _glfwPlatformGetScancodeName(int scancode) +{ + if (scancode < 0 || scancode > (KF_EXTENDED | 0xff) || + _glfw.win32.keycodes[scancode] == GLFW_KEY_UNKNOWN) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode"); + return NULL; + } + + return _glfw.win32.keynames[_glfw.win32.keycodes[scancode]]; +} + +int _glfwPlatformGetKeyScancode(int key) +{ + return _glfw.win32.scancodes[key]; +} + +int _glfwPlatformCreateCursor(_GLFWcursor* cursor, + const GLFWimage* image, + int xhot, int yhot) +{ + cursor->win32.handle = (HCURSOR) createIcon(image, xhot, yhot, GLFW_FALSE); + if (!cursor->win32.handle) + return GLFW_FALSE; + + return GLFW_TRUE; +} + +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +{ + int id = 0; + + if (shape == GLFW_ARROW_CURSOR) + id = OCR_NORMAL; + else if (shape == GLFW_IBEAM_CURSOR) + id = OCR_IBEAM; + else if (shape == GLFW_CROSSHAIR_CURSOR) + id = OCR_CROSS; + else if (shape == GLFW_POINTING_HAND_CURSOR) + id = OCR_HAND; + else if (shape == GLFW_RESIZE_EW_CURSOR) + id = OCR_SIZEWE; + else if (shape == GLFW_RESIZE_NS_CURSOR) + id = OCR_SIZENS; + else if (shape == GLFW_RESIZE_NWSE_CURSOR) + id = OCR_SIZENWSE; + else if (shape == GLFW_RESIZE_NESW_CURSOR) + id = OCR_SIZENESW; + else if (shape == GLFW_RESIZE_ALL_CURSOR) + id = OCR_SIZEALL; + else if (shape == GLFW_NOT_ALLOWED_CURSOR) + id = OCR_NO; + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Unknown standard cursor"); + return GLFW_FALSE; + } + + cursor->win32.handle = LoadImageW(NULL, + MAKEINTRESOURCEW(id), IMAGE_CURSOR, 0, 0, + LR_DEFAULTSIZE | LR_SHARED); + if (!cursor->win32.handle) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to create standard cursor"); + return GLFW_FALSE; + } + + return GLFW_TRUE; +} + +void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) +{ + if (cursor->win32.handle) + DestroyIcon((HICON) cursor->win32.handle); +} + +void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) +{ + if (cursorInContentArea(window)) + updateCursorImage(window); +} + +void _glfwPlatformSetClipboardString(const char* string) +{ + int characterCount; + HANDLE object; + WCHAR* buffer; + + characterCount = MultiByteToWideChar(CP_UTF8, 0, string, -1, NULL, 0); + if (!characterCount) + return; + + object = GlobalAlloc(GMEM_MOVEABLE, characterCount * sizeof(WCHAR)); + if (!object) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to allocate global handle for clipboard"); + return; + } + + buffer = GlobalLock(object); + if (!buffer) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to lock global handle"); + GlobalFree(object); + return; + } + + MultiByteToWideChar(CP_UTF8, 0, string, -1, buffer, characterCount); + GlobalUnlock(object); + + if (!OpenClipboard(_glfw.win32.helperWindowHandle)) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to open clipboard"); + GlobalFree(object); + return; + } + + EmptyClipboard(); + SetClipboardData(CF_UNICODETEXT, object); + CloseClipboard(); +} + +const char* _glfwPlatformGetClipboardString(void) +{ + HANDLE object; + WCHAR* buffer; + + if (!OpenClipboard(_glfw.win32.helperWindowHandle)) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to open clipboard"); + return NULL; + } + + object = GetClipboardData(CF_UNICODETEXT); + if (!object) + { + _glfwInputErrorWin32(GLFW_FORMAT_UNAVAILABLE, + "Win32: Failed to convert clipboard to string"); + CloseClipboard(); + return NULL; + } + + buffer = GlobalLock(object); + if (!buffer) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to lock global handle"); + CloseClipboard(); + return NULL; + } + + free(_glfw.win32.clipboardString); + _glfw.win32.clipboardString = _glfwCreateUTF8FromWideStringWin32(buffer); + + GlobalUnlock(object); + CloseClipboard(); + + return _glfw.win32.clipboardString; +} + +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) +{ + if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_win32_surface) + return; + + extensions[0] = "VK_KHR_surface"; + extensions[1] = "VK_KHR_win32_surface"; +} + +int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily) +{ + PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR + vkGetPhysicalDeviceWin32PresentationSupportKHR = + (PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR) + vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR"); + if (!vkGetPhysicalDeviceWin32PresentationSupportKHR) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Win32: Vulkan instance missing VK_KHR_win32_surface extension"); + return GLFW_FALSE; + } + + return vkGetPhysicalDeviceWin32PresentationSupportKHR(device, queuefamily); +} + +VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, + _GLFWwindow* window, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) +{ + VkResult err; + VkWin32SurfaceCreateInfoKHR sci; + PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR; + + vkCreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR) + vkGetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR"); + if (!vkCreateWin32SurfaceKHR) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Win32: Vulkan instance missing VK_KHR_win32_surface extension"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + memset(&sci, 0, sizeof(sci)); + sci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + sci.hinstance = GetModuleHandle(NULL); + sci.hwnd = window->win32.handle; + + err = vkCreateWin32SurfaceKHR(instance, &sci, allocator, surface); + if (err) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to create Vulkan surface: %s", + _glfwGetVulkanResultString(err)); + } + + return err; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return window->win32.handle; +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/window.c b/source/MaterialXGraphEditor/External/Glfw/src/window.c new file mode 100644 index 0000000000..bb5ba95677 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/window.c @@ -0,0 +1,1104 @@ +//======================================================================== +// GLFW 3.4 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2019 Camilla Löwy +// Copyright (c) 2012 Torsten Walluhn +// +// 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. +// +//======================================================================== +// Please use C89 style variable declarations in this file because VS 2010 +//======================================================================== + +#include "internal.h" + +#include +#include +#include +#include + + +////////////////////////////////////////////////////////////////////////// +////// GLFW event API ////// +////////////////////////////////////////////////////////////////////////// + +// Notifies shared code that a window has lost or received input focus +// +void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused) +{ + if (window->callbacks.focus) + window->callbacks.focus((GLFWwindow*) window, focused); + + if (!focused) + { + int key, button; + + for (key = 0; key <= GLFW_KEY_LAST; key++) + { + if (window->keys[key] == GLFW_PRESS) + { + const int scancode = _glfwPlatformGetKeyScancode(key); + _glfwInputKey(window, key, scancode, GLFW_RELEASE, 0); + } + } + + for (button = 0; button <= GLFW_MOUSE_BUTTON_LAST; button++) + { + if (window->mouseButtons[button] == GLFW_PRESS) + _glfwInputMouseClick(window, button, GLFW_RELEASE, 0); + } + } +} + +// Notifies shared code that a window has moved +// The position is specified in content area relative screen coordinates +// +void _glfwInputWindowPos(_GLFWwindow* window, int x, int y) +{ + if (window->callbacks.pos) + window->callbacks.pos((GLFWwindow*) window, x, y); +} + +// Notifies shared code that a window has been resized +// The size is specified in screen coordinates +// +void _glfwInputWindowSize(_GLFWwindow* window, int width, int height) +{ + if (window->callbacks.size) + window->callbacks.size((GLFWwindow*) window, width, height); +} + +// Notifies shared code that a window has been iconified or restored +// +void _glfwInputWindowIconify(_GLFWwindow* window, GLFWbool iconified) +{ + if (window->callbacks.iconify) + window->callbacks.iconify((GLFWwindow*) window, iconified); +} + +// Notifies shared code that a window has been maximized or restored +// +void _glfwInputWindowMaximize(_GLFWwindow* window, GLFWbool maximized) +{ + if (window->callbacks.maximize) + window->callbacks.maximize((GLFWwindow*) window, maximized); +} + +// Notifies shared code that a window framebuffer has been resized +// The size is specified in pixels +// +void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height) +{ + if (window->callbacks.fbsize) + window->callbacks.fbsize((GLFWwindow*) window, width, height); +} + +// Notifies shared code that a window content scale has changed +// The scale is specified as the ratio between the current and default DPI +// +void _glfwInputWindowContentScale(_GLFWwindow* window, float xscale, float yscale) +{ + if (window->callbacks.scale) + window->callbacks.scale((GLFWwindow*) window, xscale, yscale); +} + +// Notifies shared code that the window contents needs updating +// +void _glfwInputWindowDamage(_GLFWwindow* window) +{ + if (window->callbacks.refresh) + window->callbacks.refresh((GLFWwindow*) window); +} + +// Notifies shared code that the user wishes to close a window +// +void _glfwInputWindowCloseRequest(_GLFWwindow* window) +{ + window->shouldClose = GLFW_TRUE; + + if (window->callbacks.close) + window->callbacks.close((GLFWwindow*) window); +} + +// Notifies shared code that a window has changed its desired monitor +// +void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor) +{ + window->monitor = monitor; +} + +////////////////////////////////////////////////////////////////////////// +////// GLFW public API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, + const char* title, + GLFWmonitor* monitor, + GLFWwindow* share) +{ + _GLFWfbconfig fbconfig; + _GLFWctxconfig ctxconfig; + _GLFWwndconfig wndconfig; + _GLFWwindow* window; + + assert(title != NULL); + assert(width >= 0); + assert(height >= 0); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (width <= 0 || height <= 0) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid window size %ix%i", + width, height); + + return NULL; + } + + fbconfig = _glfw.hints.framebuffer; + ctxconfig = _glfw.hints.context; + wndconfig = _glfw.hints.window; + + wndconfig.width = width; + wndconfig.height = height; + wndconfig.title = title; + ctxconfig.share = (_GLFWwindow*) share; + + if (!_glfwIsValidContextConfig(&ctxconfig)) + return NULL; + + window = calloc(1, sizeof(_GLFWwindow)); + window->next = _glfw.windowListHead; + _glfw.windowListHead = window; + + window->videoMode.width = width; + window->videoMode.height = height; + window->videoMode.redBits = fbconfig.redBits; + window->videoMode.greenBits = fbconfig.greenBits; + window->videoMode.blueBits = fbconfig.blueBits; + window->videoMode.refreshRate = _glfw.hints.refreshRate; + + window->monitor = (_GLFWmonitor*) monitor; + window->resizable = wndconfig.resizable; + window->decorated = wndconfig.decorated; + window->autoIconify = wndconfig.autoIconify; + window->floating = wndconfig.floating; + window->focusOnShow = wndconfig.focusOnShow; + window->cursorMode = GLFW_CURSOR_NORMAL; + + window->minwidth = GLFW_DONT_CARE; + window->minheight = GLFW_DONT_CARE; + window->maxwidth = GLFW_DONT_CARE; + window->maxheight = GLFW_DONT_CARE; + window->numer = GLFW_DONT_CARE; + window->denom = GLFW_DONT_CARE; + + // Open the actual window and create its context + if (!_glfwPlatformCreateWindow(window, &wndconfig, &ctxconfig, &fbconfig)) + { + glfwDestroyWindow((GLFWwindow*) window); + return NULL; + } + + if (ctxconfig.client != GLFW_NO_API) + { + if (!_glfwRefreshContextAttribs(window, &ctxconfig)) + { + glfwDestroyWindow((GLFWwindow*) window); + return NULL; + } + } + + if (window->monitor) + { + if (wndconfig.centerCursor) + _glfwCenterCursorInContentArea(window); + } + else + { + if (wndconfig.visible) + { + _glfwPlatformShowWindow(window); + if (wndconfig.focused) + _glfwPlatformFocusWindow(window); + } + } + + return (GLFWwindow*) window; +} + +void glfwDefaultWindowHints(void) +{ + _GLFW_REQUIRE_INIT(); + + // The default is OpenGL with minimum version 1.0 + memset(&_glfw.hints.context, 0, sizeof(_glfw.hints.context)); + _glfw.hints.context.client = GLFW_OPENGL_API; + _glfw.hints.context.source = GLFW_NATIVE_CONTEXT_API; + _glfw.hints.context.major = 1; + _glfw.hints.context.minor = 0; + + // The default is a focused, visible, resizable window with decorations + memset(&_glfw.hints.window, 0, sizeof(_glfw.hints.window)); + _glfw.hints.window.resizable = GLFW_TRUE; + _glfw.hints.window.visible = GLFW_TRUE; + _glfw.hints.window.decorated = GLFW_TRUE; + _glfw.hints.window.focused = GLFW_TRUE; + _glfw.hints.window.autoIconify = GLFW_TRUE; + _glfw.hints.window.centerCursor = GLFW_TRUE; + _glfw.hints.window.focusOnShow = GLFW_TRUE; + + // The default is 24 bits of color, 24 bits of depth and 8 bits of stencil, + // double buffered + memset(&_glfw.hints.framebuffer, 0, sizeof(_glfw.hints.framebuffer)); + _glfw.hints.framebuffer.redBits = 8; + _glfw.hints.framebuffer.greenBits = 8; + _glfw.hints.framebuffer.blueBits = 8; + _glfw.hints.framebuffer.alphaBits = 8; + _glfw.hints.framebuffer.depthBits = 24; + _glfw.hints.framebuffer.stencilBits = 8; + _glfw.hints.framebuffer.doublebuffer = GLFW_TRUE; + + // The default is to select the highest available refresh rate + _glfw.hints.refreshRate = GLFW_DONT_CARE; + + // The default is to use full Retina resolution framebuffers + _glfw.hints.window.ns.retina = GLFW_TRUE; +} + +GLFWAPI void glfwWindowHint(int hint, int value) +{ + _GLFW_REQUIRE_INIT(); + + switch (hint) + { + case GLFW_RED_BITS: + _glfw.hints.framebuffer.redBits = value; + return; + case GLFW_GREEN_BITS: + _glfw.hints.framebuffer.greenBits = value; + return; + case GLFW_BLUE_BITS: + _glfw.hints.framebuffer.blueBits = value; + return; + case GLFW_ALPHA_BITS: + _glfw.hints.framebuffer.alphaBits = value; + return; + case GLFW_DEPTH_BITS: + _glfw.hints.framebuffer.depthBits = value; + return; + case GLFW_STENCIL_BITS: + _glfw.hints.framebuffer.stencilBits = value; + return; + case GLFW_ACCUM_RED_BITS: + _glfw.hints.framebuffer.accumRedBits = value; + return; + case GLFW_ACCUM_GREEN_BITS: + _glfw.hints.framebuffer.accumGreenBits = value; + return; + case GLFW_ACCUM_BLUE_BITS: + _glfw.hints.framebuffer.accumBlueBits = value; + return; + case GLFW_ACCUM_ALPHA_BITS: + _glfw.hints.framebuffer.accumAlphaBits = value; + return; + case GLFW_AUX_BUFFERS: + _glfw.hints.framebuffer.auxBuffers = value; + return; + case GLFW_STEREO: + _glfw.hints.framebuffer.stereo = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_DOUBLEBUFFER: + _glfw.hints.framebuffer.doublebuffer = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_TRANSPARENT_FRAMEBUFFER: + _glfw.hints.framebuffer.transparent = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_SAMPLES: + _glfw.hints.framebuffer.samples = value; + return; + case GLFW_SRGB_CAPABLE: + _glfw.hints.framebuffer.sRGB = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_RESIZABLE: + _glfw.hints.window.resizable = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_DECORATED: + _glfw.hints.window.decorated = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_FOCUSED: + _glfw.hints.window.focused = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_AUTO_ICONIFY: + _glfw.hints.window.autoIconify = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_FLOATING: + _glfw.hints.window.floating = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_MAXIMIZED: + _glfw.hints.window.maximized = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_VISIBLE: + _glfw.hints.window.visible = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_COCOA_RETINA_FRAMEBUFFER: + _glfw.hints.window.ns.retina = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_WIN32_KEYBOARD_MENU: + _glfw.hints.window.win32.keymenu = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_COCOA_GRAPHICS_SWITCHING: + _glfw.hints.context.nsgl.offline = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_SCALE_TO_MONITOR: + _glfw.hints.window.scaleToMonitor = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_CENTER_CURSOR: + _glfw.hints.window.centerCursor = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_FOCUS_ON_SHOW: + _glfw.hints.window.focusOnShow = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_CLIENT_API: + _glfw.hints.context.client = value; + return; + case GLFW_CONTEXT_CREATION_API: + _glfw.hints.context.source = value; + return; + case GLFW_CONTEXT_VERSION_MAJOR: + _glfw.hints.context.major = value; + return; + case GLFW_CONTEXT_VERSION_MINOR: + _glfw.hints.context.minor = value; + return; + case GLFW_CONTEXT_ROBUSTNESS: + _glfw.hints.context.robustness = value; + return; + case GLFW_OPENGL_FORWARD_COMPAT: + _glfw.hints.context.forward = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_OPENGL_DEBUG_CONTEXT: + _glfw.hints.context.debug = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_CONTEXT_NO_ERROR: + _glfw.hints.context.noerror = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_OPENGL_PROFILE: + _glfw.hints.context.profile = value; + return; + case GLFW_CONTEXT_RELEASE_BEHAVIOR: + _glfw.hints.context.release = value; + return; + case GLFW_REFRESH_RATE: + _glfw.hints.refreshRate = value; + return; + } + + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint 0x%08X", hint); +} + +GLFWAPI void glfwWindowHintString(int hint, const char* value) +{ + assert(value != NULL); + + _GLFW_REQUIRE_INIT(); + + switch (hint) + { + case GLFW_COCOA_FRAME_NAME: + strncpy(_glfw.hints.window.ns.frameName, value, + sizeof(_glfw.hints.window.ns.frameName) - 1); + return; + case GLFW_X11_CLASS_NAME: + strncpy(_glfw.hints.window.x11.className, value, + sizeof(_glfw.hints.window.x11.className) - 1); + return; + case GLFW_X11_INSTANCE_NAME: + strncpy(_glfw.hints.window.x11.instanceName, value, + sizeof(_glfw.hints.window.x11.instanceName) - 1); + return; + } + + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint string 0x%08X", hint); +} + +GLFWAPI void glfwDestroyWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + + _GLFW_REQUIRE_INIT(); + + // Allow closing of NULL (to match the behavior of free) + if (window == NULL) + return; + + // Clear all callbacks to avoid exposing a half torn-down window object + memset(&window->callbacks, 0, sizeof(window->callbacks)); + + // The window's context must not be current on another thread when the + // window is destroyed + if (window == _glfwPlatformGetTls(&_glfw.contextSlot)) + glfwMakeContextCurrent(NULL); + + _glfwPlatformDestroyWindow(window); + + // Unlink window from global linked list + { + _GLFWwindow** prev = &_glfw.windowListHead; + + while (*prev != window) + prev = &((*prev)->next); + + *prev = window->next; + } + + free(window); +} + +GLFWAPI int glfwWindowShouldClose(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(0); + return window->shouldClose; +} + +GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* handle, int value) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + window->shouldClose = value; +} + +GLFWAPI void glfwSetWindowTitle(GLFWwindow* handle, const char* title) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + assert(title != NULL); + + _GLFW_REQUIRE_INIT(); + _glfwPlatformSetWindowTitle(window, title); +} + +GLFWAPI void glfwSetWindowIcon(GLFWwindow* handle, + int count, const GLFWimage* images) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + assert(count >= 0); + assert(count == 0 || images != NULL); + + _GLFW_REQUIRE_INIT(); + _glfwPlatformSetWindowIcon(window, count, images); +} + +GLFWAPI void glfwGetWindowPos(GLFWwindow* handle, int* xpos, int* ypos) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + if (xpos) + *xpos = 0; + if (ypos) + *ypos = 0; + + _GLFW_REQUIRE_INIT(); + _glfwPlatformGetWindowPos(window, xpos, ypos); +} + +GLFWAPI void glfwSetWindowPos(GLFWwindow* handle, int xpos, int ypos) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (window->monitor) + return; + + _glfwPlatformSetWindowPos(window, xpos, ypos); +} + +GLFWAPI void glfwGetWindowSize(GLFWwindow* handle, int* width, int* height) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + if (width) + *width = 0; + if (height) + *height = 0; + + _GLFW_REQUIRE_INIT(); + _glfwPlatformGetWindowSize(window, width, height); +} + +GLFWAPI void glfwSetWindowSize(GLFWwindow* handle, int width, int height) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + assert(width >= 0); + assert(height >= 0); + + _GLFW_REQUIRE_INIT(); + + window->videoMode.width = width; + window->videoMode.height = height; + + _glfwPlatformSetWindowSize(window, width, height); +} + +GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* handle, + int minwidth, int minheight, + int maxwidth, int maxheight) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (minwidth != GLFW_DONT_CARE && minheight != GLFW_DONT_CARE) + { + if (minwidth < 0 || minheight < 0) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid window minimum size %ix%i", + minwidth, minheight); + return; + } + } + + if (maxwidth != GLFW_DONT_CARE && maxheight != GLFW_DONT_CARE) + { + if (maxwidth < 0 || maxheight < 0 || + maxwidth < minwidth || maxheight < minheight) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid window maximum size %ix%i", + maxwidth, maxheight); + return; + } + } + + window->minwidth = minwidth; + window->minheight = minheight; + window->maxwidth = maxwidth; + window->maxheight = maxheight; + + if (window->monitor || !window->resizable) + return; + + _glfwPlatformSetWindowSizeLimits(window, + minwidth, minheight, + maxwidth, maxheight); +} + +GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* handle, int numer, int denom) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + assert(numer != 0); + assert(denom != 0); + + _GLFW_REQUIRE_INIT(); + + if (numer != GLFW_DONT_CARE && denom != GLFW_DONT_CARE) + { + if (numer <= 0 || denom <= 0) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid window aspect ratio %i:%i", + numer, denom); + return; + } + } + + window->numer = numer; + window->denom = denom; + + if (window->monitor || !window->resizable) + return; + + _glfwPlatformSetWindowAspectRatio(window, numer, denom); +} + +GLFWAPI void glfwGetFramebufferSize(GLFWwindow* handle, int* width, int* height) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + if (width) + *width = 0; + if (height) + *height = 0; + + _GLFW_REQUIRE_INIT(); + _glfwPlatformGetFramebufferSize(window, width, height); +} + +GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* handle, + int* left, int* top, + int* right, int* bottom) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + if (left) + *left = 0; + if (top) + *top = 0; + if (right) + *right = 0; + if (bottom) + *bottom = 0; + + _GLFW_REQUIRE_INIT(); + _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) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + _glfwPlatformIconifyWindow(window); +} + +GLFWAPI void glfwRestoreWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + _glfwPlatformRestoreWindow(window); +} + +GLFWAPI void glfwMaximizeWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (window->monitor) + return; + + _glfwPlatformMaximizeWindow(window); +} + +GLFWAPI void glfwShowWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (window->monitor) + return; + + _glfwPlatformShowWindow(window); + + if (window->focusOnShow) + _glfwPlatformFocusWindow(window); +} + +GLFWAPI void glfwRequestWindowAttention(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + _glfwPlatformRequestWindowAttention(window); +} + +GLFWAPI void glfwHideWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (window->monitor) + return; + + _glfwPlatformHideWindow(window); +} + +GLFWAPI void glfwFocusWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + _glfwPlatformFocusWindow(window); +} + +GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(0); + + switch (attrib) + { + case GLFW_FOCUSED: + return _glfwPlatformWindowFocused(window); + case GLFW_ICONIFIED: + return _glfwPlatformWindowIconified(window); + case GLFW_VISIBLE: + return _glfwPlatformWindowVisible(window); + case GLFW_MAXIMIZED: + return _glfwPlatformWindowMaximized(window); + case GLFW_HOVERED: + return _glfwPlatformWindowHovered(window); + case GLFW_FOCUS_ON_SHOW: + return window->focusOnShow; + case GLFW_TRANSPARENT_FRAMEBUFFER: + return _glfwPlatformFramebufferTransparent(window); + case GLFW_RESIZABLE: + return window->resizable; + case GLFW_DECORATED: + return window->decorated; + case GLFW_FLOATING: + return window->floating; + case GLFW_AUTO_ICONIFY: + return window->autoIconify; + case GLFW_CLIENT_API: + return window->context.client; + case GLFW_CONTEXT_CREATION_API: + return window->context.source; + case GLFW_CONTEXT_VERSION_MAJOR: + return window->context.major; + case GLFW_CONTEXT_VERSION_MINOR: + return window->context.minor; + case GLFW_CONTEXT_REVISION: + return window->context.revision; + case GLFW_CONTEXT_ROBUSTNESS: + return window->context.robustness; + case GLFW_OPENGL_FORWARD_COMPAT: + return window->context.forward; + case GLFW_OPENGL_DEBUG_CONTEXT: + return window->context.debug; + case GLFW_OPENGL_PROFILE: + return window->context.profile; + case GLFW_CONTEXT_RELEASE_BEHAVIOR: + return window->context.release; + case GLFW_CONTEXT_NO_ERROR: + return window->context.noerror; + } + + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib); + return 0; +} + +GLFWAPI void glfwSetWindowAttrib(GLFWwindow* handle, int attrib, int value) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + value = value ? GLFW_TRUE : GLFW_FALSE; + + if (attrib == GLFW_AUTO_ICONIFY) + window->autoIconify = value; + else if (attrib == GLFW_RESIZABLE) + { + if (window->resizable == value) + return; + + window->resizable = value; + if (!window->monitor) + _glfwPlatformSetWindowResizable(window, value); + } + else if (attrib == GLFW_DECORATED) + { + if (window->decorated == value) + return; + + window->decorated = value; + if (!window->monitor) + _glfwPlatformSetWindowDecorated(window, value); + } + else if (attrib == GLFW_FLOATING) + { + if (window->floating == value) + return; + + window->floating = value; + if (!window->monitor) + _glfwPlatformSetWindowFloating(window, value); + } + else if (attrib == GLFW_FOCUS_ON_SHOW) + window->focusOnShow = value; + else + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib); +} + +GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return (GLFWmonitor*) window->monitor; +} + +GLFWAPI void glfwSetWindowMonitor(GLFWwindow* wh, + GLFWmonitor* mh, + int xpos, int ypos, + int width, int height, + int refreshRate) +{ + _GLFWwindow* window = (_GLFWwindow*) wh; + _GLFWmonitor* monitor = (_GLFWmonitor*) mh; + assert(window != NULL); + assert(width >= 0); + assert(height >= 0); + + _GLFW_REQUIRE_INIT(); + + if (width <= 0 || height <= 0) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid window size %ix%i", + width, height); + return; + } + + if (refreshRate < 0 && refreshRate != GLFW_DONT_CARE) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid refresh rate %i", + refreshRate); + return; + } + + window->videoMode.width = width; + window->videoMode.height = height; + window->videoMode.refreshRate = refreshRate; + + _glfwPlatformSetWindowMonitor(window, monitor, + xpos, ypos, width, height, + refreshRate); +} + +GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* handle, void* pointer) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + window->userPointer = pointer; +} + +GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return window->userPointer; +} + +GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* handle, + GLFWwindowposfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.pos, cbfun); + return cbfun; +} + +GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* handle, + GLFWwindowsizefun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.size, cbfun); + return cbfun; +} + +GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* handle, + GLFWwindowclosefun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.close, cbfun); + return cbfun; +} + +GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* handle, + GLFWwindowrefreshfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.refresh, cbfun); + return cbfun; +} + +GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* handle, + GLFWwindowfocusfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.focus, cbfun); + return cbfun; +} + +GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* handle, + GLFWwindowiconifyfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.iconify, cbfun); + return cbfun; +} + +GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* handle, + GLFWwindowmaximizefun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.maximize, cbfun); + return cbfun; +} + +GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* handle, + GLFWframebuffersizefun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.fbsize, cbfun); + return cbfun; +} + +GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* handle, + GLFWwindowcontentscalefun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.scale, cbfun); + return cbfun; +} + +GLFWAPI void glfwPollEvents(void) +{ + _GLFW_REQUIRE_INIT(); + _glfwPlatformPollEvents(); +} + +GLFWAPI void glfwWaitEvents(void) +{ + _GLFW_REQUIRE_INIT(); + _glfwPlatformWaitEvents(); +} + +GLFWAPI void glfwWaitEventsTimeout(double timeout) +{ + _GLFW_REQUIRE_INIT(); + assert(timeout == timeout); + assert(timeout >= 0.0); + assert(timeout <= DBL_MAX); + + if (timeout != timeout || timeout < 0.0 || timeout > DBL_MAX) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid time %f", timeout); + return; + } + + _glfwPlatformWaitEventsTimeout(timeout); +} + +GLFWAPI void glfwPostEmptyEvent(void) +{ + _GLFW_REQUIRE_INIT(); + _glfwPlatformPostEmptyEvent(); +} diff --git a/source/MaterialXGraphEditor/External/Glfw/src/wl_init.c b/source/MaterialXGraphEditor/External/Glfw/src/wl_init.c new file mode 100644 index 0000000000..558ff8a80e --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/wl_init.c @@ -0,0 +1,1318 @@ +//======================================================================== +// GLFW 3.4 Wayland - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2014 Jonas Ã…dahl +// +// 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. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#define _POSIX_C_SOURCE 199309L + +#include "internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static inline int min(int n1, int n2) +{ + return n1 < n2 ? n1 : n2; +} + +static _GLFWwindow* findWindowFromDecorationSurface(struct wl_surface* surface, + int* which) +{ + int focus; + _GLFWwindow* window = _glfw.windowListHead; + if (!which) + which = &focus; + while (window) + { + if (surface == window->wl.decorations.top.surface) + { + *which = topDecoration; + break; + } + if (surface == window->wl.decorations.left.surface) + { + *which = leftDecoration; + break; + } + if (surface == window->wl.decorations.right.surface) + { + *which = rightDecoration; + break; + } + if (surface == window->wl.decorations.bottom.surface) + { + *which = bottomDecoration; + break; + } + window = window->next; + } + return window; +} + +static void pointerHandleEnter(void* data, + struct wl_pointer* pointer, + uint32_t serial, + struct wl_surface* surface, + wl_fixed_t sx, + wl_fixed_t sy) +{ + // Happens in the case we just destroyed the surface. + if (!surface) + return; + + int focus = 0; + _GLFWwindow* window = wl_surface_get_user_data(surface); + if (!window) + { + window = findWindowFromDecorationSurface(surface, &focus); + if (!window) + return; + } + + window->wl.decorations.focus = focus; + _glfw.wl.serial = serial; + _glfw.wl.pointerFocus = window; + + window->wl.hovered = GLFW_TRUE; + + _glfwPlatformSetCursor(window, window->wl.currentCursor); + _glfwInputCursorEnter(window, GLFW_TRUE); +} + +static void pointerHandleLeave(void* data, + struct wl_pointer* pointer, + uint32_t serial, + struct wl_surface* surface) +{ + _GLFWwindow* window = _glfw.wl.pointerFocus; + + if (!window) + return; + + window->wl.hovered = GLFW_FALSE; + + _glfw.wl.serial = serial; + _glfw.wl.pointerFocus = NULL; + _glfwInputCursorEnter(window, GLFW_FALSE); + _glfw.wl.cursorPreviousName = NULL; +} + +static void setCursor(_GLFWwindow* window, const char* name) +{ + struct wl_buffer* buffer; + struct wl_cursor* cursor; + struct wl_cursor_image* image; + struct wl_surface* surface = _glfw.wl.cursorSurface; + struct wl_cursor_theme* theme = _glfw.wl.cursorTheme; + int scale = 1; + + if (window->wl.scale > 1 && _glfw.wl.cursorThemeHiDPI) + { + // We only support up to scale=2 for now, since libwayland-cursor + // requires us to load a different theme for each size. + scale = 2; + theme = _glfw.wl.cursorThemeHiDPI; + } + + cursor = wl_cursor_theme_get_cursor(theme, name); + if (!cursor) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Standard cursor not found"); + return; + } + // TODO: handle animated cursors too. + image = cursor->images[0]; + + if (!image) + return; + + buffer = wl_cursor_image_get_buffer(image); + if (!buffer) + return; + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, + surface, + image->hotspot_x / scale, + image->hotspot_y / scale); + wl_surface_set_buffer_scale(surface, scale); + wl_surface_attach(surface, buffer, 0, 0); + wl_surface_damage(surface, 0, 0, + image->width, image->height); + wl_surface_commit(surface); + _glfw.wl.cursorPreviousName = name; +} + +static void pointerHandleMotion(void* data, + struct wl_pointer* pointer, + uint32_t time, + wl_fixed_t sx, + wl_fixed_t sy) +{ + _GLFWwindow* window = _glfw.wl.pointerFocus; + const char* cursorName = NULL; + double x, y; + + if (!window) + return; + + if (window->cursorMode == GLFW_CURSOR_DISABLED) + return; + x = wl_fixed_to_double(sx); + y = wl_fixed_to_double(sy); + + switch (window->wl.decorations.focus) + { + case mainWindow: + window->wl.cursorPosX = x; + window->wl.cursorPosY = y; + _glfwInputCursorPos(window, x, y); + _glfw.wl.cursorPreviousName = NULL; + return; + case topDecoration: + if (y < _GLFW_DECORATION_WIDTH) + cursorName = "n-resize"; + else + cursorName = "left_ptr"; + break; + case leftDecoration: + if (y < _GLFW_DECORATION_WIDTH) + cursorName = "nw-resize"; + else + cursorName = "w-resize"; + break; + case rightDecoration: + if (y < _GLFW_DECORATION_WIDTH) + cursorName = "ne-resize"; + else + cursorName = "e-resize"; + break; + case bottomDecoration: + if (x < _GLFW_DECORATION_WIDTH) + cursorName = "sw-resize"; + else if (x > window->wl.width + _GLFW_DECORATION_WIDTH) + cursorName = "se-resize"; + else + cursorName = "s-resize"; + break; + default: + assert(0); + } + if (_glfw.wl.cursorPreviousName != cursorName) + setCursor(window, cursorName); +} + +static void pointerHandleButton(void* data, + struct wl_pointer* pointer, + uint32_t serial, + uint32_t time, + uint32_t button, + uint32_t state) +{ + _GLFWwindow* window = _glfw.wl.pointerFocus; + int glfwButton; + uint32_t edges = XDG_TOPLEVEL_RESIZE_EDGE_NONE; + + if (!window) + return; + if (button == BTN_LEFT) + { + switch (window->wl.decorations.focus) + { + case mainWindow: + break; + case topDecoration: + if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) + edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP; + else + { + xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, serial); + } + break; + case leftDecoration: + if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) + edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; + else + edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT; + break; + case rightDecoration: + if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) + edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; + else + edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; + break; + case bottomDecoration: + if (window->wl.cursorPosX < _GLFW_DECORATION_WIDTH) + edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; + else if (window->wl.cursorPosX > window->wl.width + _GLFW_DECORATION_WIDTH) + edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; + else + edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; + break; + default: + assert(0); + } + if (edges != XDG_TOPLEVEL_RESIZE_EDGE_NONE) + { + xdg_toplevel_resize(window->wl.xdg.toplevel, _glfw.wl.seat, + serial, edges); + } + } + else if (button == BTN_RIGHT) + { + if (window->wl.decorations.focus != mainWindow && window->wl.xdg.toplevel) + { + xdg_toplevel_show_window_menu(window->wl.xdg.toplevel, + _glfw.wl.seat, serial, + window->wl.cursorPosX, + window->wl.cursorPosY); + return; + } + } + + // Don’t pass the button to the user if it was related to a decoration. + if (window->wl.decorations.focus != mainWindow) + return; + + _glfw.wl.serial = serial; + + /* Makes left, right and middle 0, 1 and 2. Overall order follows evdev + * codes. */ + glfwButton = button - BTN_LEFT; + + _glfwInputMouseClick(window, + glfwButton, + state == WL_POINTER_BUTTON_STATE_PRESSED + ? GLFW_PRESS + : GLFW_RELEASE, + _glfw.wl.xkb.modifiers); +} + +static void pointerHandleAxis(void* data, + struct wl_pointer* pointer, + uint32_t time, + uint32_t axis, + wl_fixed_t value) +{ + _GLFWwindow* window = _glfw.wl.pointerFocus; + double x = 0.0, y = 0.0; + // Wayland scroll events are in pointer motion coordinate space (think two + // finger scroll). The factor 10 is commonly used to convert to "scroll + // step means 1.0. + const double scrollFactor = 1.0 / 10.0; + + if (!window) + return; + + assert(axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL || + axis == WL_POINTER_AXIS_VERTICAL_SCROLL); + + if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) + x = wl_fixed_to_double(value) * scrollFactor; + else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) + y = wl_fixed_to_double(value) * scrollFactor; + + _glfwInputScroll(window, x, y); +} + +static const struct wl_pointer_listener pointerListener = { + pointerHandleEnter, + pointerHandleLeave, + pointerHandleMotion, + pointerHandleButton, + pointerHandleAxis, +}; + +static void keyboardHandleKeymap(void* data, + struct wl_keyboard* keyboard, + uint32_t format, + int fd, + uint32_t size) +{ + struct xkb_keymap* keymap; + struct xkb_state* state; + +#ifdef HAVE_XKBCOMMON_COMPOSE_H + struct xkb_compose_table* composeTable; + struct xkb_compose_state* composeState; +#endif + + char* mapStr; + const char* locale; + + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) + { + close(fd); + return; + } + + mapStr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (mapStr == MAP_FAILED) { + close(fd); + return; + } + + keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context, + mapStr, + XKB_KEYMAP_FORMAT_TEXT_V1, + 0); + munmap(mapStr, size); + close(fd); + + if (!keymap) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to compile keymap"); + return; + } + + state = xkb_state_new(keymap); + if (!state) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create XKB state"); + xkb_keymap_unref(keymap); + return; + } + + // Look up the preferred locale, falling back to "C" as default. + locale = getenv("LC_ALL"); + if (!locale) + locale = getenv("LC_CTYPE"); + if (!locale) + locale = getenv("LANG"); + if (!locale) + locale = "C"; + +#ifdef HAVE_XKBCOMMON_COMPOSE_H + composeTable = + xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale, + XKB_COMPOSE_COMPILE_NO_FLAGS); + if (composeTable) + { + composeState = + xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS); + xkb_compose_table_unref(composeTable); + if (composeState) + _glfw.wl.xkb.composeState = composeState; + else + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create XKB compose state"); + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create XKB compose table"); + } +#endif + + xkb_keymap_unref(_glfw.wl.xkb.keymap); + xkb_state_unref(_glfw.wl.xkb.state); + _glfw.wl.xkb.keymap = keymap; + _glfw.wl.xkb.state = state; + + _glfw.wl.xkb.controlMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control"); + _glfw.wl.xkb.altMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1"); + _glfw.wl.xkb.shiftMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift"); + _glfw.wl.xkb.superMask = + 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, + struct wl_keyboard* keyboard, + uint32_t serial, + struct wl_surface* surface, + struct wl_array* keys) +{ + // Happens in the case we just destroyed the surface. + if (!surface) + return; + + _GLFWwindow* window = wl_surface_get_user_data(surface); + if (!window) + { + window = findWindowFromDecorationSurface(surface, NULL); + if (!window) + return; + } + + _glfw.wl.serial = serial; + _glfw.wl.keyboardFocus = window; + _glfwInputWindowFocus(window, GLFW_TRUE); +} + +static void keyboardHandleLeave(void* data, + struct wl_keyboard* keyboard, + uint32_t serial, + struct wl_surface* surface) +{ + _GLFWwindow* window = _glfw.wl.keyboardFocus; + + if (!window) + return; + + _glfw.wl.serial = serial; + _glfw.wl.keyboardFocus = NULL; + _glfwInputWindowFocus(window, GLFW_FALSE); +} + +static int toGLFWKeyCode(uint32_t key) +{ + if (key < sizeof(_glfw.wl.keycodes) / sizeof(_glfw.wl.keycodes[0])) + return _glfw.wl.keycodes[key]; + + return GLFW_KEY_UNKNOWN; +} + +#ifdef HAVE_XKBCOMMON_COMPOSE_H +static xkb_keysym_t composeSymbol(xkb_keysym_t sym) +{ + if (sym == XKB_KEY_NoSymbol || !_glfw.wl.xkb.composeState) + return sym; + if (xkb_compose_state_feed(_glfw.wl.xkb.composeState, sym) + != XKB_COMPOSE_FEED_ACCEPTED) + return sym; + switch (xkb_compose_state_get_status(_glfw.wl.xkb.composeState)) + { + case XKB_COMPOSE_COMPOSED: + return xkb_compose_state_get_one_sym(_glfw.wl.xkb.composeState); + case XKB_COMPOSE_COMPOSING: + case XKB_COMPOSE_CANCELLED: + return XKB_KEY_NoSymbol; + case XKB_COMPOSE_NOTHING: + default: + return sym; + } +} +#endif + +static GLFWbool inputChar(_GLFWwindow* window, uint32_t key) +{ + uint32_t code, numSyms; + long cp; + const xkb_keysym_t *syms; + xkb_keysym_t sym; + + code = key + 8; + numSyms = xkb_state_key_get_syms(_glfw.wl.xkb.state, code, &syms); + + if (numSyms == 1) + { +#ifdef HAVE_XKBCOMMON_COMPOSE_H + sym = composeSymbol(syms[0]); +#else + sym = syms[0]; +#endif + cp = _glfwKeySym2Unicode(sym); + if (cp != -1) + { + const int mods = _glfw.wl.xkb.modifiers; + const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); + _glfwInputChar(window, cp, mods, plain); + } + } + + return xkb_keymap_key_repeats(_glfw.wl.xkb.keymap, syms[0]); +} + +static void keyboardHandleKey(void* data, + struct wl_keyboard* keyboard, + uint32_t serial, + uint32_t time, + uint32_t key, + uint32_t state) +{ + int keyCode; + int action; + _GLFWwindow* window = _glfw.wl.keyboardFocus; + GLFWbool shouldRepeat; + struct itimerspec timer = {}; + + if (!window) + return; + + keyCode = toGLFWKeyCode(key); + action = state == WL_KEYBOARD_KEY_STATE_PRESSED + ? GLFW_PRESS : GLFW_RELEASE; + + _glfw.wl.serial = serial; + _glfwInputKey(window, keyCode, key, action, + _glfw.wl.xkb.modifiers); + + if (action == GLFW_PRESS) + { + shouldRepeat = inputChar(window, key); + + if (shouldRepeat && _glfw.wl.keyboardRepeatRate > 0) + { + _glfw.wl.keyboardLastKey = keyCode; + _glfw.wl.keyboardLastScancode = key; + if (_glfw.wl.keyboardRepeatRate > 1) + timer.it_interval.tv_nsec = 1000000000 / _glfw.wl.keyboardRepeatRate; + else + timer.it_interval.tv_sec = 1; + timer.it_value.tv_sec = _glfw.wl.keyboardRepeatDelay / 1000; + timer.it_value.tv_nsec = (_glfw.wl.keyboardRepeatDelay % 1000) * 1000000; + } + } + timerfd_settime(_glfw.wl.timerfd, 0, &timer, NULL); +} + +static void keyboardHandleModifiers(void* data, + struct wl_keyboard* keyboard, + uint32_t serial, + uint32_t modsDepressed, + uint32_t modsLatched, + uint32_t modsLocked, + uint32_t group) +{ + xkb_mod_mask_t mask; + unsigned int modifiers = 0; + + _glfw.wl.serial = serial; + + if (!_glfw.wl.xkb.keymap) + return; + + xkb_state_update_mask(_glfw.wl.xkb.state, + modsDepressed, + modsLatched, + modsLocked, + 0, + 0, + group); + + mask = xkb_state_serialize_mods(_glfw.wl.xkb.state, + XKB_STATE_MODS_DEPRESSED | + XKB_STATE_LAYOUT_DEPRESSED | + XKB_STATE_MODS_LATCHED | + XKB_STATE_LAYOUT_LATCHED); + if (mask & _glfw.wl.xkb.controlMask) + modifiers |= GLFW_MOD_CONTROL; + if (mask & _glfw.wl.xkb.altMask) + modifiers |= GLFW_MOD_ALT; + if (mask & _glfw.wl.xkb.shiftMask) + modifiers |= GLFW_MOD_SHIFT; + if (mask & _glfw.wl.xkb.superMask) + 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; +} + +#ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION +static void keyboardHandleRepeatInfo(void* data, + struct wl_keyboard* keyboard, + int32_t rate, + int32_t delay) +{ + if (keyboard != _glfw.wl.keyboard) + return; + + _glfw.wl.keyboardRepeatRate = rate; + _glfw.wl.keyboardRepeatDelay = delay; +} +#endif + +static const struct wl_keyboard_listener keyboardListener = { + keyboardHandleKeymap, + keyboardHandleEnter, + keyboardHandleLeave, + keyboardHandleKey, + keyboardHandleModifiers, +#ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION + keyboardHandleRepeatInfo, +#endif +}; + +static void seatHandleCapabilities(void* data, + struct wl_seat* seat, + enum wl_seat_capability caps) +{ + if ((caps & WL_SEAT_CAPABILITY_POINTER) && !_glfw.wl.pointer) + { + _glfw.wl.pointer = wl_seat_get_pointer(seat); + wl_pointer_add_listener(_glfw.wl.pointer, &pointerListener, NULL); + } + else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _glfw.wl.pointer) + { + wl_pointer_destroy(_glfw.wl.pointer); + _glfw.wl.pointer = NULL; + } + + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !_glfw.wl.keyboard) + { + _glfw.wl.keyboard = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(_glfw.wl.keyboard, &keyboardListener, NULL); + } + else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && _glfw.wl.keyboard) + { + wl_keyboard_destroy(_glfw.wl.keyboard); + _glfw.wl.keyboard = NULL; + } +} + +static void seatHandleName(void* data, + struct wl_seat* seat, + const char* name) +{ +} + +static const struct wl_seat_listener seatListener = { + seatHandleCapabilities, + seatHandleName, +}; + +static void dataOfferHandleOffer(void* data, + struct wl_data_offer* dataOffer, + const char* mimeType) +{ +} + +static const struct wl_data_offer_listener dataOfferListener = { + dataOfferHandleOffer, +}; + +static void dataDeviceHandleDataOffer(void* data, + struct wl_data_device* dataDevice, + struct wl_data_offer* id) +{ + if (_glfw.wl.dataOffer) + wl_data_offer_destroy(_glfw.wl.dataOffer); + + _glfw.wl.dataOffer = id; + wl_data_offer_add_listener(_glfw.wl.dataOffer, &dataOfferListener, NULL); +} + +static void dataDeviceHandleEnter(void* data, + struct wl_data_device* dataDevice, + uint32_t serial, + struct wl_surface *surface, + wl_fixed_t x, + wl_fixed_t y, + struct wl_data_offer *id) +{ +} + +static void dataDeviceHandleLeave(void* data, + struct wl_data_device* dataDevice) +{ +} + +static void dataDeviceHandleMotion(void* data, + struct wl_data_device* dataDevice, + uint32_t time, + wl_fixed_t x, + wl_fixed_t y) +{ +} + +static void dataDeviceHandleDrop(void* data, + struct wl_data_device* dataDevice) +{ +} + +static void dataDeviceHandleSelection(void* data, + struct wl_data_device* dataDevice, + struct wl_data_offer* id) +{ +} + +static const struct wl_data_device_listener dataDeviceListener = { + dataDeviceHandleDataOffer, + dataDeviceHandleEnter, + dataDeviceHandleLeave, + dataDeviceHandleMotion, + dataDeviceHandleDrop, + dataDeviceHandleSelection, +}; + +static void wmBaseHandlePing(void* data, + struct xdg_wm_base* wmBase, + uint32_t serial) +{ + xdg_wm_base_pong(wmBase, serial); +} + +static const struct xdg_wm_base_listener wmBaseListener = { + wmBaseHandlePing +}; + +static void registryHandleGlobal(void* data, + struct wl_registry* registry, + uint32_t name, + const char* interface, + uint32_t version) +{ + if (strcmp(interface, "wl_compositor") == 0) + { + _glfw.wl.compositorVersion = min(3, version); + _glfw.wl.compositor = + wl_registry_bind(registry, name, &wl_compositor_interface, + _glfw.wl.compositorVersion); + } + else if (strcmp(interface, "wl_subcompositor") == 0) + { + _glfw.wl.subcompositor = + wl_registry_bind(registry, name, &wl_subcompositor_interface, 1); + } + else if (strcmp(interface, "wl_shm") == 0) + { + _glfw.wl.shm = + wl_registry_bind(registry, name, &wl_shm_interface, 1); + } + else if (strcmp(interface, "wl_output") == 0) + { + _glfwAddOutputWayland(name, version); + } + else if (strcmp(interface, "wl_seat") == 0) + { + if (!_glfw.wl.seat) + { + _glfw.wl.seatVersion = min(4, version); + _glfw.wl.seat = + wl_registry_bind(registry, name, &wl_seat_interface, + _glfw.wl.seatVersion); + wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL); + } + } + else if (strcmp(interface, "wl_data_device_manager") == 0) + { + if (!_glfw.wl.dataDeviceManager) + { + _glfw.wl.dataDeviceManager = + wl_registry_bind(registry, name, + &wl_data_device_manager_interface, 1); + } + } + else if (strcmp(interface, "xdg_wm_base") == 0) + { + _glfw.wl.wmBase = + wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); + xdg_wm_base_add_listener(_glfw.wl.wmBase, &wmBaseListener, NULL); + } + else if (strcmp(interface, "zxdg_decoration_manager_v1") == 0) + { + _glfw.wl.decorationManager = + wl_registry_bind(registry, name, + &zxdg_decoration_manager_v1_interface, + 1); + } + else if (strcmp(interface, "wp_viewporter") == 0) + { + _glfw.wl.viewporter = + wl_registry_bind(registry, name, &wp_viewporter_interface, 1); + } + else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) + { + _glfw.wl.relativePointerManager = + wl_registry_bind(registry, name, + &zwp_relative_pointer_manager_v1_interface, + 1); + } + else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) + { + _glfw.wl.pointerConstraints = + wl_registry_bind(registry, name, + &zwp_pointer_constraints_v1_interface, + 1); + } + 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, + struct wl_registry *registry, + uint32_t name) +{ + int i; + _GLFWmonitor* monitor; + + for (i = 0; i < _glfw.monitorCount; ++i) + { + monitor = _glfw.monitors[i]; + if (monitor->wl.name == name) + { + _glfwInputMonitor(monitor, GLFW_DISCONNECTED, 0); + return; + } + } +} + + +static const struct wl_registry_listener registryListener = { + registryHandleGlobal, + registryHandleGlobalRemove +}; + +// Create key code translation tables +// +static void createKeyTables(void) +{ + int scancode; + + memset(_glfw.wl.keycodes, -1, sizeof(_glfw.wl.keycodes)); + memset(_glfw.wl.scancodes, -1, sizeof(_glfw.wl.scancodes)); + + _glfw.wl.keycodes[KEY_GRAVE] = GLFW_KEY_GRAVE_ACCENT; + _glfw.wl.keycodes[KEY_1] = GLFW_KEY_1; + _glfw.wl.keycodes[KEY_2] = GLFW_KEY_2; + _glfw.wl.keycodes[KEY_3] = GLFW_KEY_3; + _glfw.wl.keycodes[KEY_4] = GLFW_KEY_4; + _glfw.wl.keycodes[KEY_5] = GLFW_KEY_5; + _glfw.wl.keycodes[KEY_6] = GLFW_KEY_6; + _glfw.wl.keycodes[KEY_7] = GLFW_KEY_7; + _glfw.wl.keycodes[KEY_8] = GLFW_KEY_8; + _glfw.wl.keycodes[KEY_9] = GLFW_KEY_9; + _glfw.wl.keycodes[KEY_0] = GLFW_KEY_0; + _glfw.wl.keycodes[KEY_SPACE] = GLFW_KEY_SPACE; + _glfw.wl.keycodes[KEY_MINUS] = GLFW_KEY_MINUS; + _glfw.wl.keycodes[KEY_EQUAL] = GLFW_KEY_EQUAL; + _glfw.wl.keycodes[KEY_Q] = GLFW_KEY_Q; + _glfw.wl.keycodes[KEY_W] = GLFW_KEY_W; + _glfw.wl.keycodes[KEY_E] = GLFW_KEY_E; + _glfw.wl.keycodes[KEY_R] = GLFW_KEY_R; + _glfw.wl.keycodes[KEY_T] = GLFW_KEY_T; + _glfw.wl.keycodes[KEY_Y] = GLFW_KEY_Y; + _glfw.wl.keycodes[KEY_U] = GLFW_KEY_U; + _glfw.wl.keycodes[KEY_I] = GLFW_KEY_I; + _glfw.wl.keycodes[KEY_O] = GLFW_KEY_O; + _glfw.wl.keycodes[KEY_P] = GLFW_KEY_P; + _glfw.wl.keycodes[KEY_LEFTBRACE] = GLFW_KEY_LEFT_BRACKET; + _glfw.wl.keycodes[KEY_RIGHTBRACE] = GLFW_KEY_RIGHT_BRACKET; + _glfw.wl.keycodes[KEY_A] = GLFW_KEY_A; + _glfw.wl.keycodes[KEY_S] = GLFW_KEY_S; + _glfw.wl.keycodes[KEY_D] = GLFW_KEY_D; + _glfw.wl.keycodes[KEY_F] = GLFW_KEY_F; + _glfw.wl.keycodes[KEY_G] = GLFW_KEY_G; + _glfw.wl.keycodes[KEY_H] = GLFW_KEY_H; + _glfw.wl.keycodes[KEY_J] = GLFW_KEY_J; + _glfw.wl.keycodes[KEY_K] = GLFW_KEY_K; + _glfw.wl.keycodes[KEY_L] = GLFW_KEY_L; + _glfw.wl.keycodes[KEY_SEMICOLON] = GLFW_KEY_SEMICOLON; + _glfw.wl.keycodes[KEY_APOSTROPHE] = GLFW_KEY_APOSTROPHE; + _glfw.wl.keycodes[KEY_Z] = GLFW_KEY_Z; + _glfw.wl.keycodes[KEY_X] = GLFW_KEY_X; + _glfw.wl.keycodes[KEY_C] = GLFW_KEY_C; + _glfw.wl.keycodes[KEY_V] = GLFW_KEY_V; + _glfw.wl.keycodes[KEY_B] = GLFW_KEY_B; + _glfw.wl.keycodes[KEY_N] = GLFW_KEY_N; + _glfw.wl.keycodes[KEY_M] = GLFW_KEY_M; + _glfw.wl.keycodes[KEY_COMMA] = GLFW_KEY_COMMA; + _glfw.wl.keycodes[KEY_DOT] = GLFW_KEY_PERIOD; + _glfw.wl.keycodes[KEY_SLASH] = GLFW_KEY_SLASH; + _glfw.wl.keycodes[KEY_BACKSLASH] = GLFW_KEY_BACKSLASH; + _glfw.wl.keycodes[KEY_ESC] = GLFW_KEY_ESCAPE; + _glfw.wl.keycodes[KEY_TAB] = GLFW_KEY_TAB; + _glfw.wl.keycodes[KEY_LEFTSHIFT] = GLFW_KEY_LEFT_SHIFT; + _glfw.wl.keycodes[KEY_RIGHTSHIFT] = GLFW_KEY_RIGHT_SHIFT; + _glfw.wl.keycodes[KEY_LEFTCTRL] = GLFW_KEY_LEFT_CONTROL; + _glfw.wl.keycodes[KEY_RIGHTCTRL] = GLFW_KEY_RIGHT_CONTROL; + _glfw.wl.keycodes[KEY_LEFTALT] = GLFW_KEY_LEFT_ALT; + _glfw.wl.keycodes[KEY_RIGHTALT] = GLFW_KEY_RIGHT_ALT; + _glfw.wl.keycodes[KEY_LEFTMETA] = GLFW_KEY_LEFT_SUPER; + _glfw.wl.keycodes[KEY_RIGHTMETA] = GLFW_KEY_RIGHT_SUPER; + _glfw.wl.keycodes[KEY_MENU] = GLFW_KEY_MENU; + _glfw.wl.keycodes[KEY_NUMLOCK] = GLFW_KEY_NUM_LOCK; + _glfw.wl.keycodes[KEY_CAPSLOCK] = GLFW_KEY_CAPS_LOCK; + _glfw.wl.keycodes[KEY_PRINT] = GLFW_KEY_PRINT_SCREEN; + _glfw.wl.keycodes[KEY_SCROLLLOCK] = GLFW_KEY_SCROLL_LOCK; + _glfw.wl.keycodes[KEY_PAUSE] = GLFW_KEY_PAUSE; + _glfw.wl.keycodes[KEY_DELETE] = GLFW_KEY_DELETE; + _glfw.wl.keycodes[KEY_BACKSPACE] = GLFW_KEY_BACKSPACE; + _glfw.wl.keycodes[KEY_ENTER] = GLFW_KEY_ENTER; + _glfw.wl.keycodes[KEY_HOME] = GLFW_KEY_HOME; + _glfw.wl.keycodes[KEY_END] = GLFW_KEY_END; + _glfw.wl.keycodes[KEY_PAGEUP] = GLFW_KEY_PAGE_UP; + _glfw.wl.keycodes[KEY_PAGEDOWN] = GLFW_KEY_PAGE_DOWN; + _glfw.wl.keycodes[KEY_INSERT] = GLFW_KEY_INSERT; + _glfw.wl.keycodes[KEY_LEFT] = GLFW_KEY_LEFT; + _glfw.wl.keycodes[KEY_RIGHT] = GLFW_KEY_RIGHT; + _glfw.wl.keycodes[KEY_DOWN] = GLFW_KEY_DOWN; + _glfw.wl.keycodes[KEY_UP] = GLFW_KEY_UP; + _glfw.wl.keycodes[KEY_F1] = GLFW_KEY_F1; + _glfw.wl.keycodes[KEY_F2] = GLFW_KEY_F2; + _glfw.wl.keycodes[KEY_F3] = GLFW_KEY_F3; + _glfw.wl.keycodes[KEY_F4] = GLFW_KEY_F4; + _glfw.wl.keycodes[KEY_F5] = GLFW_KEY_F5; + _glfw.wl.keycodes[KEY_F6] = GLFW_KEY_F6; + _glfw.wl.keycodes[KEY_F7] = GLFW_KEY_F7; + _glfw.wl.keycodes[KEY_F8] = GLFW_KEY_F8; + _glfw.wl.keycodes[KEY_F9] = GLFW_KEY_F9; + _glfw.wl.keycodes[KEY_F10] = GLFW_KEY_F10; + _glfw.wl.keycodes[KEY_F11] = GLFW_KEY_F11; + _glfw.wl.keycodes[KEY_F12] = GLFW_KEY_F12; + _glfw.wl.keycodes[KEY_F13] = GLFW_KEY_F13; + _glfw.wl.keycodes[KEY_F14] = GLFW_KEY_F14; + _glfw.wl.keycodes[KEY_F15] = GLFW_KEY_F15; + _glfw.wl.keycodes[KEY_F16] = GLFW_KEY_F16; + _glfw.wl.keycodes[KEY_F17] = GLFW_KEY_F17; + _glfw.wl.keycodes[KEY_F18] = GLFW_KEY_F18; + _glfw.wl.keycodes[KEY_F19] = GLFW_KEY_F19; + _glfw.wl.keycodes[KEY_F20] = GLFW_KEY_F20; + _glfw.wl.keycodes[KEY_F21] = GLFW_KEY_F21; + _glfw.wl.keycodes[KEY_F22] = GLFW_KEY_F22; + _glfw.wl.keycodes[KEY_F23] = GLFW_KEY_F23; + _glfw.wl.keycodes[KEY_F24] = GLFW_KEY_F24; + _glfw.wl.keycodes[KEY_KPSLASH] = GLFW_KEY_KP_DIVIDE; + _glfw.wl.keycodes[KEY_KPDOT] = GLFW_KEY_KP_MULTIPLY; + _glfw.wl.keycodes[KEY_KPMINUS] = GLFW_KEY_KP_SUBTRACT; + _glfw.wl.keycodes[KEY_KPPLUS] = GLFW_KEY_KP_ADD; + _glfw.wl.keycodes[KEY_KP0] = GLFW_KEY_KP_0; + _glfw.wl.keycodes[KEY_KP1] = GLFW_KEY_KP_1; + _glfw.wl.keycodes[KEY_KP2] = GLFW_KEY_KP_2; + _glfw.wl.keycodes[KEY_KP3] = GLFW_KEY_KP_3; + _glfw.wl.keycodes[KEY_KP4] = GLFW_KEY_KP_4; + _glfw.wl.keycodes[KEY_KP5] = GLFW_KEY_KP_5; + _glfw.wl.keycodes[KEY_KP6] = GLFW_KEY_KP_6; + _glfw.wl.keycodes[KEY_KP7] = GLFW_KEY_KP_7; + _glfw.wl.keycodes[KEY_KP8] = GLFW_KEY_KP_8; + _glfw.wl.keycodes[KEY_KP9] = GLFW_KEY_KP_9; + _glfw.wl.keycodes[KEY_KPCOMMA] = GLFW_KEY_KP_DECIMAL; + _glfw.wl.keycodes[KEY_KPEQUAL] = GLFW_KEY_KP_EQUAL; + _glfw.wl.keycodes[KEY_KPENTER] = GLFW_KEY_KP_ENTER; + + for (scancode = 0; scancode < 256; scancode++) + { + if (_glfw.wl.keycodes[scancode] > 0) + _glfw.wl.scancodes[_glfw.wl.keycodes[scancode]] = scancode; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformInit(void) +{ + const char *cursorTheme; + const char *cursorSizeStr; + char *cursorSizeEnd; + long cursorSizeLong; + int cursorSize; + + _glfw.wl.cursor.handle = _glfw_dlopen("libwayland-cursor.so.0"); + if (!_glfw.wl.cursor.handle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to open libwayland-cursor"); + return GLFW_FALSE; + } + + _glfw.wl.cursor.theme_load = (PFN_wl_cursor_theme_load) + _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_load"); + _glfw.wl.cursor.theme_destroy = (PFN_wl_cursor_theme_destroy) + _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_destroy"); + _glfw.wl.cursor.theme_get_cursor = (PFN_wl_cursor_theme_get_cursor) + _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_get_cursor"); + _glfw.wl.cursor.image_get_buffer = (PFN_wl_cursor_image_get_buffer) + _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_image_get_buffer"); + + _glfw.wl.egl.handle = _glfw_dlopen("libwayland-egl.so.1"); + if (!_glfw.wl.egl.handle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to open libwayland-egl"); + return GLFW_FALSE; + } + + _glfw.wl.egl.window_create = (PFN_wl_egl_window_create) + _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_create"); + _glfw.wl.egl.window_destroy = (PFN_wl_egl_window_destroy) + _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_destroy"); + _glfw.wl.egl.window_resize = (PFN_wl_egl_window_resize) + _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_resize"); + + _glfw.wl.xkb.handle = _glfw_dlopen("libxkbcommon.so.0"); + 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) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_new"); + _glfw.wl.xkb.context_unref = (PFN_xkb_context_unref) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_unref"); + _glfw.wl.xkb.keymap_new_from_string = (PFN_xkb_keymap_new_from_string) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_new_from_string"); + _glfw.wl.xkb.keymap_unref = (PFN_xkb_keymap_unref) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_unref"); + _glfw.wl.xkb.keymap_mod_get_index = (PFN_xkb_keymap_mod_get_index) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_index"); + _glfw.wl.xkb.keymap_key_repeats = (PFN_xkb_keymap_key_repeats) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_key_repeats"); + _glfw.wl.xkb.state_new = (PFN_xkb_state_new) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_new"); + _glfw.wl.xkb.state_unref = (PFN_xkb_state_unref) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_unref"); + _glfw.wl.xkb.state_key_get_syms = (PFN_xkb_state_key_get_syms) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_key_get_syms"); + _glfw.wl.xkb.state_update_mask = (PFN_xkb_state_update_mask) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_update_mask"); + _glfw.wl.xkb.state_serialize_mods = (PFN_xkb_state_serialize_mods) + _glfw_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) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_new_from_locale"); + _glfw.wl.xkb.compose_table_unref = (PFN_xkb_compose_table_unref) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_unref"); + _glfw.wl.xkb.compose_state_new = (PFN_xkb_compose_state_new) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_new"); + _glfw.wl.xkb.compose_state_unref = (PFN_xkb_compose_state_unref) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_unref"); + _glfw.wl.xkb.compose_state_feed = (PFN_xkb_compose_state_feed) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_feed"); + _glfw.wl.xkb.compose_state_get_status = (PFN_xkb_compose_state_get_status) + _glfw_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) + _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_one_sym"); +#endif + + _glfw.wl.display = wl_display_connect(NULL); + if (!_glfw.wl.display) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to connect to display"); + return GLFW_FALSE; + } + + _glfw.wl.registry = wl_display_get_registry(_glfw.wl.display); + wl_registry_add_listener(_glfw.wl.registry, ®istryListener, NULL); + + createKeyTables(); + + _glfw.wl.xkb.context = xkb_context_new(0); + if (!_glfw.wl.xkb.context) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to initialize xkb context"); + return GLFW_FALSE; + } + + // Sync so we got all registry objects + wl_display_roundtrip(_glfw.wl.display); + + // Sync so we got all initial output events + wl_display_roundtrip(_glfw.wl.display); + +#ifdef __linux__ + if (!_glfwInitJoysticksLinux()) + return GLFW_FALSE; +#endif + + _glfwInitTimerPOSIX(); + + _glfw.wl.timerfd = -1; + if (_glfw.wl.seatVersion >= 4) + _glfw.wl.timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); + + if (!_glfw.wl.wmBase) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to find xdg-shell in your compositor"); + return GLFW_FALSE; + } + + if (_glfw.wl.pointer && _glfw.wl.shm) + { + cursorTheme = getenv("XCURSOR_THEME"); + cursorSizeStr = getenv("XCURSOR_SIZE"); + cursorSize = 32; + if (cursorSizeStr) + { + errno = 0; + cursorSizeLong = strtol(cursorSizeStr, &cursorSizeEnd, 10); + if (!*cursorSizeEnd && !errno && cursorSizeLong > 0 && cursorSizeLong <= INT_MAX) + cursorSize = (int)cursorSizeLong; + } + _glfw.wl.cursorTheme = + wl_cursor_theme_load(cursorTheme, cursorSize, _glfw.wl.shm); + if (!_glfw.wl.cursorTheme) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Unable to load default cursor theme"); + return GLFW_FALSE; + } + // If this happens to be NULL, we just fallback to the scale=1 version. + _glfw.wl.cursorThemeHiDPI = + wl_cursor_theme_load(cursorTheme, 2 * cursorSize, _glfw.wl.shm); + _glfw.wl.cursorSurface = + wl_compositor_create_surface(_glfw.wl.compositor); + _glfw.wl.cursorTimerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); + } + + if (_glfw.wl.seat && _glfw.wl.dataDeviceManager) + { + _glfw.wl.dataDevice = + wl_data_device_manager_get_data_device(_glfw.wl.dataDeviceManager, + _glfw.wl.seat); + wl_data_device_add_listener(_glfw.wl.dataDevice, &dataDeviceListener, NULL); + _glfw.wl.clipboardString = malloc(4096); + if (!_glfw.wl.clipboardString) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Unable to allocate clipboard memory"); + return GLFW_FALSE; + } + _glfw.wl.clipboardSize = 4096; + } + + return GLFW_TRUE; +} + +void _glfwPlatformTerminate(void) +{ +#ifdef __linux__ + _glfwTerminateJoysticksLinux(); +#endif + _glfwTerminateEGL(); + if (_glfw.wl.egl.handle) + { + _glfw_dlclose(_glfw.wl.egl.handle); + _glfw.wl.egl.handle = NULL; + } + +#ifdef HAVE_XKBCOMMON_COMPOSE_H + if (_glfw.wl.xkb.composeState) + xkb_compose_state_unref(_glfw.wl.xkb.composeState); +#endif + if (_glfw.wl.xkb.keymap) + xkb_keymap_unref(_glfw.wl.xkb.keymap); + if (_glfw.wl.xkb.state) + xkb_state_unref(_glfw.wl.xkb.state); + if (_glfw.wl.xkb.context) + xkb_context_unref(_glfw.wl.xkb.context); + if (_glfw.wl.xkb.handle) + { + _glfw_dlclose(_glfw.wl.xkb.handle); + _glfw.wl.xkb.handle = NULL; + } + + if (_glfw.wl.cursorTheme) + wl_cursor_theme_destroy(_glfw.wl.cursorTheme); + if (_glfw.wl.cursorThemeHiDPI) + wl_cursor_theme_destroy(_glfw.wl.cursorThemeHiDPI); + if (_glfw.wl.cursor.handle) + { + _glfw_dlclose(_glfw.wl.cursor.handle); + _glfw.wl.cursor.handle = NULL; + } + + if (_glfw.wl.cursorSurface) + wl_surface_destroy(_glfw.wl.cursorSurface); + if (_glfw.wl.subcompositor) + wl_subcompositor_destroy(_glfw.wl.subcompositor); + if (_glfw.wl.compositor) + wl_compositor_destroy(_glfw.wl.compositor); + if (_glfw.wl.shm) + wl_shm_destroy(_glfw.wl.shm); + if (_glfw.wl.viewporter) + wp_viewporter_destroy(_glfw.wl.viewporter); + if (_glfw.wl.decorationManager) + zxdg_decoration_manager_v1_destroy(_glfw.wl.decorationManager); + if (_glfw.wl.wmBase) + xdg_wm_base_destroy(_glfw.wl.wmBase); + if (_glfw.wl.dataSource) + wl_data_source_destroy(_glfw.wl.dataSource); + if (_glfw.wl.dataDevice) + wl_data_device_destroy(_glfw.wl.dataDevice); + if (_glfw.wl.dataOffer) + wl_data_offer_destroy(_glfw.wl.dataOffer); + if (_glfw.wl.dataDeviceManager) + wl_data_device_manager_destroy(_glfw.wl.dataDeviceManager); + if (_glfw.wl.pointer) + wl_pointer_destroy(_glfw.wl.pointer); + if (_glfw.wl.keyboard) + wl_keyboard_destroy(_glfw.wl.keyboard); + if (_glfw.wl.seat) + wl_seat_destroy(_glfw.wl.seat); + if (_glfw.wl.relativePointerManager) + zwp_relative_pointer_manager_v1_destroy(_glfw.wl.relativePointerManager); + if (_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) + wl_registry_destroy(_glfw.wl.registry); + if (_glfw.wl.display) + { + wl_display_flush(_glfw.wl.display); + wl_display_disconnect(_glfw.wl.display); + } + + if (_glfw.wl.timerfd >= 0) + close(_glfw.wl.timerfd); + if (_glfw.wl.cursorTimerfd >= 0) + close(_glfw.wl.cursorTimerfd); + + if (_glfw.wl.clipboardString) + free(_glfw.wl.clipboardString); + if (_glfw.wl.clipboardSendString) + free(_glfw.wl.clipboardSendString); +} + +const char* _glfwPlatformGetVersionString(void) +{ + return _GLFW_VERSION_NUMBER " Wayland EGL OSMesa" +#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) + " clock_gettime" +#else + " gettimeofday" +#endif + " evdev" +#if defined(_GLFW_BUILD_DLL) + " shared" +#endif + ; +} diff --git a/source/MaterialXGraphEditor/External/Glfw/src/wl_monitor.c b/source/MaterialXGraphEditor/External/Glfw/src/wl_monitor.c new file mode 100644 index 0000000000..55f62015ef --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/wl_monitor.c @@ -0,0 +1,225 @@ +//======================================================================== +// GLFW 3.4 Wayland - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2014 Jonas Ã…dahl +// +// 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. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#include "internal.h" + +#include +#include +#include +#include +#include + + +static void outputHandleGeometry(void* data, + struct wl_output* output, + int32_t x, + int32_t y, + int32_t physicalWidth, + int32_t physicalHeight, + int32_t subpixel, + const char* make, + const char* model, + int32_t transform) +{ + struct _GLFWmonitor *monitor = data; + char name[1024]; + + monitor->wl.x = x; + monitor->wl.y = y; + monitor->widthMM = physicalWidth; + monitor->heightMM = physicalHeight; + + snprintf(name, sizeof(name), "%s %s", make, model); + monitor->name = _glfw_strdup(name); +} + +static void outputHandleMode(void* data, + struct wl_output* output, + uint32_t flags, + int32_t width, + int32_t height, + int32_t refresh) +{ + struct _GLFWmonitor *monitor = data; + GLFWvidmode mode; + + mode.width = width; + mode.height = height; + mode.redBits = 8; + mode.greenBits = 8; + mode.blueBits = 8; + mode.refreshRate = (int) round(refresh / 1000.0); + + monitor->modeCount++; + monitor->modes = + realloc(monitor->modes, monitor->modeCount * sizeof(GLFWvidmode)); + monitor->modes[monitor->modeCount - 1] = mode; + + if (flags & WL_OUTPUT_MODE_CURRENT) + monitor->wl.currentMode = monitor->modeCount - 1; +} + +static void outputHandleDone(void* data, struct wl_output* output) +{ + struct _GLFWmonitor *monitor = data; + + _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST); +} + +static void outputHandleScale(void* data, + struct wl_output* output, + int32_t factor) +{ + struct _GLFWmonitor *monitor = data; + + monitor->wl.scale = factor; +} + +static const struct wl_output_listener outputListener = { + outputHandleGeometry, + outputHandleMode, + outputHandleDone, + outputHandleScale, +}; + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +void _glfwAddOutputWayland(uint32_t name, uint32_t version) +{ + _GLFWmonitor *monitor; + struct wl_output *output; + + if (version < 2) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Unsupported output interface version"); + return; + } + + // The actual name of this output will be set in the geometry handler. + monitor = _glfwAllocMonitor(NULL, 0, 0); + + output = wl_registry_bind(_glfw.wl.registry, + name, + &wl_output_interface, + 2); + if (!output) + { + _glfwFreeMonitor(monitor); + return; + } + + monitor->wl.scale = 1; + monitor->wl.output = output; + monitor->wl.name = name; + + wl_output_add_listener(output, &outputListener, monitor); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) +{ + if (monitor->wl.output) + wl_output_destroy(monitor->wl.output); +} + +void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) +{ + if (xpos) + *xpos = monitor->wl.x; + if (ypos) + *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; +} + +void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, + int* xpos, int* ypos, + int* width, int* height) +{ + if (xpos) + *xpos = monitor->wl.x; + if (ypos) + *ypos = monitor->wl.y; + if (width) + *width = monitor->modes[monitor->wl.currentMode].width; + if (height) + *height = monitor->modes[monitor->wl.currentMode].height; +} + +GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) +{ + *found = monitor->modeCount; + return monitor->modes; +} + +void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) +{ + *mode = monitor->modes[monitor->wl.currentMode]; +} + +GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Gamma ramp access is not available"); + return GLFW_FALSE; +} + +void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, + const GLFWgammaramp* ramp) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Gamma ramp access is not available"); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return monitor->wl.output; +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/wl_platform.h b/source/MaterialXGraphEditor/External/Glfw/src/wl_platform.h new file mode 100644 index 0000000000..542cc78d07 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/wl_platform.h @@ -0,0 +1,357 @@ +//======================================================================== +// GLFW 3.4 Wayland - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2014 Jonas Ã…dahl +// +// 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. +// +//======================================================================== + +#include +#include +#ifdef HAVE_XKBCOMMON_COMPOSE_H +#include +#endif +#include + +typedef VkFlags VkWaylandSurfaceCreateFlagsKHR; + +typedef struct VkWaylandSurfaceCreateInfoKHR +{ + VkStructureType sType; + const void* pNext; + VkWaylandSurfaceCreateFlagsKHR flags; + struct wl_display* display; + struct wl_surface* surface; +} VkWaylandSurfaceCreateInfoKHR; + +typedef VkResult (APIENTRY *PFN_vkCreateWaylandSurfaceKHR)(VkInstance,const VkWaylandSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); +typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)(VkPhysicalDevice,uint32_t,struct wl_display*); + +#include "posix_thread.h" +#include "posix_time.h" +#ifdef __linux__ +#include "linux_joystick.h" +#else +#include "null_joystick.h" +#endif +#include "xkb_unicode.h" +#include "egl_context.h" +#include "osmesa_context.h" + +#include "wayland-xdg-shell-client-protocol.h" +#include "wayland-xdg-decoration-client-protocol.h" +#include "wayland-viewporter-client-protocol.h" +#include "wayland-relative-pointer-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_dlclose(handle) dlclose(handle) +#define _glfw_dlsym(handle, name) dlsym(handle, name) + +#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->wl.native) +#define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.wl.display) + +#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowWayland wl +#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryWayland wl +#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorWayland wl +#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorWayland wl + +#define _GLFW_PLATFORM_CONTEXT_STATE struct { int dummyContext; } +#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE struct { int dummyLibraryContext; } + +struct wl_cursor_image { + uint32_t width; + uint32_t height; + uint32_t hotspot_x; + uint32_t hotspot_y; + uint32_t delay; +}; +struct wl_cursor { + unsigned int image_count; + struct wl_cursor_image** images; + char* name; +}; +typedef struct wl_cursor_theme* (* PFN_wl_cursor_theme_load)(const char*, int, struct wl_shm*); +typedef void (* PFN_wl_cursor_theme_destroy)(struct wl_cursor_theme*); +typedef struct wl_cursor* (* PFN_wl_cursor_theme_get_cursor)(struct wl_cursor_theme*, const char*); +typedef struct wl_buffer* (* PFN_wl_cursor_image_get_buffer)(struct wl_cursor_image*); +#define wl_cursor_theme_load _glfw.wl.cursor.theme_load +#define wl_cursor_theme_destroy _glfw.wl.cursor.theme_destroy +#define wl_cursor_theme_get_cursor _glfw.wl.cursor.theme_get_cursor +#define wl_cursor_image_get_buffer _glfw.wl.cursor.image_get_buffer + +typedef struct wl_egl_window* (* PFN_wl_egl_window_create)(struct wl_surface*, int, int); +typedef void (* PFN_wl_egl_window_destroy)(struct wl_egl_window*); +typedef void (* PFN_wl_egl_window_resize)(struct wl_egl_window*, int, int, int, int); +#define wl_egl_window_create _glfw.wl.egl.window_create +#define wl_egl_window_destroy _glfw.wl.egl.window_destroy +#define wl_egl_window_resize _glfw.wl.egl.window_resize + +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 int (* PFN_xkb_keymap_key_repeats)(struct xkb_keymap*, xkb_keycode_t); +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_keymap_key_repeats _glfw.wl.xkb.keymap_key_repeats +#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 + +#define _GLFW_DECORATION_WIDTH 4 +#define _GLFW_DECORATION_TOP 24 +#define _GLFW_DECORATION_VERTICAL (_GLFW_DECORATION_TOP + _GLFW_DECORATION_WIDTH) +#define _GLFW_DECORATION_HORIZONTAL (2 * _GLFW_DECORATION_WIDTH) + +typedef enum _GLFWdecorationSideWayland +{ + mainWindow, + topDecoration, + leftDecoration, + rightDecoration, + bottomDecoration, + +} _GLFWdecorationSideWayland; + +typedef struct _GLFWdecorationWayland +{ + struct wl_surface* surface; + struct wl_subsurface* subsurface; + struct wp_viewport* viewport; + +} _GLFWdecorationWayland; + +// Wayland-specific per-window data +// +typedef struct _GLFWwindowWayland +{ + int width, height; + GLFWbool visible; + GLFWbool maximized; + GLFWbool hovered; + GLFWbool transparent; + struct wl_surface* surface; + struct wl_egl_window* native; + struct wl_callback* callback; + + struct { + struct xdg_surface* surface; + struct xdg_toplevel* toplevel; + struct zxdg_toplevel_decoration_v1* decoration; + } xdg; + + _GLFWcursor* currentCursor; + double cursorPosX, cursorPosY; + + char* title; + + // We need to track the monitors the window spans on to calculate the + // optimal scaling factor. + int scale; + _GLFWmonitor** monitors; + int monitorsCount; + int monitorsSize; + + struct { + struct zwp_relative_pointer_v1* relativePointer; + struct zwp_locked_pointer_v1* lockedPointer; + } pointerLock; + + struct zwp_idle_inhibitor_v1* idleInhibitor; + + GLFWbool wasFullscreen; + + struct { + GLFWbool serverSide; + struct wl_buffer* buffer; + _GLFWdecorationWayland top, left, right, bottom; + int focus; + } decorations; + +} _GLFWwindowWayland; + +// Wayland-specific global data +// +typedef struct _GLFWlibraryWayland +{ + struct wl_display* display; + struct wl_registry* registry; + struct wl_compositor* compositor; + struct wl_subcompositor* subcompositor; + struct wl_shm* shm; + struct wl_seat* seat; + struct wl_pointer* pointer; + struct wl_keyboard* keyboard; + struct wl_data_device_manager* dataDeviceManager; + struct wl_data_device* dataDevice; + struct wl_data_offer* dataOffer; + struct wl_data_source* dataSource; + struct xdg_wm_base* wmBase; + struct zxdg_decoration_manager_v1* decorationManager; + struct wp_viewporter* viewporter; + struct zwp_relative_pointer_manager_v1* relativePointerManager; + struct zwp_pointer_constraints_v1* pointerConstraints; + struct zwp_idle_inhibit_manager_v1* idleInhibitManager; + + int compositorVersion; + int seatVersion; + + struct wl_cursor_theme* cursorTheme; + struct wl_cursor_theme* cursorThemeHiDPI; + struct wl_surface* cursorSurface; + const char* cursorPreviousName; + int cursorTimerfd; + uint32_t serial; + + int32_t keyboardRepeatRate; + int32_t keyboardRepeatDelay; + int keyboardLastKey; + int keyboardLastScancode; + char* clipboardString; + size_t clipboardSize; + char* clipboardSendString; + size_t clipboardSendSize; + int timerfd; + short int keycodes[256]; + short int scancodes[GLFW_KEY_LAST + 1]; + + struct { + void* handle; + struct xkb_context* context; + struct xkb_keymap* keymap; + struct xkb_state* state; + +#ifdef HAVE_XKBCOMMON_COMPOSE_H + struct xkb_compose_state* composeState; +#endif + + xkb_mod_mask_t controlMask; + xkb_mod_mask_t altMask; + xkb_mod_mask_t shiftMask; + xkb_mod_mask_t superMask; + xkb_mod_mask_t capsLockMask; + xkb_mod_mask_t numLockMask; + 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_keymap_key_repeats keymap_key_repeats; + 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; + + _GLFWwindow* pointerFocus; + _GLFWwindow* keyboardFocus; + + struct { + void* handle; + + PFN_wl_cursor_theme_load theme_load; + PFN_wl_cursor_theme_destroy theme_destroy; + PFN_wl_cursor_theme_get_cursor theme_get_cursor; + PFN_wl_cursor_image_get_buffer image_get_buffer; + } cursor; + + struct { + void* handle; + + PFN_wl_egl_window_create window_create; + PFN_wl_egl_window_destroy window_destroy; + PFN_wl_egl_window_resize window_resize; + } egl; + +} _GLFWlibraryWayland; + +// Wayland-specific per-monitor data +// +typedef struct _GLFWmonitorWayland +{ + struct wl_output* output; + uint32_t name; + int currentMode; + + int x; + int y; + int scale; + +} _GLFWmonitorWayland; + +// Wayland-specific per-cursor data +// +typedef struct _GLFWcursorWayland +{ + struct wl_cursor* cursor; + struct wl_cursor* cursorHiDPI; + struct wl_buffer* buffer; + int width, height; + int xhot, yhot; + int currentImage; +} _GLFWcursorWayland; + + +void _glfwAddOutputWayland(uint32_t name, uint32_t version); + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/wl_window.c b/source/MaterialXGraphEditor/External/Glfw/src/wl_window.c new file mode 100644 index 0000000000..c8dde30ad1 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/wl_window.c @@ -0,0 +1,1754 @@ +//======================================================================== +// GLFW 3.4 Wayland - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2014 Jonas Ã…dahl +// +// 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. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#define _GNU_SOURCE + +#include "internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static int createTmpfileCloexec(char* tmpname) +{ + int fd; + + fd = mkostemp(tmpname, O_CLOEXEC); + if (fd >= 0) + unlink(tmpname); + + return fd; +} + +/* + * Create a new, unique, anonymous file of the given size, and + * return the file descriptor for it. The file descriptor is set + * CLOEXEC. The file is immediately suitable for mmap()'ing + * the given size at offset zero. + * + * The file should not have a permanent backing store like a disk, + * but may have if XDG_RUNTIME_DIR is not properly implemented in OS. + * + * The file name is deleted from the file system. + * + * The file is suitable for buffer sharing between processes by + * transmitting the file descriptor over Unix sockets using the + * SCM_RIGHTS methods. + * + * posix_fallocate() is used to guarantee that disk space is available + * for the file at the given size. If disk space is insufficient, errno + * is set to ENOSPC. If posix_fallocate() is not supported, program may + * receive SIGBUS on accessing mmap()'ed file contents instead. + */ +static int createAnonymousFile(off_t size) +{ + static const char template[] = "/glfw-shared-XXXXXX"; + const char* path; + char* name; + int fd; + int ret; + +#ifdef HAVE_MEMFD_CREATE + fd = memfd_create("glfw-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING); + if (fd >= 0) + { + // We can add this seal before calling posix_fallocate(), as the file + // is currently zero-sized anyway. + // + // There is also no need to check for the return value, we couldn’t do + // anything with it anyway. + fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); + } + else +#elif defined(SHM_ANON) + fd = shm_open(SHM_ANON, O_RDWR | O_CLOEXEC, 0600); + if (fd < 0) +#endif + { + path = getenv("XDG_RUNTIME_DIR"); + if (!path) + { + errno = ENOENT; + return -1; + } + + name = calloc(strlen(path) + sizeof(template), 1); + strcpy(name, path); + strcat(name, template); + + fd = createTmpfileCloexec(name); + free(name); + if (fd < 0) + return -1; + } + +#if defined(SHM_ANON) + // posix_fallocate does not work on SHM descriptors + ret = ftruncate(fd, size); +#else + ret = posix_fallocate(fd, 0, size); +#endif + if (ret != 0) + { + close(fd); + errno = ret; + return -1; + } + return fd; +} + +static struct wl_buffer* createShmBuffer(const GLFWimage* image) +{ + struct wl_shm_pool* pool; + struct wl_buffer* buffer; + int stride = image->width * 4; + int length = image->width * image->height * 4; + void* data; + int fd, i; + + fd = createAnonymousFile(length); + if (fd < 0) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Creating a buffer file for %d B failed: %m", + length); + return NULL; + } + + data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: mmap failed: %m"); + close(fd); + return NULL; + } + + pool = wl_shm_create_pool(_glfw.wl.shm, fd, length); + + close(fd); + unsigned char* source = (unsigned char*) image->pixels; + unsigned char* target = data; + for (i = 0; i < image->width * image->height; i++, source += 4) + { + unsigned int alpha = source[3]; + + *target++ = (unsigned char) ((source[2] * alpha) / 255); + *target++ = (unsigned char) ((source[1] * alpha) / 255); + *target++ = (unsigned char) ((source[0] * alpha) / 255); + *target++ = (unsigned char) alpha; + } + + buffer = + wl_shm_pool_create_buffer(pool, 0, + image->width, + image->height, + stride, WL_SHM_FORMAT_ARGB8888); + munmap(data, length); + wl_shm_pool_destroy(pool); + + return buffer; +} + +static void createDecoration(_GLFWdecorationWayland* decoration, + struct wl_surface* parent, + struct wl_buffer* buffer, GLFWbool opaque, + int x, int y, + int width, int height) +{ + struct wl_region* region; + + decoration->surface = wl_compositor_create_surface(_glfw.wl.compositor); + decoration->subsurface = + wl_subcompositor_get_subsurface(_glfw.wl.subcompositor, + decoration->surface, parent); + wl_subsurface_set_position(decoration->subsurface, x, y); + decoration->viewport = wp_viewporter_get_viewport(_glfw.wl.viewporter, + decoration->surface); + wp_viewport_set_destination(decoration->viewport, width, height); + wl_surface_attach(decoration->surface, buffer, 0, 0); + + if (opaque) + { + region = wl_compositor_create_region(_glfw.wl.compositor); + wl_region_add(region, 0, 0, width, height); + wl_surface_set_opaque_region(decoration->surface, region); + wl_surface_commit(decoration->surface); + wl_region_destroy(region); + } + else + wl_surface_commit(decoration->surface); +} + +static void createDecorations(_GLFWwindow* window) +{ + unsigned char data[] = { 224, 224, 224, 255 }; + const GLFWimage image = { 1, 1, data }; + GLFWbool opaque = (data[3] == 255); + + if (!_glfw.wl.viewporter || !window->decorated || window->wl.decorations.serverSide) + return; + + if (!window->wl.decorations.buffer) + window->wl.decorations.buffer = createShmBuffer(&image); + if (!window->wl.decorations.buffer) + return; + + createDecoration(&window->wl.decorations.top, window->wl.surface, + window->wl.decorations.buffer, opaque, + 0, -_GLFW_DECORATION_TOP, + window->wl.width, _GLFW_DECORATION_TOP); + createDecoration(&window->wl.decorations.left, window->wl.surface, + window->wl.decorations.buffer, opaque, + -_GLFW_DECORATION_WIDTH, -_GLFW_DECORATION_TOP, + _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP); + createDecoration(&window->wl.decorations.right, window->wl.surface, + window->wl.decorations.buffer, opaque, + window->wl.width, -_GLFW_DECORATION_TOP, + _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP); + createDecoration(&window->wl.decorations.bottom, window->wl.surface, + window->wl.decorations.buffer, opaque, + -_GLFW_DECORATION_WIDTH, window->wl.height, + window->wl.width + _GLFW_DECORATION_HORIZONTAL, _GLFW_DECORATION_WIDTH); +} + +static void destroyDecoration(_GLFWdecorationWayland* decoration) +{ + if (decoration->surface) + wl_surface_destroy(decoration->surface); + if (decoration->subsurface) + wl_subsurface_destroy(decoration->subsurface); + if (decoration->viewport) + wp_viewport_destroy(decoration->viewport); + decoration->surface = NULL; + decoration->subsurface = NULL; + decoration->viewport = NULL; +} + +static void destroyDecorations(_GLFWwindow* window) +{ + destroyDecoration(&window->wl.decorations.top); + destroyDecoration(&window->wl.decorations.left); + destroyDecoration(&window->wl.decorations.right); + destroyDecoration(&window->wl.decorations.bottom); +} + +static void xdgDecorationHandleConfigure(void* data, + struct zxdg_toplevel_decoration_v1* decoration, + uint32_t mode) +{ + _GLFWwindow* window = data; + + window->wl.decorations.serverSide = (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + + if (!window->wl.decorations.serverSide) + createDecorations(window); +} + +static const struct zxdg_toplevel_decoration_v1_listener xdgDecorationListener = { + xdgDecorationHandleConfigure, +}; + +// Makes the surface considered as XRGB instead of ARGB. +static void setOpaqueRegion(_GLFWwindow* window) +{ + struct wl_region* region; + + region = wl_compositor_create_region(_glfw.wl.compositor); + if (!region) + return; + + wl_region_add(region, 0, 0, window->wl.width, window->wl.height); + wl_surface_set_opaque_region(window->wl.surface, region); + wl_surface_commit(window->wl.surface); + wl_region_destroy(region); +} + + +static void resizeWindow(_GLFWwindow* window) +{ + int scale = window->wl.scale; + int scaledWidth = window->wl.width * scale; + int scaledHeight = window->wl.height * scale; + wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0); + if (!window->wl.transparent) + setOpaqueRegion(window); + _glfwInputFramebufferSize(window, scaledWidth, scaledHeight); + _glfwInputWindowContentScale(window, scale, scale); + + if (!window->wl.decorations.top.surface) + return; + + // Top decoration. + wp_viewport_set_destination(window->wl.decorations.top.viewport, + window->wl.width, _GLFW_DECORATION_TOP); + wl_surface_commit(window->wl.decorations.top.surface); + + // Left decoration. + wp_viewport_set_destination(window->wl.decorations.left.viewport, + _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP); + wl_surface_commit(window->wl.decorations.left.surface); + + // Right decoration. + wl_subsurface_set_position(window->wl.decorations.right.subsurface, + window->wl.width, -_GLFW_DECORATION_TOP); + wp_viewport_set_destination(window->wl.decorations.right.viewport, + _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP); + wl_surface_commit(window->wl.decorations.right.surface); + + // Bottom decoration. + wl_subsurface_set_position(window->wl.decorations.bottom.subsurface, + -_GLFW_DECORATION_WIDTH, window->wl.height); + wp_viewport_set_destination(window->wl.decorations.bottom.viewport, + window->wl.width + _GLFW_DECORATION_HORIZONTAL, _GLFW_DECORATION_WIDTH); + wl_surface_commit(window->wl.decorations.bottom.surface); +} + +static void checkScaleChange(_GLFWwindow* window) +{ + int scale = 1; + int i; + int monitorScale; + + // Check if we will be able to set the buffer scale or not. + if (_glfw.wl.compositorVersion < 3) + return; + + // Get the scale factor from the highest scale monitor. + for (i = 0; i < window->wl.monitorsCount; ++i) + { + monitorScale = window->wl.monitors[i]->wl.scale; + if (scale < monitorScale) + scale = monitorScale; + } + + // Only change the framebuffer size if the scale changed. + if (scale != window->wl.scale) + { + window->wl.scale = scale; + wl_surface_set_buffer_scale(window->wl.surface, scale); + resizeWindow(window); + } +} + +static void surfaceHandleEnter(void *data, + struct wl_surface *surface, + struct wl_output *output) +{ + _GLFWwindow* window = data; + _GLFWmonitor* monitor = wl_output_get_user_data(output); + + if (window->wl.monitorsCount + 1 > window->wl.monitorsSize) + { + ++window->wl.monitorsSize; + window->wl.monitors = + realloc(window->wl.monitors, + window->wl.monitorsSize * sizeof(_GLFWmonitor*)); + } + + window->wl.monitors[window->wl.monitorsCount++] = monitor; + + checkScaleChange(window); +} + +static void surfaceHandleLeave(void *data, + struct wl_surface *surface, + struct wl_output *output) +{ + _GLFWwindow* window = data; + _GLFWmonitor* monitor = wl_output_get_user_data(output); + GLFWbool found; + int i; + + for (i = 0, found = GLFW_FALSE; i < window->wl.monitorsCount - 1; ++i) + { + if (monitor == window->wl.monitors[i]) + found = GLFW_TRUE; + if (found) + window->wl.monitors[i] = window->wl.monitors[i + 1]; + } + window->wl.monitors[--window->wl.monitorsCount] = NULL; + + checkScaleChange(window); +} + +static const struct wl_surface_listener surfaceListener = { + surfaceHandleEnter, + surfaceHandleLeave +}; + +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, + const _GLFWwndconfig* wndconfig) +{ + window->wl.surface = wl_compositor_create_surface(_glfw.wl.compositor); + if (!window->wl.surface) + return GLFW_FALSE; + + wl_surface_add_listener(window->wl.surface, + &surfaceListener, + window); + + wl_surface_set_user_data(window->wl.surface, window); + + window->wl.native = wl_egl_window_create(window->wl.surface, + wndconfig->width, + wndconfig->height); + if (!window->wl.native) + return GLFW_FALSE; + + window->wl.width = wndconfig->width; + window->wl.height = wndconfig->height; + window->wl.scale = 1; + + if (!window->wl.transparent) + setOpaqueRegion(window); + + return GLFW_TRUE; +} + +static void setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, + int refreshRate) +{ + if (window->wl.xdg.toplevel) + { + xdg_toplevel_set_fullscreen( + window->wl.xdg.toplevel, + monitor->wl.output); + } + setIdleInhibitor(window, GLFW_TRUE); + if (!window->wl.decorations.serverSide) + destroyDecorations(window); +} + +static void xdgToplevelHandleConfigure(void* data, + struct xdg_toplevel* toplevel, + int32_t width, + int32_t height, + struct wl_array* states) +{ + _GLFWwindow* window = data; + float aspectRatio; + float targetRatio; + uint32_t* state; + GLFWbool maximized = GLFW_FALSE; + GLFWbool fullscreen = GLFW_FALSE; + GLFWbool activated = GLFW_FALSE; + + wl_array_for_each(state, states) + { + switch (*state) + { + case XDG_TOPLEVEL_STATE_MAXIMIZED: + maximized = GLFW_TRUE; + break; + case XDG_TOPLEVEL_STATE_FULLSCREEN: + fullscreen = GLFW_TRUE; + break; + case XDG_TOPLEVEL_STATE_RESIZING: + break; + case XDG_TOPLEVEL_STATE_ACTIVATED: + activated = GLFW_TRUE; + break; + } + } + + if (width != 0 && height != 0) + { + if (!maximized && !fullscreen) + { + if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE) + { + aspectRatio = (float)width / (float)height; + targetRatio = (float)window->numer / (float)window->denom; + if (aspectRatio < targetRatio) + height = width / targetRatio; + else if (aspectRatio > targetRatio) + width = height * targetRatio; + } + } + + _glfwInputWindowSize(window, width, height); + _glfwPlatformSetWindowSize(window, width, height); + _glfwInputWindowDamage(window); + } + + if (window->wl.wasFullscreen && window->autoIconify) + { + if (!activated || !fullscreen) + { + _glfwPlatformIconifyWindow(window); + window->wl.wasFullscreen = GLFW_FALSE; + } + } + if (fullscreen && activated) + window->wl.wasFullscreen = GLFW_TRUE; + _glfwInputWindowFocus(window, activated); +} + +static void xdgToplevelHandleClose(void* data, + struct xdg_toplevel* toplevel) +{ + _GLFWwindow* window = data; + _glfwInputWindowCloseRequest(window); +} + +static const struct xdg_toplevel_listener xdgToplevelListener = { + xdgToplevelHandleConfigure, + xdgToplevelHandleClose +}; + +static void xdgSurfaceHandleConfigure(void* data, + struct xdg_surface* surface, + uint32_t serial) +{ + xdg_surface_ack_configure(surface, serial); +} + +static const struct xdg_surface_listener xdgSurfaceListener = { + xdgSurfaceHandleConfigure +}; + +static void setXdgDecorations(_GLFWwindow* window) +{ + if (_glfw.wl.decorationManager) + { + window->wl.xdg.decoration = + zxdg_decoration_manager_v1_get_toplevel_decoration( + _glfw.wl.decorationManager, window->wl.xdg.toplevel); + zxdg_toplevel_decoration_v1_add_listener(window->wl.xdg.decoration, + &xdgDecorationListener, + window); + zxdg_toplevel_decoration_v1_set_mode( + window->wl.xdg.decoration, + ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + } + else + { + window->wl.decorations.serverSide = GLFW_FALSE; + createDecorations(window); + } +} + +static GLFWbool createXdgSurface(_GLFWwindow* window) +{ + window->wl.xdg.surface = xdg_wm_base_get_xdg_surface(_glfw.wl.wmBase, + window->wl.surface); + if (!window->wl.xdg.surface) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: xdg-surface creation failed"); + return GLFW_FALSE; + } + + xdg_surface_add_listener(window->wl.xdg.surface, + &xdgSurfaceListener, + window); + + window->wl.xdg.toplevel = xdg_surface_get_toplevel(window->wl.xdg.surface); + if (!window->wl.xdg.toplevel) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: xdg-toplevel creation failed"); + return GLFW_FALSE; + } + + xdg_toplevel_add_listener(window->wl.xdg.toplevel, + &xdgToplevelListener, + window); + + if (window->wl.title) + xdg_toplevel_set_title(window->wl.xdg.toplevel, window->wl.title); + + if (window->minwidth != GLFW_DONT_CARE && window->minheight != GLFW_DONT_CARE) + xdg_toplevel_set_min_size(window->wl.xdg.toplevel, + window->minwidth, window->minheight); + if (window->maxwidth != GLFW_DONT_CARE && window->maxheight != GLFW_DONT_CARE) + xdg_toplevel_set_max_size(window->wl.xdg.toplevel, + window->maxwidth, window->maxheight); + + if (window->monitor) + { + xdg_toplevel_set_fullscreen(window->wl.xdg.toplevel, + window->monitor->wl.output); + setIdleInhibitor(window, GLFW_TRUE); + } + else if (window->wl.maximized) + { + xdg_toplevel_set_maximized(window->wl.xdg.toplevel); + setIdleInhibitor(window, GLFW_FALSE); + setXdgDecorations(window); + } + else + { + setIdleInhibitor(window, GLFW_FALSE); + setXdgDecorations(window); + } + + wl_surface_commit(window->wl.surface); + wl_display_roundtrip(_glfw.wl.display); + + return GLFW_TRUE; +} + +static void setCursorImage(_GLFWwindow* window, + _GLFWcursorWayland* cursorWayland) +{ + struct itimerspec timer = {}; + struct wl_cursor* wlCursor = cursorWayland->cursor; + struct wl_cursor_image* image; + struct wl_buffer* buffer; + struct wl_surface* surface = _glfw.wl.cursorSurface; + int scale = 1; + + if (!wlCursor) + buffer = cursorWayland->buffer; + else + { + if (window->wl.scale > 1 && cursorWayland->cursorHiDPI) + { + wlCursor = cursorWayland->cursorHiDPI; + scale = 2; + } + + image = wlCursor->images[cursorWayland->currentImage]; + buffer = wl_cursor_image_get_buffer(image); + if (!buffer) + return; + + timer.it_value.tv_sec = image->delay / 1000; + timer.it_value.tv_nsec = (image->delay % 1000) * 1000000; + timerfd_settime(_glfw.wl.cursorTimerfd, 0, &timer, NULL); + + cursorWayland->width = image->width; + cursorWayland->height = image->height; + cursorWayland->xhot = image->hotspot_x; + cursorWayland->yhot = image->hotspot_y; + } + + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, + surface, + cursorWayland->xhot / scale, + cursorWayland->yhot / scale); + wl_surface_set_buffer_scale(surface, scale); + wl_surface_attach(surface, buffer, 0, 0); + wl_surface_damage(surface, 0, 0, + cursorWayland->width, cursorWayland->height); + wl_surface_commit(surface); +} + +static void incrementCursorImage(_GLFWwindow* window) +{ + _GLFWcursor* cursor; + + if (!window || window->wl.decorations.focus != mainWindow) + return; + + cursor = window->wl.currentCursor; + if (cursor && cursor->wl.cursor) + { + cursor->wl.currentImage += 1; + cursor->wl.currentImage %= cursor->wl.cursor->image_count; + setCursorImage(window, &cursor->wl); + } +} + +static void handleEvents(int timeout) +{ + struct wl_display* display = _glfw.wl.display; + struct pollfd fds[] = { + { wl_display_get_fd(display), POLLIN }, + { _glfw.wl.timerfd, POLLIN }, + { _glfw.wl.cursorTimerfd, POLLIN }, + }; + ssize_t read_ret; + uint64_t repeats, i; + + while (wl_display_prepare_read(display) != 0) + wl_display_dispatch_pending(display); + + // If an error different from EAGAIN happens, we have likely been + // disconnected from the Wayland session, try to handle that the best we + // can. + if (wl_display_flush(display) < 0 && errno != EAGAIN) + { + _GLFWwindow* window = _glfw.windowListHead; + while (window) + { + _glfwInputWindowCloseRequest(window); + window = window->next; + } + wl_display_cancel_read(display); + return; + } + + if (poll(fds, 3, timeout) > 0) + { + if (fds[0].revents & POLLIN) + { + wl_display_read_events(display); + wl_display_dispatch_pending(display); + } + else + { + wl_display_cancel_read(display); + } + + if (fds[1].revents & POLLIN) + { + read_ret = read(_glfw.wl.timerfd, &repeats, sizeof(repeats)); + if (read_ret != 8) + return; + + for (i = 0; i < repeats; ++i) + _glfwInputKey(_glfw.wl.keyboardFocus, _glfw.wl.keyboardLastKey, + _glfw.wl.keyboardLastScancode, GLFW_REPEAT, + _glfw.wl.xkb.modifiers); + } + + if (fds[2].revents & POLLIN) + { + read_ret = read(_glfw.wl.cursorTimerfd, &repeats, sizeof(repeats)); + if (read_ret != 8) + return; + + incrementCursorImage(_glfw.wl.pointerFocus); + } + } + else + { + wl_display_cancel_read(display); + } +} + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformCreateWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + window->wl.transparent = fbconfig->transparent; + + if (!createSurface(window, wndconfig)) + return GLFW_FALSE; + + if (ctxconfig->client != GLFW_NO_API) + { + if (ctxconfig->source == GLFW_EGL_CONTEXT_API || + ctxconfig->source == GLFW_NATIVE_CONTEXT_API) + { + if (!_glfwInitEGL()) + return GLFW_FALSE; + if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + } + + if (wndconfig->title) + window->wl.title = _glfw_strdup(wndconfig->title); + + if (wndconfig->visible) + { + if (!createXdgSurface(window)) + return GLFW_FALSE; + + window->wl.visible = GLFW_TRUE; + } + else + { + window->wl.xdg.surface = NULL; + window->wl.xdg.toplevel = NULL; + window->wl.visible = GLFW_FALSE; + } + + window->wl.currentCursor = NULL; + + window->wl.monitors = calloc(1, sizeof(_GLFWmonitor*)); + window->wl.monitorsCount = 0; + window->wl.monitorsSize = 1; + + return GLFW_TRUE; +} + +void _glfwPlatformDestroyWindow(_GLFWwindow* window) +{ + if (window == _glfw.wl.pointerFocus) + { + _glfw.wl.pointerFocus = NULL; + _glfwInputCursorEnter(window, GLFW_FALSE); + } + if (window == _glfw.wl.keyboardFocus) + { + _glfw.wl.keyboardFocus = NULL; + _glfwInputWindowFocus(window, GLFW_FALSE); + } + + if (window->wl.idleInhibitor) + zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor); + + if (window->context.destroy) + window->context.destroy(window); + + destroyDecorations(window); + if (window->wl.xdg.decoration) + zxdg_toplevel_decoration_v1_destroy(window->wl.xdg.decoration); + + if (window->wl.decorations.buffer) + wl_buffer_destroy(window->wl.decorations.buffer); + + if (window->wl.native) + wl_egl_window_destroy(window->wl.native); + + if (window->wl.xdg.toplevel) + xdg_toplevel_destroy(window->wl.xdg.toplevel); + + if (window->wl.xdg.surface) + xdg_surface_destroy(window->wl.xdg.surface); + + if (window->wl.surface) + wl_surface_destroy(window->wl.surface); + + free(window->wl.title); + free(window->wl.monitors); +} + +void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) +{ + if (window->wl.title) + free(window->wl.title); + window->wl.title = _glfw_strdup(title); + if (window->wl.xdg.toplevel) + xdg_toplevel_set_title(window->wl.xdg.toplevel, title); +} + +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, + int count, const GLFWimage* images) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Setting window icon not supported"); +} + +void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) +{ + // A Wayland client is not aware of its position, so just warn and leave it + // as (0, 0) + + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Window position retrieval not supported"); +} + +void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) +{ + // A Wayland client can not set its position, so just warn + + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Window position setting not supported"); +} + +void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) +{ + if (width) + *width = window->wl.width; + if (height) + *height = window->wl.height; +} + +void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) +{ + window->wl.width = width; + window->wl.height = height; + resizeWindow(window); +} + +void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, + int minwidth, int minheight, + int maxwidth, int maxheight) +{ + if (window->wl.xdg.toplevel) + { + if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) + minwidth = minheight = 0; + if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE) + maxwidth = maxheight = 0; + xdg_toplevel_set_min_size(window->wl.xdg.toplevel, minwidth, minheight); + xdg_toplevel_set_max_size(window->wl.xdg.toplevel, maxwidth, maxheight); + wl_surface_commit(window->wl.surface); + } +} + +void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, + int numer, int denom) +{ + // TODO: find out how to trigger a resize. + // The actual limits are checked in the xdg_toplevel::configure handler. +} + +void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, + int* width, int* height) +{ + _glfwPlatformGetWindowSize(window, width, height); + *width *= window->wl.scale; + *height *= window->wl.scale; +} + +void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, + int* left, int* top, + int* right, int* bottom) +{ + if (window->decorated && !window->monitor && !window->wl.decorations.serverSide) + { + if (top) + *top = _GLFW_DECORATION_TOP; + if (left) + *left = _GLFW_DECORATION_WIDTH; + if (right) + *right = _GLFW_DECORATION_WIDTH; + if (bottom) + *bottom = _GLFW_DECORATION_WIDTH; + } +} + +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) +{ + if (window->wl.xdg.toplevel) + xdg_toplevel_set_minimized(window->wl.xdg.toplevel); +} + +void _glfwPlatformRestoreWindow(_GLFWwindow* window) +{ + if (window->wl.xdg.toplevel) + { + if (window->monitor) + xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel); + if (window->wl.maximized) + xdg_toplevel_unset_maximized(window->wl.xdg.toplevel); + // There is no way to unset minimized, or even to know if we are + // minimized, so there is nothing to do in this case. + } + _glfwInputWindowMonitor(window, NULL); + window->wl.maximized = GLFW_FALSE; +} + +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ + if (window->wl.xdg.toplevel) + { + xdg_toplevel_set_maximized(window->wl.xdg.toplevel); + } + window->wl.maximized = GLFW_TRUE; +} + +void _glfwPlatformShowWindow(_GLFWwindow* window) +{ + if (!window->wl.visible) + { + createXdgSurface(window); + window->wl.visible = GLFW_TRUE; + } +} + +void _glfwPlatformHideWindow(_GLFWwindow* window) +{ + if (window->wl.xdg.toplevel) + { + xdg_toplevel_destroy(window->wl.xdg.toplevel); + xdg_surface_destroy(window->wl.xdg.surface); + window->wl.xdg.toplevel = NULL; + window->wl.xdg.surface = NULL; + } + window->wl.visible = GLFW_FALSE; +} + +void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) +{ + // TODO + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Window attention request not implemented yet"); +} + +void _glfwPlatformFocusWindow(_GLFWwindow* window) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Focusing a window requires user interaction"); +} + +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) +{ + if (monitor) + { + setFullscreen(window, monitor, refreshRate); + } + else + { + if (window->wl.xdg.toplevel) + xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel); + setIdleInhibitor(window, GLFW_FALSE); + if (!_glfw.wl.decorationManager) + createDecorations(window); + } + _glfwInputWindowMonitor(window, monitor); +} + +int _glfwPlatformWindowFocused(_GLFWwindow* window) +{ + return _glfw.wl.keyboardFocus == window; +} + +int _glfwPlatformWindowIconified(_GLFWwindow* window) +{ + // xdg-shell doesn’t give any way to request whether a surface is + // iconified. + return GLFW_FALSE; +} + +int _glfwPlatformWindowVisible(_GLFWwindow* window) +{ + return window->wl.visible; +} + +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + return window->wl.maximized; +} + +int _glfwPlatformWindowHovered(_GLFWwindow* window) +{ + return window->wl.hovered; +} + +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + return window->wl.transparent; +} + +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +{ + // TODO + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Window attribute setting not implemented yet"); +} + +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +{ + if (!window->monitor) + { + if (enabled) + createDecorations(window); + else + destroyDecorations(window); + } +} + +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +{ + // TODO + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Window attribute setting not implemented yet"); +} + +float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) +{ + return 1.f; +} + +void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) +{ +} + +void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) +{ + // This is handled in relativePointerHandleRelativeMotion +} + +GLFWbool _glfwPlatformRawMouseMotionSupported(void) +{ + return GLFW_TRUE; +} + +void _glfwPlatformPollEvents(void) +{ + handleEvents(0); +} + +void _glfwPlatformWaitEvents(void) +{ + handleEvents(-1); +} + +void _glfwPlatformWaitEventsTimeout(double timeout) +{ + handleEvents((int) (timeout * 1e3)); +} + +void _glfwPlatformPostEmptyEvent(void) +{ + wl_display_sync(_glfw.wl.display); +} + +void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) +{ + if (xpos) + *xpos = window->wl.cursorPosX; + if (ypos) + *ypos = window->wl.cursorPosY; +} + +static GLFWbool isPointerLocked(_GLFWwindow* window); + +void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) +{ + if (isPointerLocked(window)) + { + zwp_locked_pointer_v1_set_cursor_position_hint( + window->wl.pointerLock.lockedPointer, + wl_fixed_from_double(x), wl_fixed_from_double(y)); + wl_surface_commit(window->wl.surface); + } +} + +void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) +{ + _glfwPlatformSetCursor(window, window->wl.currentCursor); +} + +const char* _glfwPlatformGetScancodeName(int scancode) +{ + // TODO + return NULL; +} + +int _glfwPlatformGetKeyScancode(int key) +{ + return _glfw.wl.scancodes[key]; +} + +int _glfwPlatformCreateCursor(_GLFWcursor* cursor, + const GLFWimage* image, + int xhot, int yhot) +{ + cursor->wl.buffer = createShmBuffer(image); + if (!cursor->wl.buffer) + return GLFW_FALSE; + + cursor->wl.width = image->width; + cursor->wl.height = image->height; + cursor->wl.xhot = xhot; + cursor->wl.yhot = yhot; + return GLFW_TRUE; +} + +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +{ + const char* name = NULL; + + // Try the XDG names first + if (shape == GLFW_ARROW_CURSOR) + name = "default"; + else if (shape == GLFW_IBEAM_CURSOR) + name = "text"; + else if (shape == GLFW_CROSSHAIR_CURSOR) + name = "crosshair"; + else if (shape == GLFW_POINTING_HAND_CURSOR) + name = "pointer"; + else if (shape == GLFW_RESIZE_EW_CURSOR) + name = "ew-resize"; + else if (shape == GLFW_RESIZE_NS_CURSOR) + name = "ns-resize"; + else if (shape == GLFW_RESIZE_NWSE_CURSOR) + name = "nwse-resize"; + else if (shape == GLFW_RESIZE_NESW_CURSOR) + name = "nesw-resize"; + else if (shape == GLFW_RESIZE_ALL_CURSOR) + name = "all-scroll"; + else if (shape == GLFW_NOT_ALLOWED_CURSOR) + name = "not-allowed"; + + cursor->wl.cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, name); + + if (_glfw.wl.cursorThemeHiDPI) + { + cursor->wl.cursorHiDPI = + wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, name); + } + + if (!cursor->wl.cursor) + { + // Fall back to the core X11 names + if (shape == GLFW_ARROW_CURSOR) + name = "left_ptr"; + else if (shape == GLFW_IBEAM_CURSOR) + name = "xterm"; + else if (shape == GLFW_CROSSHAIR_CURSOR) + name = "crosshair"; + else if (shape == GLFW_POINTING_HAND_CURSOR) + name = "hand2"; + else if (shape == GLFW_RESIZE_EW_CURSOR) + name = "sb_h_double_arrow"; + else if (shape == GLFW_RESIZE_NS_CURSOR) + name = "sb_v_double_arrow"; + else if (shape == GLFW_RESIZE_ALL_CURSOR) + name = "fleur"; + else + { + _glfwInputError(GLFW_CURSOR_UNAVAILABLE, + "Wayland: Standard cursor shape unavailable"); + return GLFW_FALSE; + } + + cursor->wl.cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, name); + if (!cursor->wl.cursor) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create standard cursor \"%s\"", + name); + return GLFW_FALSE; + } + + if (_glfw.wl.cursorThemeHiDPI) + { + if (!cursor->wl.cursorHiDPI) + { + cursor->wl.cursorHiDPI = + wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, name); + } + } + } + + return GLFW_TRUE; +} + +void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) +{ + // If it's a standard cursor we don't need to do anything here + if (cursor->wl.cursor) + return; + + if (cursor->wl.buffer) + wl_buffer_destroy(cursor->wl.buffer); +} + +static void relativePointerHandleRelativeMotion(void* data, + struct zwp_relative_pointer_v1* pointer, + uint32_t timeHi, + uint32_t timeLo, + wl_fixed_t dx, + wl_fixed_t dy, + wl_fixed_t dxUnaccel, + wl_fixed_t dyUnaccel) +{ + _GLFWwindow* window = data; + double xpos = window->virtualCursorPosX; + double ypos = window->virtualCursorPosY; + + if (window->cursorMode != GLFW_CURSOR_DISABLED) + return; + + if (window->rawMouseMotion) + { + xpos += wl_fixed_to_double(dxUnaccel); + ypos += wl_fixed_to_double(dyUnaccel); + } + else + { + xpos += wl_fixed_to_double(dx); + ypos += wl_fixed_to_double(dy); + } + + _glfwInputCursorPos(window, xpos, ypos); +} + +static const struct zwp_relative_pointer_v1_listener relativePointerListener = { + relativePointerHandleRelativeMotion +}; + +static void lockedPointerHandleLocked(void* data, + struct zwp_locked_pointer_v1* lockedPointer) +{ +} + +static void unlockPointer(_GLFWwindow* window) +{ + struct zwp_relative_pointer_v1* relativePointer = + window->wl.pointerLock.relativePointer; + struct zwp_locked_pointer_v1* lockedPointer = + window->wl.pointerLock.lockedPointer; + + zwp_relative_pointer_v1_destroy(relativePointer); + zwp_locked_pointer_v1_destroy(lockedPointer); + + window->wl.pointerLock.relativePointer = NULL; + window->wl.pointerLock.lockedPointer = NULL; +} + +static void lockPointer(_GLFWwindow* window); + +static void lockedPointerHandleUnlocked(void* data, + struct zwp_locked_pointer_v1* lockedPointer) +{ +} + +static const struct zwp_locked_pointer_v1_listener lockedPointerListener = { + lockedPointerHandleLocked, + lockedPointerHandleUnlocked +}; + +static void lockPointer(_GLFWwindow* window) +{ + struct zwp_relative_pointer_v1* relativePointer; + struct zwp_locked_pointer_v1* lockedPointer; + + if (!_glfw.wl.relativePointerManager) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: no relative pointer manager"); + return; + } + + relativePointer = + zwp_relative_pointer_manager_v1_get_relative_pointer( + _glfw.wl.relativePointerManager, + _glfw.wl.pointer); + zwp_relative_pointer_v1_add_listener(relativePointer, + &relativePointerListener, + window); + + lockedPointer = + zwp_pointer_constraints_v1_lock_pointer( + _glfw.wl.pointerConstraints, + window->wl.surface, + _glfw.wl.pointer, + NULL, + ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); + zwp_locked_pointer_v1_add_listener(lockedPointer, + &lockedPointerListener, + window); + + window->wl.pointerLock.relativePointer = relativePointer; + window->wl.pointerLock.lockedPointer = lockedPointer; + + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, + NULL, 0, 0); +} + +static GLFWbool isPointerLocked(_GLFWwindow* window) +{ + return window->wl.pointerLock.lockedPointer != NULL; +} + +void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) +{ + struct wl_cursor* defaultCursor; + struct wl_cursor* defaultCursorHiDPI = NULL; + + if (!_glfw.wl.pointer) + return; + + window->wl.currentCursor = cursor; + + // If we're not in the correct window just save the cursor + // the next time the pointer enters the window the cursor will change + if (window != _glfw.wl.pointerFocus || window->wl.decorations.focus != mainWindow) + return; + + // Unlock possible pointer lock if no longer disabled. + if (window->cursorMode != GLFW_CURSOR_DISABLED && isPointerLocked(window)) + unlockPointer(window); + + if (window->cursorMode == GLFW_CURSOR_NORMAL) + { + if (cursor) + setCursorImage(window, &cursor->wl); + else + { + defaultCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, + "left_ptr"); + if (!defaultCursor) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Standard cursor not found"); + return; + } + if (_glfw.wl.cursorThemeHiDPI) + defaultCursorHiDPI = + wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, + "left_ptr"); + _GLFWcursorWayland cursorWayland = { + defaultCursor, + defaultCursorHiDPI, + NULL, + 0, 0, + 0, 0, + 0 + }; + setCursorImage(window, &cursorWayland); + } + } + else if (window->cursorMode == GLFW_CURSOR_DISABLED) + { + if (!isPointerLocked(window)) + lockPointer(window); + } + else if (window->cursorMode == GLFW_CURSOR_HIDDEN) + { + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, NULL, 0, 0); + } +} + +static void dataSourceHandleTarget(void* data, + struct wl_data_source* dataSource, + const char* mimeType) +{ + if (_glfw.wl.dataSource != dataSource) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Unknown clipboard data source"); + return; + } +} + +static void dataSourceHandleSend(void* data, + struct wl_data_source* dataSource, + const char* mimeType, + int fd) +{ + const char* string = _glfw.wl.clipboardSendString; + size_t len = _glfw.wl.clipboardSendSize; + int ret; + + if (_glfw.wl.dataSource != dataSource) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Unknown clipboard data source"); + return; + } + + if (!string) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Copy requested from an invalid string"); + return; + } + + if (strcmp(mimeType, "text/plain;charset=utf-8") != 0) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Wrong MIME type asked from clipboard"); + close(fd); + return; + } + + while (len > 0) + { + ret = write(fd, string, len); + if (ret == -1 && errno == EINTR) + continue; + if (ret == -1) + { + // TODO: also report errno maybe. + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Error while writing the clipboard"); + close(fd); + return; + } + len -= ret; + } + close(fd); +} + +static void dataSourceHandleCancelled(void* data, + struct wl_data_source* dataSource) +{ + wl_data_source_destroy(dataSource); + + if (_glfw.wl.dataSource != dataSource) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Unknown clipboard data source"); + return; + } + + _glfw.wl.dataSource = NULL; +} + +static const struct wl_data_source_listener dataSourceListener = { + dataSourceHandleTarget, + dataSourceHandleSend, + dataSourceHandleCancelled, +}; + +void _glfwPlatformSetClipboardString(const char* string) +{ + if (_glfw.wl.dataSource) + { + wl_data_source_destroy(_glfw.wl.dataSource); + _glfw.wl.dataSource = NULL; + } + + if (_glfw.wl.clipboardSendString) + { + free(_glfw.wl.clipboardSendString); + _glfw.wl.clipboardSendString = NULL; + } + + _glfw.wl.clipboardSendString = strdup(string); + if (!_glfw.wl.clipboardSendString) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Impossible to allocate clipboard string"); + return; + } + _glfw.wl.clipboardSendSize = strlen(string); + _glfw.wl.dataSource = + wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager); + if (!_glfw.wl.dataSource) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Impossible to create clipboard source"); + free(_glfw.wl.clipboardSendString); + return; + } + wl_data_source_add_listener(_glfw.wl.dataSource, + &dataSourceListener, + NULL); + wl_data_source_offer(_glfw.wl.dataSource, "text/plain;charset=utf-8"); + wl_data_device_set_selection(_glfw.wl.dataDevice, + _glfw.wl.dataSource, + _glfw.wl.serial); +} + +static GLFWbool growClipboardString(void) +{ + char* clipboard = _glfw.wl.clipboardString; + + clipboard = realloc(clipboard, _glfw.wl.clipboardSize * 2); + if (!clipboard) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Impossible to grow clipboard string"); + return GLFW_FALSE; + } + _glfw.wl.clipboardString = clipboard; + _glfw.wl.clipboardSize = _glfw.wl.clipboardSize * 2; + return GLFW_TRUE; +} + +const char* _glfwPlatformGetClipboardString(void) +{ + int fds[2]; + int ret; + size_t len = 0; + + if (!_glfw.wl.dataOffer) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "No clipboard data has been sent yet"); + return NULL; + } + + ret = pipe2(fds, O_CLOEXEC); + if (ret < 0) + { + // TODO: also report errno maybe? + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Impossible to create clipboard pipe fds"); + return NULL; + } + + wl_data_offer_receive(_glfw.wl.dataOffer, "text/plain;charset=utf-8", fds[1]); + close(fds[1]); + + // XXX: this is a huge hack, this function shouldn’t be synchronous! + handleEvents(-1); + + while (1) + { + // Grow the clipboard if we need to paste something bigger, there is no + // shrink operation yet. + if (len + 4096 > _glfw.wl.clipboardSize) + { + if (!growClipboardString()) + { + close(fds[0]); + return NULL; + } + } + + // Then read from the fd to the clipboard, handling all known errors. + ret = read(fds[0], _glfw.wl.clipboardString + len, 4096); + if (ret == 0) + break; + if (ret == -1 && errno == EINTR) + continue; + if (ret == -1) + { + // TODO: also report errno maybe. + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Impossible to read from clipboard fd"); + close(fds[0]); + return NULL; + } + len += ret; + } + close(fds[0]); + if (len + 1 > _glfw.wl.clipboardSize) + { + if (!growClipboardString()) + return NULL; + } + _glfw.wl.clipboardString[len] = '\0'; + return _glfw.wl.clipboardString; +} + +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) +{ + if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_wayland_surface) + return; + + extensions[0] = "VK_KHR_surface"; + extensions[1] = "VK_KHR_wayland_surface"; +} + +int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily) +{ + PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR + vkGetPhysicalDeviceWaylandPresentationSupportKHR = + (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR) + vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR"); + if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension"); + return VK_NULL_HANDLE; + } + + return vkGetPhysicalDeviceWaylandPresentationSupportKHR(device, + queuefamily, + _glfw.wl.display); +} + +VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, + _GLFWwindow* window, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) +{ + VkResult err; + VkWaylandSurfaceCreateInfoKHR sci; + PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR; + + vkCreateWaylandSurfaceKHR = (PFN_vkCreateWaylandSurfaceKHR) + vkGetInstanceProcAddr(instance, "vkCreateWaylandSurfaceKHR"); + if (!vkCreateWaylandSurfaceKHR) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + memset(&sci, 0, sizeof(sci)); + sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; + sci.display = _glfw.wl.display; + sci.surface = window->wl.surface; + + err = vkCreateWaylandSurfaceKHR(instance, &sci, allocator, surface); + if (err) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create Vulkan surface: %s", + _glfwGetVulkanResultString(err)); + } + + return err; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI struct wl_display* glfwGetWaylandDisplay(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return _glfw.wl.display; +} + +GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return window->wl.surface; +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/x11_init.c b/source/MaterialXGraphEditor/External/Glfw/src/x11_init.c new file mode 100644 index 0000000000..2b7bc7f101 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/x11_init.c @@ -0,0 +1,1113 @@ +//======================================================================== +// GLFW 3.4 X11 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#include "internal.h" + +#include + +#include +#include +#include +#include +#include +#include + + +// Translate an X11 key code to a GLFW key code. +// +static int translateKeyCode(int scancode) +{ + int keySym; + + // Valid key code range is [8,255], according to the Xlib manual + if (scancode < 8 || scancode > 255) + return GLFW_KEY_UNKNOWN; + + if (_glfw.x11.xkb.available) + { + // Try secondary keysym, for numeric keypad keys + // Note: This way we always force "NumLock = ON", which is intentional + // since the returned key code should correspond to a physical + // location. + keySym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, _glfw.x11.xkb.group, 1); + switch (keySym) + { + case XK_KP_0: return GLFW_KEY_KP_0; + case XK_KP_1: return GLFW_KEY_KP_1; + case XK_KP_2: return GLFW_KEY_KP_2; + case XK_KP_3: return GLFW_KEY_KP_3; + case XK_KP_4: return GLFW_KEY_KP_4; + case XK_KP_5: return GLFW_KEY_KP_5; + case XK_KP_6: return GLFW_KEY_KP_6; + case XK_KP_7: return GLFW_KEY_KP_7; + case XK_KP_8: return GLFW_KEY_KP_8; + case XK_KP_9: return GLFW_KEY_KP_9; + case XK_KP_Separator: + case XK_KP_Decimal: return GLFW_KEY_KP_DECIMAL; + case XK_KP_Equal: return GLFW_KEY_KP_EQUAL; + case XK_KP_Enter: return GLFW_KEY_KP_ENTER; + default: break; + } + + // Now try primary keysym for function keys (non-printable keys) + // These should not depend on the current keyboard layout + keySym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, _glfw.x11.xkb.group, 0); + } + else + { + int dummy; + KeySym* keySyms; + + keySyms = XGetKeyboardMapping(_glfw.x11.display, scancode, 1, &dummy); + keySym = keySyms[0]; + XFree(keySyms); + } + + switch (keySym) + { + case XK_Escape: return GLFW_KEY_ESCAPE; + case XK_Tab: return GLFW_KEY_TAB; + case XK_Shift_L: return GLFW_KEY_LEFT_SHIFT; + case XK_Shift_R: return GLFW_KEY_RIGHT_SHIFT; + case XK_Control_L: return GLFW_KEY_LEFT_CONTROL; + case XK_Control_R: return GLFW_KEY_RIGHT_CONTROL; + case XK_Meta_L: + case XK_Alt_L: return GLFW_KEY_LEFT_ALT; + case XK_Mode_switch: // Mapped to Alt_R on many keyboards + case XK_ISO_Level3_Shift: // AltGr on at least some machines + case XK_Meta_R: + case XK_Alt_R: return GLFW_KEY_RIGHT_ALT; + case XK_Super_L: return GLFW_KEY_LEFT_SUPER; + case XK_Super_R: return GLFW_KEY_RIGHT_SUPER; + case XK_Menu: return GLFW_KEY_MENU; + case XK_Num_Lock: return GLFW_KEY_NUM_LOCK; + case XK_Caps_Lock: return GLFW_KEY_CAPS_LOCK; + case XK_Print: return GLFW_KEY_PRINT_SCREEN; + case XK_Scroll_Lock: return GLFW_KEY_SCROLL_LOCK; + case XK_Pause: return GLFW_KEY_PAUSE; + case XK_Delete: return GLFW_KEY_DELETE; + case XK_BackSpace: return GLFW_KEY_BACKSPACE; + case XK_Return: return GLFW_KEY_ENTER; + case XK_Home: return GLFW_KEY_HOME; + case XK_End: return GLFW_KEY_END; + case XK_Page_Up: return GLFW_KEY_PAGE_UP; + case XK_Page_Down: return GLFW_KEY_PAGE_DOWN; + case XK_Insert: return GLFW_KEY_INSERT; + case XK_Left: return GLFW_KEY_LEFT; + case XK_Right: return GLFW_KEY_RIGHT; + case XK_Down: return GLFW_KEY_DOWN; + case XK_Up: return GLFW_KEY_UP; + case XK_F1: return GLFW_KEY_F1; + case XK_F2: return GLFW_KEY_F2; + case XK_F3: return GLFW_KEY_F3; + case XK_F4: return GLFW_KEY_F4; + case XK_F5: return GLFW_KEY_F5; + case XK_F6: return GLFW_KEY_F6; + case XK_F7: return GLFW_KEY_F7; + case XK_F8: return GLFW_KEY_F8; + case XK_F9: return GLFW_KEY_F9; + case XK_F10: return GLFW_KEY_F10; + case XK_F11: return GLFW_KEY_F11; + case XK_F12: return GLFW_KEY_F12; + case XK_F13: return GLFW_KEY_F13; + case XK_F14: return GLFW_KEY_F14; + case XK_F15: return GLFW_KEY_F15; + case XK_F16: return GLFW_KEY_F16; + case XK_F17: return GLFW_KEY_F17; + case XK_F18: return GLFW_KEY_F18; + case XK_F19: return GLFW_KEY_F19; + case XK_F20: return GLFW_KEY_F20; + case XK_F21: return GLFW_KEY_F21; + case XK_F22: return GLFW_KEY_F22; + case XK_F23: return GLFW_KEY_F23; + case XK_F24: return GLFW_KEY_F24; + case XK_F25: return GLFW_KEY_F25; + + // Numeric keypad + case XK_KP_Divide: return GLFW_KEY_KP_DIVIDE; + case XK_KP_Multiply: return GLFW_KEY_KP_MULTIPLY; + case XK_KP_Subtract: return GLFW_KEY_KP_SUBTRACT; + case XK_KP_Add: return GLFW_KEY_KP_ADD; + + // These should have been detected in secondary keysym test above! + case XK_KP_Insert: return GLFW_KEY_KP_0; + case XK_KP_End: return GLFW_KEY_KP_1; + case XK_KP_Down: return GLFW_KEY_KP_2; + case XK_KP_Page_Down: return GLFW_KEY_KP_3; + case XK_KP_Left: return GLFW_KEY_KP_4; + case XK_KP_Right: return GLFW_KEY_KP_6; + case XK_KP_Home: return GLFW_KEY_KP_7; + case XK_KP_Up: return GLFW_KEY_KP_8; + case XK_KP_Page_Up: return GLFW_KEY_KP_9; + case XK_KP_Delete: return GLFW_KEY_KP_DECIMAL; + case XK_KP_Equal: return GLFW_KEY_KP_EQUAL; + case XK_KP_Enter: return GLFW_KEY_KP_ENTER; + + // Last resort: Check for printable keys (should not happen if the XKB + // extension is available). This will give a layout dependent mapping + // (which is wrong, and we may miss some keys, especially on non-US + // keyboards), but it's better than nothing... + case XK_a: return GLFW_KEY_A; + case XK_b: return GLFW_KEY_B; + case XK_c: return GLFW_KEY_C; + case XK_d: return GLFW_KEY_D; + case XK_e: return GLFW_KEY_E; + case XK_f: return GLFW_KEY_F; + case XK_g: return GLFW_KEY_G; + case XK_h: return GLFW_KEY_H; + case XK_i: return GLFW_KEY_I; + case XK_j: return GLFW_KEY_J; + case XK_k: return GLFW_KEY_K; + case XK_l: return GLFW_KEY_L; + case XK_m: return GLFW_KEY_M; + case XK_n: return GLFW_KEY_N; + case XK_o: return GLFW_KEY_O; + case XK_p: return GLFW_KEY_P; + case XK_q: return GLFW_KEY_Q; + case XK_r: return GLFW_KEY_R; + case XK_s: return GLFW_KEY_S; + case XK_t: return GLFW_KEY_T; + case XK_u: return GLFW_KEY_U; + case XK_v: return GLFW_KEY_V; + case XK_w: return GLFW_KEY_W; + case XK_x: return GLFW_KEY_X; + case XK_y: return GLFW_KEY_Y; + case XK_z: return GLFW_KEY_Z; + case XK_1: return GLFW_KEY_1; + case XK_2: return GLFW_KEY_2; + case XK_3: return GLFW_KEY_3; + case XK_4: return GLFW_KEY_4; + case XK_5: return GLFW_KEY_5; + case XK_6: return GLFW_KEY_6; + case XK_7: return GLFW_KEY_7; + case XK_8: return GLFW_KEY_8; + case XK_9: return GLFW_KEY_9; + case XK_0: return GLFW_KEY_0; + case XK_space: return GLFW_KEY_SPACE; + case XK_minus: return GLFW_KEY_MINUS; + case XK_equal: return GLFW_KEY_EQUAL; + case XK_bracketleft: return GLFW_KEY_LEFT_BRACKET; + case XK_bracketright: return GLFW_KEY_RIGHT_BRACKET; + case XK_backslash: return GLFW_KEY_BACKSLASH; + case XK_semicolon: return GLFW_KEY_SEMICOLON; + case XK_apostrophe: return GLFW_KEY_APOSTROPHE; + case XK_grave: return GLFW_KEY_GRAVE_ACCENT; + case XK_comma: return GLFW_KEY_COMMA; + case XK_period: return GLFW_KEY_PERIOD; + case XK_slash: return GLFW_KEY_SLASH; + case XK_less: return GLFW_KEY_WORLD_1; // At least in some layouts... + default: break; + } + + // No matching translation was found + return GLFW_KEY_UNKNOWN; +} + +// Create key code translation tables +// +static void createKeyTables(void) +{ + int scancode, key; + + memset(_glfw.x11.keycodes, -1, sizeof(_glfw.x11.keycodes)); + memset(_glfw.x11.scancodes, -1, sizeof(_glfw.x11.scancodes)); + + if (_glfw.x11.xkb.available) + { + // Use XKB to determine physical key locations independently of the + // current keyboard layout + + char name[XkbKeyNameLength + 1]; + XkbDescPtr desc = XkbGetMap(_glfw.x11.display, 0, XkbUseCoreKbd); + XkbGetNames(_glfw.x11.display, XkbKeyNamesMask, desc); + + // Find the X11 key code -> GLFW key code mapping + for (scancode = desc->min_key_code; scancode <= desc->max_key_code; scancode++) + { + memcpy(name, desc->names->keys[scancode].name, XkbKeyNameLength); + name[XkbKeyNameLength] = '\0'; + + // Map the key name to a GLFW key code. Note: We only map printable + // keys here, and we use the US keyboard layout. The rest of the + // keys (function keys) are mapped using traditional KeySym + // translations. + if (strcmp(name, "TLDE") == 0) key = GLFW_KEY_GRAVE_ACCENT; + else if (strcmp(name, "AE01") == 0) key = GLFW_KEY_1; + else if (strcmp(name, "AE02") == 0) key = GLFW_KEY_2; + else if (strcmp(name, "AE03") == 0) key = GLFW_KEY_3; + else if (strcmp(name, "AE04") == 0) key = GLFW_KEY_4; + else if (strcmp(name, "AE05") == 0) key = GLFW_KEY_5; + else if (strcmp(name, "AE06") == 0) key = GLFW_KEY_6; + else if (strcmp(name, "AE07") == 0) key = GLFW_KEY_7; + else if (strcmp(name, "AE08") == 0) key = GLFW_KEY_8; + else if (strcmp(name, "AE09") == 0) key = GLFW_KEY_9; + else if (strcmp(name, "AE10") == 0) key = GLFW_KEY_0; + else if (strcmp(name, "AE11") == 0) key = GLFW_KEY_MINUS; + else if (strcmp(name, "AE12") == 0) key = GLFW_KEY_EQUAL; + else if (strcmp(name, "AD01") == 0) key = GLFW_KEY_Q; + else if (strcmp(name, "AD02") == 0) key = GLFW_KEY_W; + else if (strcmp(name, "AD03") == 0) key = GLFW_KEY_E; + else if (strcmp(name, "AD04") == 0) key = GLFW_KEY_R; + else if (strcmp(name, "AD05") == 0) key = GLFW_KEY_T; + else if (strcmp(name, "AD06") == 0) key = GLFW_KEY_Y; + else if (strcmp(name, "AD07") == 0) key = GLFW_KEY_U; + else if (strcmp(name, "AD08") == 0) key = GLFW_KEY_I; + else if (strcmp(name, "AD09") == 0) key = GLFW_KEY_O; + else if (strcmp(name, "AD10") == 0) key = GLFW_KEY_P; + else if (strcmp(name, "AD11") == 0) key = GLFW_KEY_LEFT_BRACKET; + else if (strcmp(name, "AD12") == 0) key = GLFW_KEY_RIGHT_BRACKET; + else if (strcmp(name, "AC01") == 0) key = GLFW_KEY_A; + else if (strcmp(name, "AC02") == 0) key = GLFW_KEY_S; + else if (strcmp(name, "AC03") == 0) key = GLFW_KEY_D; + else if (strcmp(name, "AC04") == 0) key = GLFW_KEY_F; + else if (strcmp(name, "AC05") == 0) key = GLFW_KEY_G; + else if (strcmp(name, "AC06") == 0) key = GLFW_KEY_H; + else if (strcmp(name, "AC07") == 0) key = GLFW_KEY_J; + else if (strcmp(name, "AC08") == 0) key = GLFW_KEY_K; + else if (strcmp(name, "AC09") == 0) key = GLFW_KEY_L; + else if (strcmp(name, "AC10") == 0) key = GLFW_KEY_SEMICOLON; + else if (strcmp(name, "AC11") == 0) key = GLFW_KEY_APOSTROPHE; + else if (strcmp(name, "AB01") == 0) key = GLFW_KEY_Z; + else if (strcmp(name, "AB02") == 0) key = GLFW_KEY_X; + else if (strcmp(name, "AB03") == 0) key = GLFW_KEY_C; + else if (strcmp(name, "AB04") == 0) key = GLFW_KEY_V; + else if (strcmp(name, "AB05") == 0) key = GLFW_KEY_B; + else if (strcmp(name, "AB06") == 0) key = GLFW_KEY_N; + else if (strcmp(name, "AB07") == 0) key = GLFW_KEY_M; + else if (strcmp(name, "AB08") == 0) key = GLFW_KEY_COMMA; + else if (strcmp(name, "AB09") == 0) key = GLFW_KEY_PERIOD; + else if (strcmp(name, "AB10") == 0) key = GLFW_KEY_SLASH; + else if (strcmp(name, "BKSL") == 0) key = GLFW_KEY_BACKSLASH; + else if (strcmp(name, "LSGT") == 0) key = GLFW_KEY_WORLD_1; + else key = GLFW_KEY_UNKNOWN; + + if ((scancode >= 0) && (scancode < 256)) + _glfw.x11.keycodes[scancode] = key; + } + + XkbFreeNames(desc, XkbKeyNamesMask, True); + XkbFreeKeyboard(desc, 0, True); + } + + for (scancode = 0; scancode < 256; scancode++) + { + // Translate the un-translated key codes using traditional X11 KeySym + // lookups + if (_glfw.x11.keycodes[scancode] < 0) + _glfw.x11.keycodes[scancode] = translateKeyCode(scancode); + + // Store the reverse translation for faster key name lookup + if (_glfw.x11.keycodes[scancode] > 0) + _glfw.x11.scancodes[_glfw.x11.keycodes[scancode]] = scancode; + } +} + +// Check whether the IM has a usable style +// +static GLFWbool hasUsableInputMethodStyle(void) +{ + GLFWbool found = GLFW_FALSE; + XIMStyles* styles = NULL; + + if (XGetIMValues(_glfw.x11.im, XNQueryInputStyle, &styles, NULL) != NULL) + return GLFW_FALSE; + + for (unsigned int i = 0; i < styles->count_styles; i++) + { + if (styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing)) + { + found = GLFW_TRUE; + break; + } + } + + XFree(styles); + return found; +} + +// Check whether the specified atom is supported +// +static Atom getSupportedAtom(Atom* supportedAtoms, + unsigned long atomCount, + const char* atomName) +{ + const Atom atom = XInternAtom(_glfw.x11.display, atomName, False); + + for (unsigned int i = 0; i < atomCount; i++) + { + if (supportedAtoms[i] == atom) + return atom; + } + + return None; +} + +// Check whether the running window manager is EWMH-compliant +// +static void detectEWMH(void) +{ + // First we read the _NET_SUPPORTING_WM_CHECK property on the root window + + Window* windowFromRoot = NULL; + if (!_glfwGetWindowPropertyX11(_glfw.x11.root, + _glfw.x11.NET_SUPPORTING_WM_CHECK, + XA_WINDOW, + (unsigned char**) &windowFromRoot)) + { + return; + } + + _glfwGrabErrorHandlerX11(); + + // If it exists, it should be the XID of a top-level window + // Then we look for the same property on that window + + Window* windowFromChild = NULL; + if (!_glfwGetWindowPropertyX11(*windowFromRoot, + _glfw.x11.NET_SUPPORTING_WM_CHECK, + XA_WINDOW, + (unsigned char**) &windowFromChild)) + { + XFree(windowFromRoot); + return; + } + + _glfwReleaseErrorHandlerX11(); + + // If the property exists, it should contain the XID of the window + + if (*windowFromRoot != *windowFromChild) + { + XFree(windowFromRoot); + XFree(windowFromChild); + return; + } + + XFree(windowFromRoot); + XFree(windowFromChild); + + // We are now fairly sure that an EWMH-compliant WM is currently running + // We can now start querying the WM about what features it supports by + // looking in the _NET_SUPPORTED property on the root window + // It should contain a list of supported EWMH protocol and state atoms + + Atom* supportedAtoms = NULL; + const unsigned long atomCount = + _glfwGetWindowPropertyX11(_glfw.x11.root, + _glfw.x11.NET_SUPPORTED, + XA_ATOM, + (unsigned char**) &supportedAtoms); + + // See which of the atoms we support that are supported by the WM + + _glfw.x11.NET_WM_STATE = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE"); + _glfw.x11.NET_WM_STATE_ABOVE = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_ABOVE"); + _glfw.x11.NET_WM_STATE_FULLSCREEN = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_FULLSCREEN"); + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_VERT"); + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_HORZ"); + _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_DEMANDS_ATTENTION"); + _glfw.x11.NET_WM_FULLSCREEN_MONITORS = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_FULLSCREEN_MONITORS"); + _glfw.x11.NET_WM_WINDOW_TYPE = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE"); + _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE_NORMAL"); + _glfw.x11.NET_WORKAREA = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WORKAREA"); + _glfw.x11.NET_CURRENT_DESKTOP = + getSupportedAtom(supportedAtoms, atomCount, "_NET_CURRENT_DESKTOP"); + _glfw.x11.NET_ACTIVE_WINDOW = + getSupportedAtom(supportedAtoms, atomCount, "_NET_ACTIVE_WINDOW"); + _glfw.x11.NET_FRAME_EXTENTS = + getSupportedAtom(supportedAtoms, atomCount, "_NET_FRAME_EXTENTS"); + _glfw.x11.NET_REQUEST_FRAME_EXTENTS = + getSupportedAtom(supportedAtoms, atomCount, "_NET_REQUEST_FRAME_EXTENTS"); + + if (supportedAtoms) + XFree(supportedAtoms); +} + +// Look for and initialize supported X11 extensions +// +static GLFWbool initExtensions(void) +{ + _glfw.x11.vidmode.handle = _glfw_dlopen("libXxf86vm.so.1"); + if (_glfw.x11.vidmode.handle) + { + _glfw.x11.vidmode.QueryExtension = (PFN_XF86VidModeQueryExtension) + _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeQueryExtension"); + _glfw.x11.vidmode.GetGammaRamp = (PFN_XF86VidModeGetGammaRamp) + _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeGetGammaRamp"); + _glfw.x11.vidmode.SetGammaRamp = (PFN_XF86VidModeSetGammaRamp) + _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeSetGammaRamp"); + _glfw.x11.vidmode.GetGammaRampSize = (PFN_XF86VidModeGetGammaRampSize) + _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeGetGammaRampSize"); + + _glfw.x11.vidmode.available = + XF86VidModeQueryExtension(_glfw.x11.display, + &_glfw.x11.vidmode.eventBase, + &_glfw.x11.vidmode.errorBase); + } + +#if defined(__CYGWIN__) + _glfw.x11.xi.handle = _glfw_dlopen("libXi-6.so"); +#else + _glfw.x11.xi.handle = _glfw_dlopen("libXi.so.6"); +#endif + if (_glfw.x11.xi.handle) + { + _glfw.x11.xi.QueryVersion = (PFN_XIQueryVersion) + _glfw_dlsym(_glfw.x11.xi.handle, "XIQueryVersion"); + _glfw.x11.xi.SelectEvents = (PFN_XISelectEvents) + _glfw_dlsym(_glfw.x11.xi.handle, "XISelectEvents"); + + if (XQueryExtension(_glfw.x11.display, + "XInputExtension", + &_glfw.x11.xi.majorOpcode, + &_glfw.x11.xi.eventBase, + &_glfw.x11.xi.errorBase)) + { + _glfw.x11.xi.major = 2; + _glfw.x11.xi.minor = 0; + + if (XIQueryVersion(_glfw.x11.display, + &_glfw.x11.xi.major, + &_glfw.x11.xi.minor) == Success) + { + _glfw.x11.xi.available = GLFW_TRUE; + } + } + } + +#if defined(__CYGWIN__) + _glfw.x11.randr.handle = _glfw_dlopen("libXrandr-2.so"); +#else + _glfw.x11.randr.handle = _glfw_dlopen("libXrandr.so.2"); +#endif + if (_glfw.x11.randr.handle) + { + _glfw.x11.randr.AllocGamma = (PFN_XRRAllocGamma) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRAllocGamma"); + _glfw.x11.randr.FreeGamma = (PFN_XRRFreeGamma) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeGamma"); + _glfw.x11.randr.FreeCrtcInfo = (PFN_XRRFreeCrtcInfo) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeCrtcInfo"); + _glfw.x11.randr.FreeGamma = (PFN_XRRFreeGamma) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeGamma"); + _glfw.x11.randr.FreeOutputInfo = (PFN_XRRFreeOutputInfo) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeOutputInfo"); + _glfw.x11.randr.FreeScreenResources = (PFN_XRRFreeScreenResources) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeScreenResources"); + _glfw.x11.randr.GetCrtcGamma = (PFN_XRRGetCrtcGamma) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetCrtcGamma"); + _glfw.x11.randr.GetCrtcGammaSize = (PFN_XRRGetCrtcGammaSize) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetCrtcGammaSize"); + _glfw.x11.randr.GetCrtcInfo = (PFN_XRRGetCrtcInfo) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetCrtcInfo"); + _glfw.x11.randr.GetOutputInfo = (PFN_XRRGetOutputInfo) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetOutputInfo"); + _glfw.x11.randr.GetOutputPrimary = (PFN_XRRGetOutputPrimary) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetOutputPrimary"); + _glfw.x11.randr.GetScreenResourcesCurrent = (PFN_XRRGetScreenResourcesCurrent) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetScreenResourcesCurrent"); + _glfw.x11.randr.QueryExtension = (PFN_XRRQueryExtension) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRQueryExtension"); + _glfw.x11.randr.QueryVersion = (PFN_XRRQueryVersion) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRQueryVersion"); + _glfw.x11.randr.SelectInput = (PFN_XRRSelectInput) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRSelectInput"); + _glfw.x11.randr.SetCrtcConfig = (PFN_XRRSetCrtcConfig) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRSetCrtcConfig"); + _glfw.x11.randr.SetCrtcGamma = (PFN_XRRSetCrtcGamma) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRSetCrtcGamma"); + _glfw.x11.randr.UpdateConfiguration = (PFN_XRRUpdateConfiguration) + _glfw_dlsym(_glfw.x11.randr.handle, "XRRUpdateConfiguration"); + + if (XRRQueryExtension(_glfw.x11.display, + &_glfw.x11.randr.eventBase, + &_glfw.x11.randr.errorBase)) + { + if (XRRQueryVersion(_glfw.x11.display, + &_glfw.x11.randr.major, + &_glfw.x11.randr.minor)) + { + // The GLFW RandR path requires at least version 1.3 + if (_glfw.x11.randr.major > 1 || _glfw.x11.randr.minor >= 3) + _glfw.x11.randr.available = GLFW_TRUE; + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to query RandR version"); + } + } + } + + if (_glfw.x11.randr.available) + { + XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, + _glfw.x11.root); + + if (!sr->ncrtc || !XRRGetCrtcGammaSize(_glfw.x11.display, sr->crtcs[0])) + { + // This is likely an older Nvidia driver with broken gamma support + // Flag it as useless and fall back to xf86vm gamma, if available + _glfw.x11.randr.gammaBroken = GLFW_TRUE; + } + + if (!sr->ncrtc) + { + // A system without CRTCs is likely a system with broken RandR + // Disable the RandR monitor path and fall back to core functions + _glfw.x11.randr.monitorBroken = GLFW_TRUE; + } + + XRRFreeScreenResources(sr); + } + + if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) + { + XRRSelectInput(_glfw.x11.display, _glfw.x11.root, + RROutputChangeNotifyMask); + } + +#if defined(__CYGWIN__) + _glfw.x11.xcursor.handle = _glfw_dlopen("libXcursor-1.so"); +#else + _glfw.x11.xcursor.handle = _glfw_dlopen("libXcursor.so.1"); +#endif + if (_glfw.x11.xcursor.handle) + { + _glfw.x11.xcursor.ImageCreate = (PFN_XcursorImageCreate) + _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageCreate"); + _glfw.x11.xcursor.ImageDestroy = (PFN_XcursorImageDestroy) + _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageDestroy"); + _glfw.x11.xcursor.ImageLoadCursor = (PFN_XcursorImageLoadCursor) + _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageLoadCursor"); + _glfw.x11.xcursor.GetTheme = (PFN_XcursorGetTheme) + _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorGetTheme"); + _glfw.x11.xcursor.GetDefaultSize = (PFN_XcursorGetDefaultSize) + _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorGetDefaultSize"); + _glfw.x11.xcursor.LibraryLoadImage = (PFN_XcursorLibraryLoadImage) + _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorLibraryLoadImage"); + } + +#if defined(__CYGWIN__) + _glfw.x11.xinerama.handle = _glfw_dlopen("libXinerama-1.so"); +#else + _glfw.x11.xinerama.handle = _glfw_dlopen("libXinerama.so.1"); +#endif + if (_glfw.x11.xinerama.handle) + { + _glfw.x11.xinerama.IsActive = (PFN_XineramaIsActive) + _glfw_dlsym(_glfw.x11.xinerama.handle, "XineramaIsActive"); + _glfw.x11.xinerama.QueryExtension = (PFN_XineramaQueryExtension) + _glfw_dlsym(_glfw.x11.xinerama.handle, "XineramaQueryExtension"); + _glfw.x11.xinerama.QueryScreens = (PFN_XineramaQueryScreens) + _glfw_dlsym(_glfw.x11.xinerama.handle, "XineramaQueryScreens"); + + if (XineramaQueryExtension(_glfw.x11.display, + &_glfw.x11.xinerama.major, + &_glfw.x11.xinerama.minor)) + { + if (XineramaIsActive(_glfw.x11.display)) + _glfw.x11.xinerama.available = GLFW_TRUE; + } + } + + _glfw.x11.xkb.major = 1; + _glfw.x11.xkb.minor = 0; + _glfw.x11.xkb.available = + XkbQueryExtension(_glfw.x11.display, + &_glfw.x11.xkb.majorOpcode, + &_glfw.x11.xkb.eventBase, + &_glfw.x11.xkb.errorBase, + &_glfw.x11.xkb.major, + &_glfw.x11.xkb.minor); + + if (_glfw.x11.xkb.available) + { + Bool supported; + + if (XkbSetDetectableAutoRepeat(_glfw.x11.display, True, &supported)) + { + if (supported) + _glfw.x11.xkb.detectable = GLFW_TRUE; + } + + _glfw.x11.xkb.group = 0; + XkbStateRec state; + if (XkbGetState(_glfw.x11.display, XkbUseCoreKbd, &state) == Success) + { + XkbSelectEventDetails(_glfw.x11.display, XkbUseCoreKbd, XkbStateNotify, XkbAllStateComponentsMask, XkbGroupStateMask); + _glfw.x11.xkb.group = (unsigned int)state.group; + } + } + +#if defined(__CYGWIN__) + _glfw.x11.x11xcb.handle = _glfw_dlopen("libX11-xcb-1.so"); +#else + _glfw.x11.x11xcb.handle = _glfw_dlopen("libX11-xcb.so.1"); +#endif + if (_glfw.x11.x11xcb.handle) + { + _glfw.x11.x11xcb.GetXCBConnection = (PFN_XGetXCBConnection) + _glfw_dlsym(_glfw.x11.x11xcb.handle, "XGetXCBConnection"); + } + +#if defined(__CYGWIN__) + _glfw.x11.xrender.handle = _glfw_dlopen("libXrender-1.so"); +#else + _glfw.x11.xrender.handle = _glfw_dlopen("libXrender.so.1"); +#endif + if (_glfw.x11.xrender.handle) + { + _glfw.x11.xrender.QueryExtension = (PFN_XRenderQueryExtension) + _glfw_dlsym(_glfw.x11.xrender.handle, "XRenderQueryExtension"); + _glfw.x11.xrender.QueryVersion = (PFN_XRenderQueryVersion) + _glfw_dlsym(_glfw.x11.xrender.handle, "XRenderQueryVersion"); + _glfw.x11.xrender.FindVisualFormat = (PFN_XRenderFindVisualFormat) + _glfw_dlsym(_glfw.x11.xrender.handle, "XRenderFindVisualFormat"); + + if (XRenderQueryExtension(_glfw.x11.display, + &_glfw.x11.xrender.errorBase, + &_glfw.x11.xrender.eventBase)) + { + if (XRenderQueryVersion(_glfw.x11.display, + &_glfw.x11.xrender.major, + &_glfw.x11.xrender.minor)) + { + _glfw.x11.xrender.available = GLFW_TRUE; + } + } + } + + // Update the key code LUT + // FIXME: We should listen to XkbMapNotify events to track changes to + // the keyboard mapping. + createKeyTables(); + + // String format atoms + _glfw.x11.NULL_ = XInternAtom(_glfw.x11.display, "NULL", False); + _glfw.x11.UTF8_STRING = XInternAtom(_glfw.x11.display, "UTF8_STRING", False); + _glfw.x11.ATOM_PAIR = XInternAtom(_glfw.x11.display, "ATOM_PAIR", False); + + // Custom selection property atom + _glfw.x11.GLFW_SELECTION = + XInternAtom(_glfw.x11.display, "GLFW_SELECTION", False); + + // ICCCM standard clipboard atoms + _glfw.x11.TARGETS = XInternAtom(_glfw.x11.display, "TARGETS", False); + _glfw.x11.MULTIPLE = XInternAtom(_glfw.x11.display, "MULTIPLE", False); + _glfw.x11.PRIMARY = XInternAtom(_glfw.x11.display, "PRIMARY", False); + _glfw.x11.INCR = XInternAtom(_glfw.x11.display, "INCR", False); + _glfw.x11.CLIPBOARD = XInternAtom(_glfw.x11.display, "CLIPBOARD", False); + + // Clipboard manager atoms + _glfw.x11.CLIPBOARD_MANAGER = + XInternAtom(_glfw.x11.display, "CLIPBOARD_MANAGER", False); + _glfw.x11.SAVE_TARGETS = + XInternAtom(_glfw.x11.display, "SAVE_TARGETS", False); + + // Xdnd (drag and drop) atoms + _glfw.x11.XdndAware = XInternAtom(_glfw.x11.display, "XdndAware", False); + _glfw.x11.XdndEnter = XInternAtom(_glfw.x11.display, "XdndEnter", False); + _glfw.x11.XdndPosition = XInternAtom(_glfw.x11.display, "XdndPosition", False); + _glfw.x11.XdndStatus = XInternAtom(_glfw.x11.display, "XdndStatus", False); + _glfw.x11.XdndActionCopy = XInternAtom(_glfw.x11.display, "XdndActionCopy", False); + _glfw.x11.XdndDrop = XInternAtom(_glfw.x11.display, "XdndDrop", False); + _glfw.x11.XdndFinished = XInternAtom(_glfw.x11.display, "XdndFinished", 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 + // These can be set safely even without WM support + // The EWMH atoms that require WM support are handled in detectEWMH + _glfw.x11.WM_PROTOCOLS = + XInternAtom(_glfw.x11.display, "WM_PROTOCOLS", False); + _glfw.x11.WM_STATE = + XInternAtom(_glfw.x11.display, "WM_STATE", False); + _glfw.x11.WM_DELETE_WINDOW = + XInternAtom(_glfw.x11.display, "WM_DELETE_WINDOW", False); + _glfw.x11.NET_SUPPORTED = + XInternAtom(_glfw.x11.display, "_NET_SUPPORTED", False); + _glfw.x11.NET_SUPPORTING_WM_CHECK = + XInternAtom(_glfw.x11.display, "_NET_SUPPORTING_WM_CHECK", False); + _glfw.x11.NET_WM_ICON = + XInternAtom(_glfw.x11.display, "_NET_WM_ICON", False); + _glfw.x11.NET_WM_PING = + XInternAtom(_glfw.x11.display, "_NET_WM_PING", False); + _glfw.x11.NET_WM_PID = + XInternAtom(_glfw.x11.display, "_NET_WM_PID", False); + _glfw.x11.NET_WM_NAME = + XInternAtom(_glfw.x11.display, "_NET_WM_NAME", False); + _glfw.x11.NET_WM_ICON_NAME = + XInternAtom(_glfw.x11.display, "_NET_WM_ICON_NAME", False); + _glfw.x11.NET_WM_BYPASS_COMPOSITOR = + XInternAtom(_glfw.x11.display, "_NET_WM_BYPASS_COMPOSITOR", False); + _glfw.x11.NET_WM_WINDOW_OPACITY = + XInternAtom(_glfw.x11.display, "_NET_WM_WINDOW_OPACITY", False); + _glfw.x11.MOTIF_WM_HINTS = + 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); + } + + // Detect whether an EWMH-conformant window manager is running + detectEWMH(); + + return GLFW_TRUE; +} + +// Retrieve system content scale via folklore heuristics +// +static void getSystemContentScale(float* xscale, float* yscale) +{ + // Start by assuming the default X11 DPI + // NOTE: Some desktop environments (KDE) may remove the Xft.dpi field when it + // would be set to 96, so assume that is the case if we cannot find it + float xdpi = 96.f, ydpi = 96.f; + + // 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 +// +static Cursor createHiddenCursor(void) +{ + unsigned char pixels[16 * 16 * 4] = { 0 }; + GLFWimage image = { 16, 16, pixels }; + return _glfwCreateCursorX11(&image, 0, 0); +} + +// Create a helper window for IPC +// +static Window createHelperWindow(void) +{ + XSetWindowAttributes wa; + wa.event_mask = PropertyChangeMask; + + return XCreateWindow(_glfw.x11.display, _glfw.x11.root, + 0, 0, 1, 1, 0, 0, + InputOnly, + DefaultVisual(_glfw.x11.display, _glfw.x11.screen), + CWEventMask, &wa); +} + +// X error handler +// +static int errorHandler(Display *display, XErrorEvent* event) +{ + _glfw.x11.errorCode = event->error_code; + return 0; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Sets the X error handler callback +// +void _glfwGrabErrorHandlerX11(void) +{ + _glfw.x11.errorCode = Success; + XSetErrorHandler(errorHandler); +} + +// Clears the X error handler callback +// +void _glfwReleaseErrorHandlerX11(void) +{ + // Synchronize to make sure all commands are processed + XSync(_glfw.x11.display, False); + XSetErrorHandler(NULL); +} + +// Reports the specified error, appending information about the last X error +// +void _glfwInputErrorX11(int error, const char* message) +{ + char buffer[_GLFW_MESSAGE_SIZE]; + XGetErrorText(_glfw.x11.display, _glfw.x11.errorCode, + buffer, sizeof(buffer)); + + _glfwInputError(error, "%s: %s", message, buffer); +} + +// Creates a native cursor object from the specified image and hotspot +// +Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot) +{ + int i; + Cursor cursor; + + if (!_glfw.x11.xcursor.handle) + return None; + + XcursorImage* native = XcursorImageCreate(image->width, image->height); + if (native == NULL) + return None; + + native->xhot = xhot; + native->yhot = yhot; + + unsigned char* source = (unsigned char*) image->pixels; + XcursorPixel* target = native->pixels; + + for (i = 0; i < image->width * image->height; i++, target++, source += 4) + { + unsigned int alpha = source[3]; + + *target = (alpha << 24) | + ((unsigned char) ((source[0] * alpha) / 255) << 16) | + ((unsigned char) ((source[1] * alpha) / 255) << 8) | + ((unsigned char) ((source[2] * alpha) / 255) << 0); + } + + cursor = XcursorImageLoadCursor(_glfw.x11.display, native); + XcursorImageDestroy(native); + + return cursor; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformInit(void) +{ +#if !defined(X_HAVE_UTF8_STRING) + // HACK: If the current locale is "C" and the Xlib UTF-8 functions are + // unavailable, apply the environment's locale in the hope that it's + // both available and not "C" + // This is done because the "C" locale breaks wide character input, + // which is what we fall back on when UTF-8 support is missing + if (strcmp(setlocale(LC_CTYPE, NULL), "C") == 0) + setlocale(LC_CTYPE, ""); +#endif + + XInitThreads(); + XrmInitialize(); + + _glfw.x11.display = XOpenDisplay(NULL); + if (!_glfw.x11.display) + { + const char* display = getenv("DISPLAY"); + if (display) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to open display %s", display); + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: The DISPLAY environment variable is missing"); + } + + return GLFW_FALSE; + } + + _glfw.x11.screen = DefaultScreen(_glfw.x11.display); + _glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen); + _glfw.x11.context = XUniqueContext(); + + getSystemContentScale(&_glfw.x11.contentScaleX, &_glfw.x11.contentScaleY); + + if (!initExtensions()) + return GLFW_FALSE; + + _glfw.x11.helperWindowHandle = createHelperWindow(); + _glfw.x11.hiddenCursorHandle = createHiddenCursor(); + + if (XSupportsLocale()) + { + XSetLocaleModifiers(""); + + _glfw.x11.im = XOpenIM(_glfw.x11.display, 0, NULL, NULL); + if (_glfw.x11.im) + { + if (!hasUsableInputMethodStyle()) + { + XCloseIM(_glfw.x11.im); + _glfw.x11.im = NULL; + } + } + } + +#if defined(__linux__) + if (!_glfwInitJoysticksLinux()) + return GLFW_FALSE; +#endif + + _glfwInitTimerPOSIX(); + + _glfwPollMonitorsX11(); + return GLFW_TRUE; +} + +void _glfwPlatformTerminate(void) +{ + if (_glfw.x11.helperWindowHandle) + { + if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) == + _glfw.x11.helperWindowHandle) + { + _glfwPushSelectionToManagerX11(); + } + + XDestroyWindow(_glfw.x11.display, _glfw.x11.helperWindowHandle); + _glfw.x11.helperWindowHandle = None; + } + + if (_glfw.x11.hiddenCursorHandle) + { + XFreeCursor(_glfw.x11.display, _glfw.x11.hiddenCursorHandle); + _glfw.x11.hiddenCursorHandle = (Cursor) 0; + } + + free(_glfw.x11.primarySelectionString); + free(_glfw.x11.clipboardString); + + if (_glfw.x11.im) + { + XCloseIM(_glfw.x11.im); + _glfw.x11.im = NULL; + } + + if (_glfw.x11.display) + { + XCloseDisplay(_glfw.x11.display); + _glfw.x11.display = NULL; + } + + if (_glfw.x11.x11xcb.handle) + { + _glfw_dlclose(_glfw.x11.x11xcb.handle); + _glfw.x11.x11xcb.handle = NULL; + } + + if (_glfw.x11.xcursor.handle) + { + _glfw_dlclose(_glfw.x11.xcursor.handle); + _glfw.x11.xcursor.handle = NULL; + } + + if (_glfw.x11.randr.handle) + { + _glfw_dlclose(_glfw.x11.randr.handle); + _glfw.x11.randr.handle = NULL; + } + + if (_glfw.x11.xinerama.handle) + { + _glfw_dlclose(_glfw.x11.xinerama.handle); + _glfw.x11.xinerama.handle = NULL; + } + + if (_glfw.x11.xrender.handle) + { + _glfw_dlclose(_glfw.x11.xrender.handle); + _glfw.x11.xrender.handle = NULL; + } + + if (_glfw.x11.vidmode.handle) + { + _glfw_dlclose(_glfw.x11.vidmode.handle); + _glfw.x11.vidmode.handle = NULL; + } + + if (_glfw.x11.xi.handle) + { + _glfw_dlclose(_glfw.x11.xi.handle); + _glfw.x11.xi.handle = NULL; + } + + // NOTE: These need to be unloaded after XCloseDisplay, as they register + // cleanup callbacks that get called by that function + _glfwTerminateEGL(); + _glfwTerminateGLX(); + +#if defined(__linux__) + _glfwTerminateJoysticksLinux(); +#endif +} + +const char* _glfwPlatformGetVersionString(void) +{ + return _GLFW_VERSION_NUMBER " X11 GLX EGL OSMesa" +#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) + " clock_gettime" +#else + " gettimeofday" +#endif +#if defined(__linux__) + " evdev" +#endif +#if defined(_GLFW_BUILD_DLL) + " shared" +#endif + ; +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/x11_monitor.c b/source/MaterialXGraphEditor/External/Glfw/src/x11_monitor.c new file mode 100644 index 0000000000..809b93e483 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/x11_monitor.c @@ -0,0 +1,614 @@ +//======================================================================== +// GLFW 3.4 X11 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#include "internal.h" + +#include +#include +#include +#include + + +// Check whether the display mode should be included in enumeration +// +static GLFWbool modeIsGood(const XRRModeInfo* mi) +{ + return (mi->modeFlags & RR_Interlace) == 0; +} + +// Calculates the refresh rate, in Hz, from the specified RandR mode info +// +static int calculateRefreshRate(const XRRModeInfo* mi) +{ + if (mi->hTotal && mi->vTotal) + return (int) round((double) mi->dotClock / ((double) mi->hTotal * (double) mi->vTotal)); + else + return 0; +} + +// Returns the mode info for a RandR mode XID +// +static const XRRModeInfo* getModeInfo(const XRRScreenResources* sr, RRMode id) +{ + for (int i = 0; i < sr->nmode; i++) + { + if (sr->modes[i].id == id) + return sr->modes + i; + } + + return NULL; +} + +// Convert RandR mode info to GLFW video mode +// +static GLFWvidmode vidmodeFromModeInfo(const XRRModeInfo* mi, + const XRRCrtcInfo* ci) +{ + GLFWvidmode mode; + + if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) + { + mode.width = mi->height; + mode.height = mi->width; + } + else + { + mode.width = mi->width; + mode.height = mi->height; + } + + mode.refreshRate = calculateRefreshRate(mi); + + _glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen), + &mode.redBits, &mode.greenBits, &mode.blueBits); + + return mode; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Poll for changes in the set of connected monitors +// +void _glfwPollMonitorsX11(void) +{ + if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) + { + int disconnectedCount, screenCount = 0; + _GLFWmonitor** disconnected = NULL; + XineramaScreenInfo* screens = NULL; + XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, + _glfw.x11.root); + RROutput primary = XRRGetOutputPrimary(_glfw.x11.display, + _glfw.x11.root); + + if (_glfw.x11.xinerama.available) + screens = XineramaQueryScreens(_glfw.x11.display, &screenCount); + + disconnectedCount = _glfw.monitorCount; + if (disconnectedCount) + { + disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); + memcpy(disconnected, + _glfw.monitors, + _glfw.monitorCount * sizeof(_GLFWmonitor*)); + } + + for (int i = 0; i < sr->noutput; i++) + { + int j, type, widthMM, heightMM; + + XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, sr->outputs[i]); + if (oi->connection != RR_Connected || oi->crtc == None) + { + XRRFreeOutputInfo(oi); + continue; + } + + for (j = 0; j < disconnectedCount; j++) + { + if (disconnected[j] && + disconnected[j]->x11.output == sr->outputs[i]) + { + disconnected[j] = NULL; + break; + } + } + + if (j < disconnectedCount) + { + XRRFreeOutputInfo(oi); + continue; + } + + XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, oi->crtc); + if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) + { + widthMM = oi->mm_height; + heightMM = oi->mm_width; + } + else + { + widthMM = oi->mm_width; + heightMM = oi->mm_height; + } + + if (widthMM <= 0 || heightMM <= 0) + { + // HACK: If RandR does not provide a physical size, assume the + // X11 default 96 DPI and calcuate from the CRTC viewport + // NOTE: These members are affected by rotation, unlike the mode + // info and output info members + widthMM = (int) (ci->width * 25.4f / 96.f); + heightMM = (int) (ci->height * 25.4f / 96.f); + } + + _GLFWmonitor* monitor = _glfwAllocMonitor(oi->name, widthMM, heightMM); + monitor->x11.output = sr->outputs[i]; + monitor->x11.crtc = oi->crtc; + + for (j = 0; j < screenCount; j++) + { + if (screens[j].x_org == ci->x && + screens[j].y_org == ci->y && + screens[j].width == ci->width && + screens[j].height == ci->height) + { + monitor->x11.index = j; + break; + } + } + + if (monitor->x11.output == primary) + type = _GLFW_INSERT_FIRST; + else + type = _GLFW_INSERT_LAST; + + _glfwInputMonitor(monitor, GLFW_CONNECTED, type); + + XRRFreeOutputInfo(oi); + XRRFreeCrtcInfo(ci); + } + + XRRFreeScreenResources(sr); + + if (screens) + XFree(screens); + + for (int i = 0; i < disconnectedCount; i++) + { + if (disconnected[i]) + _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); + } + + free(disconnected); + } + else + { + const int widthMM = DisplayWidthMM(_glfw.x11.display, _glfw.x11.screen); + const int heightMM = DisplayHeightMM(_glfw.x11.display, _glfw.x11.screen); + + _glfwInputMonitor(_glfwAllocMonitor("Display", widthMM, heightMM), + GLFW_CONNECTED, + _GLFW_INSERT_FIRST); + } +} + +// Set the current video mode for the specified monitor +// +void _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired) +{ + if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) + { + GLFWvidmode current; + RRMode native = None; + + const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired); + _glfwPlatformGetVideoMode(monitor, ¤t); + if (_glfwCompareVideoModes(¤t, best) == 0) + return; + + XRRScreenResources* sr = + XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); + XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); + XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output); + + for (int i = 0; i < oi->nmode; i++) + { + const XRRModeInfo* mi = getModeInfo(sr, oi->modes[i]); + if (!modeIsGood(mi)) + continue; + + const GLFWvidmode mode = vidmodeFromModeInfo(mi, ci); + if (_glfwCompareVideoModes(best, &mode) == 0) + { + native = mi->id; + break; + } + } + + if (native) + { + if (monitor->x11.oldMode == None) + monitor->x11.oldMode = ci->mode; + + XRRSetCrtcConfig(_glfw.x11.display, + sr, monitor->x11.crtc, + CurrentTime, + ci->x, ci->y, + native, + ci->rotation, + ci->outputs, + ci->noutput); + } + + XRRFreeOutputInfo(oi); + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(sr); + } +} + +// Restore the saved (original) video mode for the specified monitor +// +void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor) +{ + if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) + { + if (monitor->x11.oldMode == None) + return; + + XRRScreenResources* sr = + XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); + XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); + + XRRSetCrtcConfig(_glfw.x11.display, + sr, monitor->x11.crtc, + CurrentTime, + ci->x, ci->y, + monitor->x11.oldMode, + ci->rotation, + ci->outputs, + ci->noutput); + + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(sr); + + monitor->x11.oldMode = None; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) +{ +} + +void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) +{ + if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) + { + XRRScreenResources* sr = + XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); + XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); + + if (ci) + { + if (xpos) + *xpos = ci->x; + if (ypos) + *ypos = ci->y; + + XRRFreeCrtcInfo(ci); + } + + XRRFreeScreenResources(sr); + } +} + +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = _glfw.x11.contentScaleX; + if (yscale) + *yscale = _glfw.x11.contentScaleY; +} + +void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height) +{ + int areaX = 0, areaY = 0, areaWidth = 0, areaHeight = 0; + + if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) + { + XRRScreenResources* sr = + XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); + XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); + + areaX = ci->x; + areaY = ci->y; + + const XRRModeInfo* mi = getModeInfo(sr, ci->mode); + + if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) + { + areaWidth = mi->height; + areaHeight = mi->width; + } + else + { + areaWidth = mi->width; + areaHeight = mi->height; + } + + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(sr); + } + else + { + areaWidth = DisplayWidth(_glfw.x11.display, _glfw.x11.screen); + areaHeight = DisplayHeight(_glfw.x11.display, _glfw.x11.screen); + } + + if (_glfw.x11.NET_WORKAREA && _glfw.x11.NET_CURRENT_DESKTOP) + { + Atom* extents = NULL; + Atom* desktop = NULL; + const unsigned long extentCount = + _glfwGetWindowPropertyX11(_glfw.x11.root, + _glfw.x11.NET_WORKAREA, + XA_CARDINAL, + (unsigned char**) &extents); + + if (_glfwGetWindowPropertyX11(_glfw.x11.root, + _glfw.x11.NET_CURRENT_DESKTOP, + XA_CARDINAL, + (unsigned char**) &desktop) > 0) + { + if (extentCount >= 4 && *desktop < extentCount / 4) + { + const int globalX = extents[*desktop * 4 + 0]; + const int globalY = extents[*desktop * 4 + 1]; + const int globalWidth = extents[*desktop * 4 + 2]; + const int globalHeight = extents[*desktop * 4 + 3]; + + if (areaX < globalX) + { + areaWidth -= globalX - areaX; + areaX = globalX; + } + + if (areaY < globalY) + { + areaHeight -= globalY - areaY; + areaY = globalY; + } + + if (areaX + areaWidth > globalX + globalWidth) + areaWidth = globalX - areaX + globalWidth; + if (areaY + areaHeight > globalY + globalHeight) + areaHeight = globalY - areaY + globalHeight; + } + } + + if (extents) + XFree(extents); + if (desktop) + XFree(desktop); + } + + if (xpos) + *xpos = areaX; + if (ypos) + *ypos = areaY; + if (width) + *width = areaWidth; + if (height) + *height = areaHeight; +} + +GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) +{ + GLFWvidmode* result; + + *count = 0; + + if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) + { + XRRScreenResources* sr = + XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); + XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); + XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output); + + result = calloc(oi->nmode, sizeof(GLFWvidmode)); + + for (int i = 0; i < oi->nmode; i++) + { + const XRRModeInfo* mi = getModeInfo(sr, oi->modes[i]); + if (!modeIsGood(mi)) + continue; + + const GLFWvidmode mode = vidmodeFromModeInfo(mi, ci); + int j; + + for (j = 0; j < *count; j++) + { + if (_glfwCompareVideoModes(result + j, &mode) == 0) + break; + } + + // Skip duplicate modes + if (j < *count) + continue; + + (*count)++; + result[*count - 1] = mode; + } + + XRRFreeOutputInfo(oi); + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(sr); + } + else + { + *count = 1; + result = calloc(1, sizeof(GLFWvidmode)); + _glfwPlatformGetVideoMode(monitor, result); + } + + return result; +} + +void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) +{ + if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) + { + XRRScreenResources* sr = + XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); + XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); + + if (ci) + { + const XRRModeInfo* mi = getModeInfo(sr, ci->mode); + if (mi) // mi can be NULL if the monitor has been disconnected + *mode = vidmodeFromModeInfo(mi, ci); + + XRRFreeCrtcInfo(ci); + } + + XRRFreeScreenResources(sr); + } + else + { + mode->width = DisplayWidth(_glfw.x11.display, _glfw.x11.screen); + mode->height = DisplayHeight(_glfw.x11.display, _glfw.x11.screen); + mode->refreshRate = 0; + + _glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen), + &mode->redBits, &mode->greenBits, &mode->blueBits); + } +} + +GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +{ + if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken) + { + const size_t size = XRRGetCrtcGammaSize(_glfw.x11.display, + monitor->x11.crtc); + XRRCrtcGamma* gamma = XRRGetCrtcGamma(_glfw.x11.display, + monitor->x11.crtc); + + _glfwAllocGammaArrays(ramp, size); + + memcpy(ramp->red, gamma->red, size * sizeof(unsigned short)); + memcpy(ramp->green, gamma->green, size * sizeof(unsigned short)); + memcpy(ramp->blue, gamma->blue, size * sizeof(unsigned short)); + + XRRFreeGamma(gamma); + return GLFW_TRUE; + } + else if (_glfw.x11.vidmode.available) + { + int size; + XF86VidModeGetGammaRampSize(_glfw.x11.display, _glfw.x11.screen, &size); + + _glfwAllocGammaArrays(ramp, size); + + XF86VidModeGetGammaRamp(_glfw.x11.display, + _glfw.x11.screen, + ramp->size, ramp->red, ramp->green, ramp->blue); + return GLFW_TRUE; + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Gamma ramp access not supported by server"); + return GLFW_FALSE; + } +} + +void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) +{ + 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); + + memcpy(gamma->red, ramp->red, ramp->size * sizeof(unsigned short)); + memcpy(gamma->green, ramp->green, ramp->size * sizeof(unsigned short)); + memcpy(gamma->blue, ramp->blue, ramp->size * sizeof(unsigned short)); + + XRRSetCrtcGamma(_glfw.x11.display, monitor->x11.crtc, gamma); + XRRFreeGamma(gamma); + } + else if (_glfw.x11.vidmode.available) + { + XF86VidModeSetGammaRamp(_glfw.x11.display, + _glfw.x11.screen, + ramp->size, + (unsigned short*) ramp->red, + (unsigned short*) ramp->green, + (unsigned short*) ramp->blue); + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Gamma ramp access not supported by server"); + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(None); + return monitor->x11.crtc; +} + +GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(None); + return monitor->x11.output; +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/x11_platform.h b/source/MaterialXGraphEditor/External/Glfw/src/x11_platform.h new file mode 100644 index 0000000000..04c46647c5 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/x11_platform.h @@ -0,0 +1,459 @@ +//======================================================================== +// GLFW 3.4 X11 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include +#include +#include +#include + +#include +#include +#include +#include + +// The XRandR extension provides mode setting and gamma control +#include + +// The Xkb extension provides improved keyboard support +#include + +// The Xinerama extension provides legacy monitor indices +#include + +// The XInput extension provides raw mouse motion input +#include + +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*); +typedef char* (* PFN_XcursorGetTheme)(Display*); +typedef int (* PFN_XcursorGetDefaultSize)(Display*); +typedef XcursorImage* (* PFN_XcursorLibraryLoadImage)(const char*,const char*,int); +#define XcursorImageCreate _glfw.x11.xcursor.ImageCreate +#define XcursorImageDestroy _glfw.x11.xcursor.ImageDestroy +#define XcursorImageLoadCursor _glfw.x11.xcursor.ImageLoadCursor +#define XcursorGetTheme _glfw.x11.xcursor.GetTheme +#define XcursorGetDefaultSize _glfw.x11.xcursor.GetDefaultSize +#define XcursorLibraryLoadImage _glfw.x11.xcursor.LibraryLoadImage + +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_visualid_t; +typedef struct xcb_connection_t xcb_connection_t; +typedef xcb_connection_t* (* PFN_XGetXCBConnection)(Display*); +#define XGetXCBConnection _glfw.x11.x11xcb.GetXCBConnection + +typedef Bool (* PFN_XF86VidModeQueryExtension)(Display*,int*,int*); +typedef Bool (* PFN_XF86VidModeGetGammaRamp)(Display*,int,int,unsigned short*,unsigned short*,unsigned short*); +typedef Bool (* PFN_XF86VidModeSetGammaRamp)(Display*,int,int,unsigned short*,unsigned short*,unsigned short*); +typedef Bool (* PFN_XF86VidModeGetGammaRampSize)(Display*,int,int*); +#define XF86VidModeQueryExtension _glfw.x11.vidmode.QueryExtension +#define XF86VidModeGetGammaRamp _glfw.x11.vidmode.GetGammaRamp +#define XF86VidModeSetGammaRamp _glfw.x11.vidmode.SetGammaRamp +#define XF86VidModeGetGammaRampSize _glfw.x11.vidmode.GetGammaRampSize + +typedef Status (* PFN_XIQueryVersion)(Display*,int*,int*); +typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int); +#define XIQueryVersion _glfw.x11.xi.QueryVersion +#define XISelectEvents _glfw.x11.xi.SelectEvents + +typedef Bool (* PFN_XRenderQueryExtension)(Display*,int*,int*); +typedef Status (* PFN_XRenderQueryVersion)(Display*dpy,int*,int*); +typedef XRenderPictFormat* (* PFN_XRenderFindVisualFormat)(Display*,Visual const*); +#define XRenderQueryExtension _glfw.x11.xrender.QueryExtension +#define XRenderQueryVersion _glfw.x11.xrender.QueryVersion +#define XRenderFindVisualFormat _glfw.x11.xrender.FindVisualFormat + +typedef VkFlags VkXlibSurfaceCreateFlagsKHR; +typedef VkFlags VkXcbSurfaceCreateFlagsKHR; + +typedef struct VkXlibSurfaceCreateInfoKHR +{ + VkStructureType sType; + const void* pNext; + VkXlibSurfaceCreateFlagsKHR flags; + Display* dpy; + Window window; +} VkXlibSurfaceCreateInfoKHR; + +typedef struct VkXcbSurfaceCreateInfoKHR +{ + VkStructureType sType; + const void* pNext; + VkXcbSurfaceCreateFlagsKHR flags; + xcb_connection_t* connection; + xcb_window_t window; +} VkXcbSurfaceCreateInfoKHR; + +typedef VkResult (APIENTRY *PFN_vkCreateXlibSurfaceKHR)(VkInstance,const VkXlibSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); +typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice,uint32_t,Display*,VisualID); +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); + +#include "posix_thread.h" +#include "posix_time.h" +#include "xkb_unicode.h" +#include "glx_context.h" +#include "egl_context.h" +#include "osmesa_context.h" +#if defined(__linux__) +#include "linux_joystick.h" +#else +#include "null_joystick.h" +#endif + +#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) +#define _glfw_dlclose(handle) dlclose(handle) +#define _glfw_dlsym(handle, name) dlsym(handle, name) + +#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->x11.handle) +#define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.x11.display) + +#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowX11 x11 +#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11 +#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorX11 x11 +#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorX11 x11 + + +// X11-specific per-window data +// +typedef struct _GLFWwindowX11 +{ + Colormap colormap; + Window handle; + Window parent; + XIC ic; + + GLFWbool overrideRedirect; + GLFWbool iconified; + GLFWbool maximized; + + // Whether the visual supports framebuffer transparency + GLFWbool transparent; + + // Cached position and size used to filter out duplicate events + int width, height; + int xpos, ypos; + + // The last received cursor position, regardless of source + int lastCursorPosX, lastCursorPosY; + // The last position the cursor was warped to by GLFW + int warpCursorPosX, warpCursorPosY; + + // The time of the last KeyPress event + Time lastKeyTime; + +} _GLFWwindowX11; + +// X11-specific global data +// +typedef struct _GLFWlibraryX11 +{ + Display* display; + int screen; + Window root; + + // System content scale + float contentScaleX, contentScaleY; + // Helper window for IPC + Window helperWindowHandle; + // Invisible cursor for hidden cursor mode + Cursor hiddenCursorHandle; + // Context for mapping window XIDs to _GLFWwindow pointers + XContext context; + // XIM input method + XIM im; + // Most recent error code received by X error handler + int errorCode; + // Primary selection string (while the primary selection is owned) + char* primarySelectionString; + // Clipboard string (while the selection is owned) + char* clipboardString; + // Key name string + char keynames[GLFW_KEY_LAST + 1][5]; + // X11 keycode to GLFW key LUT + short int keycodes[256]; + // GLFW key to X11 keycode LUT + short int scancodes[GLFW_KEY_LAST + 1]; + // Where to place the cursor when re-enabled + double restoreCursorPosX, restoreCursorPosY; + // The window whose disabled cursor mode is active + _GLFWwindow* disabledCursorWindow; + + // Window manager atoms + Atom NET_SUPPORTED; + Atom NET_SUPPORTING_WM_CHECK; + Atom WM_PROTOCOLS; + Atom WM_STATE; + Atom WM_DELETE_WINDOW; + Atom NET_WM_NAME; + Atom NET_WM_ICON_NAME; + Atom NET_WM_ICON; + Atom NET_WM_PID; + Atom NET_WM_PING; + Atom NET_WM_WINDOW_TYPE; + Atom NET_WM_WINDOW_TYPE_NORMAL; + Atom NET_WM_STATE; + Atom NET_WM_STATE_ABOVE; + Atom NET_WM_STATE_FULLSCREEN; + Atom NET_WM_STATE_MAXIMIZED_VERT; + Atom NET_WM_STATE_MAXIMIZED_HORZ; + Atom NET_WM_STATE_DEMANDS_ATTENTION; + Atom NET_WM_BYPASS_COMPOSITOR; + Atom NET_WM_FULLSCREEN_MONITORS; + Atom NET_WM_WINDOW_OPACITY; + Atom NET_WM_CM_Sx; + Atom NET_WORKAREA; + Atom NET_CURRENT_DESKTOP; + Atom NET_ACTIVE_WINDOW; + Atom NET_FRAME_EXTENTS; + Atom NET_REQUEST_FRAME_EXTENTS; + Atom MOTIF_WM_HINTS; + + // Xdnd (drag and drop) atoms + Atom XdndAware; + Atom XdndEnter; + Atom XdndPosition; + Atom XdndStatus; + Atom XdndActionCopy; + Atom XdndDrop; + Atom XdndFinished; + Atom XdndSelection; + Atom XdndTypeList; + Atom text_uri_list; + + // Selection (clipboard) atoms + Atom TARGETS; + Atom MULTIPLE; + Atom INCR; + Atom CLIPBOARD; + Atom PRIMARY; + Atom CLIPBOARD_MANAGER; + Atom SAVE_TARGETS; + Atom NULL_; + Atom UTF8_STRING; + Atom COMPOUND_STRING; + Atom ATOM_PAIR; + Atom GLFW_SELECTION; + + struct { + GLFWbool available; + void* handle; + int eventBase; + int errorBase; + int major; + int minor; + GLFWbool gammaBroken; + 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; + + struct { + GLFWbool available; + GLFWbool detectable; + int majorOpcode; + int eventBase; + int errorBase; + int major; + int minor; + unsigned int group; + } xkb; + + struct { + int count; + int timeout; + int interval; + int blanking; + int exposure; + } saver; + + struct { + int version; + Window source; + Atom format; + } xdnd; + + struct { + void* handle; + PFN_XcursorImageCreate ImageCreate; + PFN_XcursorImageDestroy ImageDestroy; + PFN_XcursorImageLoadCursor ImageLoadCursor; + PFN_XcursorGetTheme GetTheme; + PFN_XcursorGetDefaultSize GetDefaultSize; + PFN_XcursorLibraryLoadImage LibraryLoadImage; + } xcursor; + + struct { + GLFWbool available; + void* handle; + int major; + int minor; + PFN_XineramaIsActive IsActive; + PFN_XineramaQueryExtension QueryExtension; + PFN_XineramaQueryScreens QueryScreens; + } xinerama; + + struct { + void* handle; + PFN_XGetXCBConnection GetXCBConnection; + } x11xcb; + + struct { + GLFWbool available; + void* handle; + int eventBase; + int errorBase; + PFN_XF86VidModeQueryExtension QueryExtension; + PFN_XF86VidModeGetGammaRamp GetGammaRamp; + PFN_XF86VidModeSetGammaRamp SetGammaRamp; + PFN_XF86VidModeGetGammaRampSize GetGammaRampSize; + } vidmode; + + struct { + GLFWbool available; + void* handle; + int majorOpcode; + int eventBase; + int errorBase; + int major; + int minor; + PFN_XIQueryVersion QueryVersion; + PFN_XISelectEvents SelectEvents; + } xi; + + struct { + GLFWbool available; + void* handle; + int major; + int minor; + int eventBase; + int errorBase; + PFN_XRenderQueryExtension QueryExtension; + PFN_XRenderQueryVersion QueryVersion; + PFN_XRenderFindVisualFormat FindVisualFormat; + } xrender; + +} _GLFWlibraryX11; + +// X11-specific per-monitor data +// +typedef struct _GLFWmonitorX11 +{ + RROutput output; + RRCrtc crtc; + RRMode oldMode; + + // Index of corresponding Xinerama screen, + // for EWMH full screen window placement + int index; + +} _GLFWmonitorX11; + +// X11-specific per-cursor data +// +typedef struct _GLFWcursorX11 +{ + Cursor handle; + +} _GLFWcursorX11; + + +void _glfwPollMonitorsX11(void); +void _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired); +void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor); + +Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot); + +unsigned long _glfwGetWindowPropertyX11(Window window, + Atom property, + Atom type, + unsigned char** value); +GLFWbool _glfwIsVisualTransparentX11(Visual* visual); + +void _glfwGrabErrorHandlerX11(void); +void _glfwReleaseErrorHandlerX11(void); +void _glfwInputErrorX11(int error, const char* message); + +void _glfwPushSelectionToManagerX11(void); + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/x11_window.c b/source/MaterialXGraphEditor/External/Glfw/src/x11_window.c new file mode 100644 index 0000000000..23dc89adf0 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/x11_window.c @@ -0,0 +1,3213 @@ +//======================================================================== +// GLFW 3.4 X11 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#include "internal.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +// Action for EWMH client messages +#define _NET_WM_STATE_REMOVE 0 +#define _NET_WM_STATE_ADD 1 +#define _NET_WM_STATE_TOGGLE 2 + +// Additional mouse button names for XButtonEvent +#define Button6 6 +#define Button7 7 + +// Motif WM hints flags +#define MWM_HINTS_DECORATIONS 2 +#define MWM_DECOR_ALL 1 + +#define _GLFW_XDND_VERSION 5 + + +// Wait for data to arrive using select +// This avoids blocking other threads via the per-display Xlib lock that also +// covers GLX functions +// +static GLFWbool waitForEvent(double* timeout) +{ + fd_set fds; + const int fd = ConnectionNumber(_glfw.x11.display); + int count = fd + 1; + +#if defined(__linux__) + if (_glfw.linjs.inotify > fd) + count = _glfw.linjs.inotify + 1; +#endif + for (;;) + { + FD_ZERO(&fds); + FD_SET(fd, &fds); +#if defined(__linux__) + if (_glfw.linjs.inotify > 0) + FD_SET(_glfw.linjs.inotify, &fds); +#endif + + if (timeout) + { + const long seconds = (long) *timeout; + const long microseconds = (long) ((*timeout - seconds) * 1e6); + struct timeval tv = { seconds, microseconds }; + const uint64_t base = _glfwPlatformGetTimerValue(); + + const int result = select(count, &fds, NULL, NULL, &tv); + const int error = errno; + + *timeout -= (_glfwPlatformGetTimerValue() - base) / + (double) _glfwPlatformGetTimerFrequency(); + + if (result > 0) + return GLFW_TRUE; + if ((result == -1 && error == EINTR) || *timeout <= 0.0) + return GLFW_FALSE; + } + else if (select(count, &fds, NULL, NULL, NULL) != -1 || errno != EINTR) + return GLFW_TRUE; + } +} + +// Waits until a VisibilityNotify event arrives for the specified window or the +// timeout period elapses (ICCCM section 4.2.2) +// +static GLFWbool waitForVisibilityNotify(_GLFWwindow* window) +{ + XEvent dummy; + double timeout = 0.1; + + while (!XCheckTypedWindowEvent(_glfw.x11.display, + window->x11.handle, + VisibilityNotify, + &dummy)) + { + if (!waitForEvent(&timeout)) + return GLFW_FALSE; + } + + return GLFW_TRUE; +} + +// Returns whether the window is iconified +// +static int getWindowState(_GLFWwindow* window) +{ + int result = WithdrawnState; + struct { + CARD32 state; + Window icon; + } *state = NULL; + + if (_glfwGetWindowPropertyX11(window->x11.handle, + _glfw.x11.WM_STATE, + _glfw.x11.WM_STATE, + (unsigned char**) &state) >= 2) + { + result = state->state; + } + + if (state) + XFree(state); + + return result; +} + +// Returns whether the event is a selection event +// +static Bool isSelectionEvent(Display* display, XEvent* event, XPointer pointer) +{ + if (event->xany.window != _glfw.x11.helperWindowHandle) + return False; + + return event->type == SelectionRequest || + event->type == SelectionNotify || + event->type == SelectionClear; +} + +// Returns whether it is a _NET_FRAME_EXTENTS event for the specified window +// +static Bool isFrameExtentsEvent(Display* display, XEvent* event, XPointer pointer) +{ + _GLFWwindow* window = (_GLFWwindow*) pointer; + return event->type == PropertyNotify && + event->xproperty.state == PropertyNewValue && + event->xproperty.window == window->x11.handle && + 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 an X event modifier state mask +// +static int translateState(int state) +{ + int mods = 0; + + if (state & ShiftMask) + mods |= GLFW_MOD_SHIFT; + if (state & ControlMask) + mods |= GLFW_MOD_CONTROL; + if (state & Mod1Mask) + mods |= GLFW_MOD_ALT; + if (state & Mod4Mask) + mods |= GLFW_MOD_SUPER; + if (state & LockMask) + mods |= GLFW_MOD_CAPS_LOCK; + if (state & Mod2Mask) + mods |= GLFW_MOD_NUM_LOCK; + + return mods; +} + +// Translates an X11 key code to a GLFW key token +// +static int translateKey(int scancode) +{ + // Use the pre-filled LUT (see createKeyTables() in x11_init.c) + if (scancode < 0 || scancode > 255) + return GLFW_KEY_UNKNOWN; + + return _glfw.x11.keycodes[scancode]; +} + +// Sends an EWMH or ICCCM event to the window manager +// +static void sendEventToWM(_GLFWwindow* window, Atom type, + long a, long b, long c, long d, long e) +{ + XEvent event = { ClientMessage }; + event.xclient.window = window->x11.handle; + event.xclient.format = 32; // Data is 32-bit longs + event.xclient.message_type = type; + event.xclient.data.l[0] = a; + event.xclient.data.l[1] = b; + event.xclient.data.l[2] = c; + event.xclient.data.l[3] = d; + event.xclient.data.l[4] = e; + + XSendEvent(_glfw.x11.display, _glfw.x11.root, + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &event); +} + +// Updates the normal hints according to the window settings +// +static void updateNormalHints(_GLFWwindow* window, int width, int height) +{ + XSizeHints* hints = XAllocSizeHints(); + + if (!window->monitor) + { + if (window->resizable) + { + if (window->minwidth != GLFW_DONT_CARE && + window->minheight != GLFW_DONT_CARE) + { + hints->flags |= PMinSize; + hints->min_width = window->minwidth; + hints->min_height = window->minheight; + } + + if (window->maxwidth != GLFW_DONT_CARE && + window->maxheight != GLFW_DONT_CARE) + { + hints->flags |= PMaxSize; + hints->max_width = window->maxwidth; + hints->max_height = window->maxheight; + } + + if (window->numer != GLFW_DONT_CARE && + window->denom != GLFW_DONT_CARE) + { + hints->flags |= PAspect; + hints->min_aspect.x = hints->max_aspect.x = window->numer; + hints->min_aspect.y = hints->max_aspect.y = window->denom; + } + } + else + { + hints->flags |= (PMinSize | PMaxSize); + hints->min_width = hints->max_width = width; + hints->min_height = hints->max_height = height; + } + } + + hints->flags |= PWinGravity; + hints->win_gravity = StaticGravity; + + XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints); + XFree(hints); +} + +// Updates the full screen status of the window +// +static void updateWindowMode(_GLFWwindow* window) +{ + if (window->monitor) + { + if (_glfw.x11.xinerama.available && + _glfw.x11.NET_WM_FULLSCREEN_MONITORS) + { + sendEventToWM(window, + _glfw.x11.NET_WM_FULLSCREEN_MONITORS, + window->monitor->x11.index, + window->monitor->x11.index, + window->monitor->x11.index, + window->monitor->x11.index, + 0); + } + + if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN) + { + sendEventToWM(window, + _glfw.x11.NET_WM_STATE, + _NET_WM_STATE_ADD, + _glfw.x11.NET_WM_STATE_FULLSCREEN, + 0, 1, 0); + } + else + { + // This is the butcher's way of removing window decorations + // Setting the override-redirect attribute on a window makes the + // window manager ignore the window completely (ICCCM, section 4) + // The good thing is that this makes undecorated full screen windows + // easy to do; the bad thing is that we have to do everything + // manually and some things (like iconify/restore) won't work at + // all, as those are tasks usually performed by the window manager + + XSetWindowAttributes attributes; + attributes.override_redirect = True; + XChangeWindowAttributes(_glfw.x11.display, + window->x11.handle, + CWOverrideRedirect, + &attributes); + + window->x11.overrideRedirect = GLFW_TRUE; + } + + // Enable compositor bypass + if (!window->x11.transparent) + { + const unsigned long value = 1; + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32, + PropModeReplace, (unsigned char*) &value, 1); + } + } + else + { + if (_glfw.x11.xinerama.available && + _glfw.x11.NET_WM_FULLSCREEN_MONITORS) + { + XDeleteProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_FULLSCREEN_MONITORS); + } + + if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN) + { + sendEventToWM(window, + _glfw.x11.NET_WM_STATE, + _NET_WM_STATE_REMOVE, + _glfw.x11.NET_WM_STATE_FULLSCREEN, + 0, 1, 0); + } + else + { + XSetWindowAttributes attributes; + attributes.override_redirect = False; + XChangeWindowAttributes(_glfw.x11.display, + window->x11.handle, + CWOverrideRedirect, + &attributes); + + window->x11.overrideRedirect = GLFW_FALSE; + } + + // Disable compositor bypass + if (!window->x11.transparent) + { + XDeleteProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_BYPASS_COMPOSITOR); + } + } +} + +// Splits and translates a text/uri-list into separate file paths +// NOTE: This function destroys the provided string +// +static char** parseUriList(char* text, int* count) +{ + const char* prefix = "file://"; + char** paths = NULL; + char* line; + + *count = 0; + + while ((line = strtok(text, "\r\n"))) + { + text = NULL; + + if (line[0] == '#') + continue; + + if (strncmp(line, prefix, strlen(prefix)) == 0) + { + line += strlen(prefix); + // TODO: Validate hostname + while (*line != '/') + line++; + } + + (*count)++; + + char* path = calloc(strlen(line) + 1, 1); + paths = realloc(paths, *count * sizeof(char*)); + paths[*count - 1] = path; + + while (*line) + { + if (line[0] == '%' && line[1] && line[2]) + { + const char digits[3] = { line[1], line[2], '\0' }; + *path = strtol(digits, NULL, 16); + line += 2; + } + else + *path = *line; + + path++; + line++; + } + } + + 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; +} + +// Updates the cursor image according to its cursor mode +// +static void updateCursorImage(_GLFWwindow* window) +{ + if (window->cursorMode == GLFW_CURSOR_NORMAL) + { + if (window->cursor) + { + XDefineCursor(_glfw.x11.display, window->x11.handle, + window->cursor->x11.handle); + } + else + XUndefineCursor(_glfw.x11.display, window->x11.handle); + } + else + { + XDefineCursor(_glfw.x11.display, window->x11.handle, + _glfw.x11.hiddenCursorHandle); + } +} + +// Enable XI2 raw mouse motion events +// +static void enableRawMouseMotion(_GLFWwindow* window) +{ + XIEventMask em; + unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 }; + + em.deviceid = XIAllMasterDevices; + em.mask_len = sizeof(mask); + em.mask = mask; + XISetMask(mask, XI_RawMotion); + + XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1); +} + +// Disable XI2 raw mouse motion events +// +static void disableRawMouseMotion(_GLFWwindow* window) +{ + XIEventMask em; + unsigned char mask[] = { 0 }; + + em.deviceid = XIAllMasterDevices; + em.mask_len = sizeof(mask); + em.mask = mask; + + XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1); +} + +// Apply disabled cursor mode to a focused window +// +static void disableCursor(_GLFWwindow* window) +{ + if (window->rawMouseMotion) + enableRawMouseMotion(window); + + _glfw.x11.disabledCursorWindow = window; + _glfwPlatformGetCursorPos(window, + &_glfw.x11.restoreCursorPosX, + &_glfw.x11.restoreCursorPosY); + updateCursorImage(window); + _glfwCenterCursorInContentArea(window); + XGrabPointer(_glfw.x11.display, window->x11.handle, True, + ButtonPressMask | ButtonReleaseMask | PointerMotionMask, + GrabModeAsync, GrabModeAsync, + window->x11.handle, + _glfw.x11.hiddenCursorHandle, + CurrentTime); +} + +// Exit disabled cursor mode for the specified window +// +static void enableCursor(_GLFWwindow* window) +{ + if (window->rawMouseMotion) + disableRawMouseMotion(window); + + _glfw.x11.disabledCursorWindow = NULL; + XUngrabPointer(_glfw.x11.display, CurrentTime); + _glfwPlatformSetCursorPos(window, + _glfw.x11.restoreCursorPosX, + _glfw.x11.restoreCursorPosY); + updateCursorImage(window); +} + +// Create the X11 window (and its colormap) +// +static GLFWbool createNativeWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + Visual* visual, int depth) +{ + int width = wndconfig->width; + int height = wndconfig->height; + + if (wndconfig->scaleToMonitor) + { + width *= _glfw.x11.contentScaleX; + height *= _glfw.x11.contentScaleY; + } + + // Create a colormap based on the visual used by the current context + window->x11.colormap = XCreateColormap(_glfw.x11.display, + _glfw.x11.root, + visual, + AllocNone); + + window->x11.transparent = _glfwIsVisualTransparentX11(visual); + + XSetWindowAttributes wa = { 0 }; + wa.colormap = window->x11.colormap; + wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | + PointerMotionMask | ButtonPressMask | ButtonReleaseMask | + ExposureMask | FocusChangeMask | VisibilityChangeMask | + EnterWindowMask | LeaveWindowMask | PropertyChangeMask; + + _glfwGrabErrorHandlerX11(); + + window->x11.parent = _glfw.x11.root; + window->x11.handle = XCreateWindow(_glfw.x11.display, + _glfw.x11.root, + 0, 0, // Position + width, height, + 0, // Border width + depth, // Color depth + InputOutput, + visual, + CWBorderPixel | CWColormap | CWEventMask, + &wa); + + _glfwReleaseErrorHandlerX11(); + + if (!window->x11.handle) + { + _glfwInputErrorX11(GLFW_PLATFORM_ERROR, + "X11: Failed to create window"); + return GLFW_FALSE; + } + + XSaveContext(_glfw.x11.display, + window->x11.handle, + _glfw.x11.context, + (XPointer) window); + + if (!wndconfig->decorated) + _glfwPlatformSetWindowDecorated(window, GLFW_FALSE); + + if (_glfw.x11.NET_WM_STATE && !window->monitor) + { + Atom states[3]; + int count = 0; + + if (wndconfig->floating) + { + if (_glfw.x11.NET_WM_STATE_ABOVE) + states[count++] = _glfw.x11.NET_WM_STATE_ABOVE; + } + + if (wndconfig->maximized) + { + if (_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) + { + states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT; + states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ; + window->x11.maximized = GLFW_TRUE; + } + } + + if (count) + { + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_STATE, XA_ATOM, 32, + PropModeReplace, (unsigned char*) states, count); + } + } + + // Declare the WM protocols supported by GLFW + { + Atom protocols[] = + { + _glfw.x11.WM_DELETE_WINDOW, + _glfw.x11.NET_WM_PING + }; + + XSetWMProtocols(_glfw.x11.display, window->x11.handle, + protocols, sizeof(protocols) / sizeof(Atom)); + } + + // Declare our PID + { + const long pid = getpid(); + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_PID, XA_CARDINAL, 32, + PropModeReplace, + (unsigned char*) &pid, 1); + } + + if (_glfw.x11.NET_WM_WINDOW_TYPE && _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL) + { + Atom type = _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL; + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32, + PropModeReplace, (unsigned char*) &type, 1); + } + + // Set ICCCM WM_HINTS property + { + XWMHints* hints = XAllocWMHints(); + if (!hints) + { + _glfwInputError(GLFW_OUT_OF_MEMORY, + "X11: Failed to allocate WM hints"); + return GLFW_FALSE; + } + + hints->flags = StateHint; + hints->initial_state = NormalState; + + XSetWMHints(_glfw.x11.display, window->x11.handle, hints); + XFree(hints); + } + + updateNormalHints(window, width, height); + + // Set ICCCM WM_CLASS property + { + XClassHint* hint = XAllocClassHint(); + + if (strlen(wndconfig->x11.instanceName) && + strlen(wndconfig->x11.className)) + { + hint->res_name = (char*) wndconfig->x11.instanceName; + hint->res_class = (char*) wndconfig->x11.className; + } + else + { + const char* resourceName = getenv("RESOURCE_NAME"); + if (resourceName && strlen(resourceName)) + hint->res_name = (char*) resourceName; + else if (strlen(wndconfig->title)) + hint->res_name = (char*) wndconfig->title; + else + hint->res_name = (char*) "glfw-application"; + + if (strlen(wndconfig->title)) + hint->res_class = (char*) wndconfig->title; + else + hint->res_class = (char*) "GLFW-Application"; + } + + XSetClassHint(_glfw.x11.display, window->x11.handle, hint); + XFree(hint); + } + + // Announce support for Xdnd (drag and drop) + { + const Atom version = _GLFW_XDND_VERSION; + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.XdndAware, XA_ATOM, 32, + PropModeReplace, (unsigned char*) &version, 1); + } + + _glfwPlatformSetWindowTitle(window, wndconfig->title); + + if (_glfw.x11.im) + { + window->x11.ic = XCreateIC(_glfw.x11.im, + XNInputStyle, + XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, + window->x11.handle, + XNFocusWindow, + window->x11.handle, + NULL); + } + + if (window->x11.ic) + { + unsigned long filter = 0; + if (XGetICValues(window->x11.ic, XNFilterEvents, &filter, NULL) == NULL) + XSelectInput(_glfw.x11.display, window->x11.handle, wa.event_mask | filter); + } + + _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos); + _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height); + + return GLFW_TRUE; +} + +// Set the specified property to the selection converted to the requested target +// +static Atom writeTargetToProperty(const XSelectionRequestEvent* request) +{ + int i; + char* selectionString = NULL; + const Atom formats[] = { _glfw.x11.UTF8_STRING, XA_STRING }; + 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) + { + // The requester is a legacy client (ICCCM section 2.2) + // We don't support legacy clients, so fail here + return None; + } + + if (request->target == _glfw.x11.TARGETS) + { + // The list of supported targets was requested + + const Atom targets[] = { _glfw.x11.TARGETS, + _glfw.x11.MULTIPLE, + _glfw.x11.UTF8_STRING, + XA_STRING }; + + XChangeProperty(_glfw.x11.display, + request->requestor, + request->property, + XA_ATOM, + 32, + PropModeReplace, + (unsigned char*) targets, + sizeof(targets) / sizeof(targets[0])); + + return request->property; + } + + if (request->target == _glfw.x11.MULTIPLE) + { + // Multiple conversions were requested + + Atom* targets; + unsigned long i, count; + + count = _glfwGetWindowPropertyX11(request->requestor, + request->property, + _glfw.x11.ATOM_PAIR, + (unsigned char**) &targets); + + for (i = 0; i < count; i += 2) + { + int j; + + for (j = 0; j < formatCount; j++) + { + if (targets[i] == formats[j]) + break; + } + + if (j < formatCount) + { + XChangeProperty(_glfw.x11.display, + request->requestor, + targets[i + 1], + targets[i], + 8, + PropModeReplace, + (unsigned char *) selectionString, + strlen(selectionString)); + } + else + targets[i + 1] = None; + } + + XChangeProperty(_glfw.x11.display, + request->requestor, + request->property, + _glfw.x11.ATOM_PAIR, + 32, + PropModeReplace, + (unsigned char*) targets, + count); + + XFree(targets); + + return request->property; + } + + if (request->target == _glfw.x11.SAVE_TARGETS) + { + // The request is a check whether we support SAVE_TARGETS + // It should be handled as a no-op side effect target + + XChangeProperty(_glfw.x11.display, + request->requestor, + request->property, + _glfw.x11.NULL_, + 32, + PropModeReplace, + NULL, + 0); + + return request->property; + } + + // Conversion to a data target was requested + + for (i = 0; i < formatCount; i++) + { + if (request->target == formats[i]) + { + // The requested target is one we support + + XChangeProperty(_glfw.x11.display, + request->requestor, + request->property, + request->target, + 8, + PropModeReplace, + (unsigned char *) selectionString, + strlen(selectionString)); + + return request->property; + } + } + + // The requested target is not supported + + return None; +} + +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); + _glfw.x11.clipboardString = NULL; + } +} + +static void handleSelectionRequest(XEvent* event) +{ + const XSelectionRequestEvent* request = &event->xselectionrequest; + + XEvent reply = { SelectionNotify }; + reply.xselection.property = writeTargetToProperty(request); + reply.xselection.display = request->display; + reply.xselection.requestor = request->requestor; + reply.xselection.selection = request->selection; + reply.xselection.target = request->target; + reply.xselection.time = request->time; + + XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply); +} + +static const char* getSelectionString(Atom selection) +{ + 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 (size_t 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, + ¬ification)) + { + waitForEvent(NULL); + } + + if (notification.xselection.property == None) + continue; + + XCheckIfEvent(_glfw.x11.display, + &dummy, + isSelPropNewValueNotify, + (XPointer) ¬ification); + + XGetWindowProperty(_glfw.x11.display, + notification.xselection.requestor, + notification.xselection.property, + 0, + LONG_MAX, + True, + AnyPropertyType, + &actualType, + &actualFormat, + &itemCount, + &bytesAfter, + (unsigned char**) &data); + + if (actualType == _glfw.x11.INCR) + { + size_t size = 1; + char* string = NULL; + + for (;;) + { + while (!XCheckIfEvent(_glfw.x11.display, + &dummy, + isSelPropNewValueNotify, + (XPointer) ¬ification)) + { + waitForEvent(NULL); + } + + XFree(data); + XGetWindowProperty(_glfw.x11.display, + notification.xselection.requestor, + notification.xselection.property, + 0, + LONG_MAX, + True, + AnyPropertyType, + &actualType, + &actualFormat, + &itemCount, + &bytesAfter, + (unsigned char**) &data); + + if (itemCount) + { + size += itemCount; + 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 = _glfw_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 +// +static void acquireMonitor(_GLFWwindow* window) +{ + if (_glfw.x11.saver.count == 0) + { + // Remember old screen saver settings + XGetScreenSaver(_glfw.x11.display, + &_glfw.x11.saver.timeout, + &_glfw.x11.saver.interval, + &_glfw.x11.saver.blanking, + &_glfw.x11.saver.exposure); + + // Disable screen saver + XSetScreenSaver(_glfw.x11.display, 0, 0, DontPreferBlanking, + DefaultExposures); + } + + if (!window->monitor->window) + _glfw.x11.saver.count++; + + _glfwSetVideoModeX11(window->monitor, &window->videoMode); + + if (window->x11.overrideRedirect) + { + int xpos, ypos; + GLFWvidmode mode; + + // Manually position the window over its monitor + _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); + _glfwPlatformGetVideoMode(window->monitor, &mode); + + XMoveResizeWindow(_glfw.x11.display, window->x11.handle, + xpos, ypos, mode.width, mode.height); + } + + _glfwInputMonitorWindow(window->monitor, window); +} + +// Remove the window and restore the original video mode +// +static void releaseMonitor(_GLFWwindow* window) +{ + if (window->monitor->window != window) + return; + + _glfwInputMonitorWindow(window->monitor, NULL); + _glfwRestoreVideoModeX11(window->monitor); + + _glfw.x11.saver.count--; + + if (_glfw.x11.saver.count == 0) + { + // Restore old screen saver settings + XSetScreenSaver(_glfw.x11.display, + _glfw.x11.saver.timeout, + _glfw.x11.saver.interval, + _glfw.x11.saver.blanking, + _glfw.x11.saver.exposure); + } +} + +// Process the specified X event +// +static void processEvent(XEvent *event) +{ + int keycode = 0; + Bool filtered = False; + + // HACK: Save scancode as some IMs clear the field in XFilterEvent + if (event->type == KeyPress || event->type == KeyRelease) + keycode = event->xkey.keycode; + + if (_glfw.x11.im) + filtered = XFilterEvent(event, None); + + if (_glfw.x11.randr.available) + { + if (event->type == _glfw.x11.randr.eventBase + RRNotify) + { + XRRUpdateConfiguration(event); + _glfwPollMonitorsX11(); + return; + } + } + + if (_glfw.x11.xkb.available) + { + if (event->type == _glfw.x11.xkb.eventBase + XkbEventCode) + { + if (((XkbEvent*) event)->any.xkb_type == XkbStateNotify && + (((XkbEvent*) event)->state.changed & XkbGroupStateMask)) + { + _glfw.x11.xkb.group = ((XkbEvent*) event)->state.group; + } + } + } + + if (event->type == GenericEvent) + { + if (_glfw.x11.xi.available) + { + _GLFWwindow* window = _glfw.x11.disabledCursorWindow; + + if (window && + window->rawMouseMotion && + event->xcookie.extension == _glfw.x11.xi.majorOpcode && + XGetEventData(_glfw.x11.display, &event->xcookie) && + event->xcookie.evtype == XI_RawMotion) + { + XIRawEvent* re = event->xcookie.data; + if (re->valuators.mask_len) + { + const double* values = re->raw_values; + double xpos = window->virtualCursorPosX; + double ypos = window->virtualCursorPosY; + + if (XIMaskIsSet(re->valuators.mask, 0)) + { + xpos += *values; + values++; + } + + if (XIMaskIsSet(re->valuators.mask, 1)) + ypos += *values; + + _glfwInputCursorPos(window, xpos, ypos); + } + } + + XFreeEventData(_glfw.x11.display, &event->xcookie); + } + + return; + } + + if (event->type == SelectionClear) + { + handleSelectionClear(event); + return; + } + else if (event->type == SelectionRequest) + { + handleSelectionRequest(event); + return; + } + + _GLFWwindow* window = NULL; + if (XFindContext(_glfw.x11.display, + event->xany.window, + _glfw.x11.context, + (XPointer*) &window) != 0) + { + // This is an event for a window that has already been destroyed + return; + } + + switch (event->type) + { + case ReparentNotify: + { + window->x11.parent = event->xreparent.parent; + return; + } + + case KeyPress: + { + const int key = translateKey(keycode); + const int mods = translateState(event->xkey.state); + const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); + + if (window->x11.ic) + { + // HACK: Ignore duplicate key press events generated by ibus + // These have the same timestamp as the original event + // Corresponding release events are filtered out + // implicitly by the GLFW key repeat logic + if (window->x11.lastKeyTime < event->xkey.time) + { + if (keycode) + _glfwInputKey(window, key, keycode, GLFW_PRESS, mods); + + window->x11.lastKeyTime = event->xkey.time; + } + + if (!filtered) + { + int count; + Status status; +#if defined(X_HAVE_UTF8_STRING) + char buffer[100]; + char* chars = buffer; + + count = Xutf8LookupString(window->x11.ic, + &event->xkey, + buffer, sizeof(buffer) - 1, + NULL, &status); + + if (status == XBufferOverflow) + { + chars = calloc(count + 1, 1); + count = Xutf8LookupString(window->x11.ic, + &event->xkey, + chars, count, + NULL, &status); + } + + if (status == XLookupChars || status == XLookupBoth) + { + const char* c = chars; + chars[count] = '\0'; + while (c - chars < count) + _glfwInputChar(window, decodeUTF8(&c), mods, plain); + } +#else /*X_HAVE_UTF8_STRING*/ + wchar_t buffer[16]; + wchar_t* chars = buffer; + + count = XwcLookupString(window->x11.ic, + &event->xkey, + buffer, + sizeof(buffer) / sizeof(wchar_t), + NULL, + &status); + + if (status == XBufferOverflow) + { + chars = calloc(count, sizeof(wchar_t)); + count = XwcLookupString(window->x11.ic, + &event->xkey, + chars, count, + NULL, &status); + } + + if (status == XLookupChars || status == XLookupBoth) + { + int i; + for (i = 0; i < count; i++) + _glfwInputChar(window, chars[i], mods, plain); + } +#endif /*X_HAVE_UTF8_STRING*/ + + if (chars != buffer) + free(chars); + } + } + else + { + KeySym keysym; + XLookupString(&event->xkey, NULL, 0, &keysym, NULL); + + _glfwInputKey(window, key, keycode, GLFW_PRESS, mods); + + const long character = _glfwKeySym2Unicode(keysym); + if (character != -1) + _glfwInputChar(window, character, mods, plain); + } + + return; + } + + case KeyRelease: + { + const int key = translateKey(keycode); + const int mods = translateState(event->xkey.state); + + if (!_glfw.x11.xkb.detectable) + { + // HACK: Key repeat events will arrive as KeyRelease/KeyPress + // pairs with similar or identical time stamps + // The key repeat logic in _glfwInputKey expects only key + // presses to repeat, so detect and discard release events + if (XEventsQueued(_glfw.x11.display, QueuedAfterReading)) + { + XEvent next; + XPeekEvent(_glfw.x11.display, &next); + + if (next.type == KeyPress && + next.xkey.window == event->xkey.window && + next.xkey.keycode == keycode) + { + // HACK: The time of repeat events sometimes doesn't + // match that of the press event, so add an + // epsilon + // Toshiyuki Takahashi can press a button + // 16 times per second so it's fairly safe to + // assume that no human is pressing the key 50 + // times per second (value is ms) + if ((next.xkey.time - event->xkey.time) < 20) + { + // This is very likely a server-generated key repeat + // event, so ignore it + return; + } + } + } + } + + _glfwInputKey(window, key, keycode, GLFW_RELEASE, mods); + return; + } + + case ButtonPress: + { + const int mods = translateState(event->xbutton.state); + + if (event->xbutton.button == Button1) + _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods); + else if (event->xbutton.button == Button2) + _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods); + else if (event->xbutton.button == Button3) + _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods); + + // Modern X provides scroll events as mouse button presses + else if (event->xbutton.button == Button4) + _glfwInputScroll(window, 0.0, 1.0); + else if (event->xbutton.button == Button5) + _glfwInputScroll(window, 0.0, -1.0); + else if (event->xbutton.button == Button6) + _glfwInputScroll(window, 1.0, 0.0); + else if (event->xbutton.button == Button7) + _glfwInputScroll(window, -1.0, 0.0); + + else + { + // Additional buttons after 7 are treated as regular buttons + // We subtract 4 to fill the gap left by scroll input above + _glfwInputMouseClick(window, + event->xbutton.button - Button1 - 4, + GLFW_PRESS, + mods); + } + + return; + } + + case ButtonRelease: + { + const int mods = translateState(event->xbutton.state); + + if (event->xbutton.button == Button1) + { + _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_LEFT, + GLFW_RELEASE, + mods); + } + else if (event->xbutton.button == Button2) + { + _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_MIDDLE, + GLFW_RELEASE, + mods); + } + else if (event->xbutton.button == Button3) + { + _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_RIGHT, + GLFW_RELEASE, + mods); + } + else if (event->xbutton.button > Button7) + { + // Additional buttons after 7 are treated as regular buttons + // We subtract 4 to fill the gap left by scroll input above + _glfwInputMouseClick(window, + event->xbutton.button - Button1 - 4, + GLFW_RELEASE, + mods); + } + + return; + } + + case EnterNotify: + { + // XEnterWindowEvent is XCrossingEvent + const int x = event->xcrossing.x; + const int y = event->xcrossing.y; + + // HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise + // ignore the defined cursor for hidden cursor mode + if (window->cursorMode == GLFW_CURSOR_HIDDEN) + updateCursorImage(window); + + _glfwInputCursorEnter(window, GLFW_TRUE); + _glfwInputCursorPos(window, x, y); + + window->x11.lastCursorPosX = x; + window->x11.lastCursorPosY = y; + return; + } + + case LeaveNotify: + { + _glfwInputCursorEnter(window, GLFW_FALSE); + return; + } + + case MotionNotify: + { + const int x = event->xmotion.x; + const int y = event->xmotion.y; + + if (x != window->x11.warpCursorPosX || + y != window->x11.warpCursorPosY) + { + // The cursor was moved by something other than GLFW + + if (window->cursorMode == GLFW_CURSOR_DISABLED) + { + if (_glfw.x11.disabledCursorWindow != window) + return; + if (window->rawMouseMotion) + return; + + const int dx = x - window->x11.lastCursorPosX; + const int dy = y - window->x11.lastCursorPosY; + + _glfwInputCursorPos(window, + window->virtualCursorPosX + dx, + window->virtualCursorPosY + dy); + } + else + _glfwInputCursorPos(window, x, y); + } + + window->x11.lastCursorPosX = x; + window->x11.lastCursorPosY = y; + return; + } + + case ConfigureNotify: + { + if (event->xconfigure.width != window->x11.width || + event->xconfigure.height != window->x11.height) + { + _glfwInputFramebufferSize(window, + event->xconfigure.width, + event->xconfigure.height); + + _glfwInputWindowSize(window, + event->xconfigure.width, + event->xconfigure.height); + + window->x11.width = event->xconfigure.width; + window->x11.height = event->xconfigure.height; + } + + int xpos = event->xconfigure.x; + int ypos = event->xconfigure.y; + + // NOTE: ConfigureNotify events from the server are in local + // coordinates, so if we are reparented we need to translate + // the position into root (screen) coordinates + if (!event->xany.send_event && window->x11.parent != _glfw.x11.root) + { + Window dummy; + XTranslateCoordinates(_glfw.x11.display, + window->x11.parent, + _glfw.x11.root, + xpos, ypos, + &xpos, &ypos, + &dummy); + } + + if (xpos != window->x11.xpos || ypos != window->x11.ypos) + { + _glfwInputWindowPos(window, xpos, ypos); + window->x11.xpos = xpos; + window->x11.ypos = ypos; + } + + return; + } + + case ClientMessage: + { + // Custom client message, probably from the window manager + + if (filtered) + return; + + if (event->xclient.message_type == None) + return; + + if (event->xclient.message_type == _glfw.x11.WM_PROTOCOLS) + { + const Atom protocol = event->xclient.data.l[0]; + if (protocol == None) + return; + + if (protocol == _glfw.x11.WM_DELETE_WINDOW) + { + // The window manager was asked to close the window, for + // example by the user pressing a 'close' window decoration + // button + _glfwInputWindowCloseRequest(window); + } + else if (protocol == _glfw.x11.NET_WM_PING) + { + // The window manager is pinging the application to ensure + // it's still responding to events + + XEvent reply = *event; + reply.xclient.window = _glfw.x11.root; + + XSendEvent(_glfw.x11.display, _glfw.x11.root, + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &reply); + } + } + else if (event->xclient.message_type == _glfw.x11.XdndEnter) + { + // A drag operation has entered the window + 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) + { + // The drag operation has finished by dropping on the window + Time time = CurrentTime; + + 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, + _glfw.x11.XdndSelection, + _glfw.x11.xdnd.format, + _glfw.x11.XdndSelection, + window->x11.handle, + time); + } + else if (_glfw.x11.xdnd.version >= 2) + { + XEvent reply = { ClientMessage }; + 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 = { ClientMessage }; + reply.xclient.window = _glfw.x11.xdnd.source; + reply.xclient.message_type = _glfw.x11.XdndStatus; + reply.xclient.format = 32; + reply.xclient.data.l[0] = window->x11.handle; + reply.xclient.data.l[2] = 0; // Specify an empty rectangle + reply.xclient.data.l[3] = 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); + XFlush(_glfw.x11.display); + } + + return; + } + + case SelectionNotify: + { + if (event->xselection.property == _glfw.x11.XdndSelection) + { + // The converted data from the drag operation has arrived + char* data; + const unsigned long result = + _glfwGetWindowPropertyX11(event->xselection.requestor, + event->xselection.property, + event->xselection.target, + (unsigned char**) &data); + + if (result) + { + int i, count; + char** paths = parseUriList(data, &count); + + _glfwInputDrop(window, count, (const char**) paths); + + for (i = 0; i < count; i++) + free(paths[i]); + free(paths); + } + + if (data) + XFree(data); + + if (_glfw.x11.xdnd.version >= 2) + { + XEvent reply = { ClientMessage }; + 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] = result; + reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy; + + XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, + False, NoEventMask, &reply); + XFlush(_glfw.x11.display); + } + } + + return; + } + + case FocusIn: + { + if (event->xfocus.mode == NotifyGrab || + event->xfocus.mode == NotifyUngrab) + { + // Ignore focus events from popup indicator windows, window menu + // key chords and window dragging + return; + } + + if (window->cursorMode == GLFW_CURSOR_DISABLED) + disableCursor(window); + + if (window->x11.ic) + XSetICFocus(window->x11.ic); + + _glfwInputWindowFocus(window, GLFW_TRUE); + return; + } + + case FocusOut: + { + if (event->xfocus.mode == NotifyGrab || + event->xfocus.mode == NotifyUngrab) + { + // Ignore focus events from popup indicator windows, window menu + // key chords and window dragging + return; + } + + if (window->cursorMode == GLFW_CURSOR_DISABLED) + enableCursor(window); + + if (window->x11.ic) + XUnsetICFocus(window->x11.ic); + + if (window->monitor && window->autoIconify) + _glfwPlatformIconifyWindow(window); + + _glfwInputWindowFocus(window, GLFW_FALSE); + return; + } + + case Expose: + { + _glfwInputWindowDamage(window); + return; + } + + case PropertyNotify: + { + if (event->xproperty.state != PropertyNewValue) + return; + + if (event->xproperty.atom == _glfw.x11.WM_STATE) + { + const int state = getWindowState(window); + if (state != IconicState && state != NormalState) + return; + + const GLFWbool iconified = (state == IconicState); + if (window->x11.iconified != iconified) + { + if (window->monitor) + { + if (iconified) + releaseMonitor(window); + else + acquireMonitor(window); + } + + window->x11.iconified = iconified; + _glfwInputWindowIconify(window, iconified); + } + } + else if (event->xproperty.atom == _glfw.x11.NET_WM_STATE) + { + const GLFWbool maximized = _glfwPlatformWindowMaximized(window); + if (window->x11.maximized != maximized) + { + window->x11.maximized = maximized; + _glfwInputWindowMaximize(window, maximized); + } + } + + return; + } + + case DestroyNotify: + return; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Retrieve a single window property of the specified type +// Inspired by fghGetWindowProperty from freeglut +// +unsigned long _glfwGetWindowPropertyX11(Window window, + Atom property, + Atom type, + unsigned char** value) +{ + Atom actualType; + int actualFormat; + unsigned long itemCount, bytesAfter; + + XGetWindowProperty(_glfw.x11.display, + window, + property, + 0, + LONG_MAX, + False, + type, + &actualType, + &actualFormat, + &itemCount, + &bytesAfter, + value); + + return itemCount; +} + +GLFWbool _glfwIsVisualTransparentX11(Visual* visual) +{ + if (!_glfw.x11.xrender.available) + return GLFW_FALSE; + + XRenderPictFormat* pf = XRenderFindVisualFormat(_glfw.x11.display, visual); + return pf && pf->direct.alphaMask; +} + +// Push contents of our selection to clipboard manager +// +void _glfwPushSelectionToManagerX11(void) +{ + XConvertSelection(_glfw.x11.display, + _glfw.x11.CLIPBOARD_MANAGER, + _glfw.x11.SAVE_TARGETS, + None, + _glfw.x11.helperWindowHandle, + CurrentTime); + + for (;;) + { + XEvent event; + + while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL)) + { + switch (event.type) + { + case SelectionRequest: + handleSelectionRequest(&event); + break; + + case SelectionClear: + handleSelectionClear(&event); + break; + + case SelectionNotify: + { + if (event.xselection.target == _glfw.x11.SAVE_TARGETS) + { + // This means one of two things; either the selection + // was not owned, which means there is no clipboard + // manager, or the transfer to the clipboard manager has + // completed + // In either case, it means we are done here + return; + } + + break; + } + } + } + + waitForEvent(NULL); + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformCreateWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + Visual* visual; + int depth; + + if (ctxconfig->client != GLFW_NO_API) + { + if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) + { + if (!_glfwInitGLX()) + return GLFW_FALSE; + if (!_glfwChooseVisualGLX(wndconfig, ctxconfig, fbconfig, &visual, &depth)) + return GLFW_FALSE; + } + else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) + { + if (!_glfwInitEGL()) + return GLFW_FALSE; + if (!_glfwChooseVisualEGL(wndconfig, ctxconfig, fbconfig, &visual, &depth)) + return GLFW_FALSE; + } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + } + } + + if (ctxconfig->client == GLFW_NO_API || + ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen); + depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen); + } + + if (!createNativeWindow(window, wndconfig, visual, depth)) + return GLFW_FALSE; + + if (ctxconfig->client != GLFW_NO_API) + { + if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) + { + if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) + { + if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + } + + if (window->monitor) + { + _glfwPlatformShowWindow(window); + updateWindowMode(window); + acquireMonitor(window); + } + + XFlush(_glfw.x11.display); + return GLFW_TRUE; +} + +void _glfwPlatformDestroyWindow(_GLFWwindow* window) +{ + if (_glfw.x11.disabledCursorWindow == window) + _glfw.x11.disabledCursorWindow = NULL; + + if (window->monitor) + releaseMonitor(window); + + if (window->x11.ic) + { + XDestroyIC(window->x11.ic); + window->x11.ic = NULL; + } + + if (window->context.destroy) + window->context.destroy(window); + + if (window->x11.handle) + { + XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context); + XUnmapWindow(_glfw.x11.display, window->x11.handle); + XDestroyWindow(_glfw.x11.display, window->x11.handle); + window->x11.handle = (Window) 0; + } + + if (window->x11.colormap) + { + XFreeColormap(_glfw.x11.display, window->x11.colormap); + window->x11.colormap = (Colormap) 0; + } + + XFlush(_glfw.x11.display); +} + +void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) +{ +#if defined(X_HAVE_UTF8_STRING) + Xutf8SetWMProperties(_glfw.x11.display, + window->x11.handle, + title, title, + NULL, 0, + NULL, NULL, NULL); +#else + // This may be a slightly better fallback than using XStoreName and + // XSetIconName, which always store their arguments using STRING + XmbSetWMProperties(_glfw.x11.display, + window->x11.handle, + title, title, + NULL, 0, + NULL, NULL, NULL); +#endif + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_NAME, _glfw.x11.UTF8_STRING, 8, + PropModeReplace, + (unsigned char*) title, strlen(title)); + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_ICON_NAME, _glfw.x11.UTF8_STRING, 8, + PropModeReplace, + (unsigned char*) title, strlen(title)); + + XFlush(_glfw.x11.display); +} + +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, + int count, const GLFWimage* images) +{ + if (count) + { + int i, j, longCount = 0; + + for (i = 0; i < count; i++) + longCount += 2 + images[i].width * images[i].height; + + long* icon = calloc(longCount, sizeof(long)); + long* target = icon; + + for (i = 0; i < count; i++) + { + *target++ = images[i].width; + *target++ = images[i].height; + + for (j = 0; j < images[i].width * images[i].height; j++) + { + *target++ = (images[i].pixels[j * 4 + 0] << 16) | + (images[i].pixels[j * 4 + 1] << 8) | + (images[i].pixels[j * 4 + 2] << 0) | + (images[i].pixels[j * 4 + 3] << 24); + } + } + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_ICON, + XA_CARDINAL, 32, + PropModeReplace, + (unsigned char*) icon, + longCount); + + free(icon); + } + else + { + XDeleteProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_ICON); + } + + XFlush(_glfw.x11.display); +} + +void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) +{ + Window dummy; + int x, y; + + XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root, + 0, 0, &x, &y, &dummy); + + if (xpos) + *xpos = x; + if (ypos) + *ypos = y; +} + +void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) +{ + // HACK: Explicitly setting PPosition to any value causes some WMs, notably + // Compiz and Metacity, to honor the position of unmapped windows + if (!_glfwPlatformWindowVisible(window)) + { + long supplied; + XSizeHints* hints = XAllocSizeHints(); + + if (XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied)) + { + hints->flags |= PPosition; + hints->x = hints->y = 0; + + XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints); + } + + XFree(hints); + } + + XMoveWindow(_glfw.x11.display, window->x11.handle, xpos, ypos); + XFlush(_glfw.x11.display); +} + +void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) +{ + XWindowAttributes attribs; + XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs); + + if (width) + *width = attribs.width; + if (height) + *height = attribs.height; +} + +void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) +{ + if (window->monitor) + { + if (window->monitor->window == window) + acquireMonitor(window); + } + else + { + if (!window->resizable) + updateNormalHints(window, width, height); + + XResizeWindow(_glfw.x11.display, window->x11.handle, width, height); + } + + XFlush(_glfw.x11.display); +} + +void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, + int minwidth, int minheight, + int maxwidth, int maxheight) +{ + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + updateNormalHints(window, width, height); + XFlush(_glfw.x11.display); +} + +void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) +{ + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + updateNormalHints(window, width, height); + XFlush(_glfw.x11.display); +} + +void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) +{ + _glfwPlatformGetWindowSize(window, width, height); +} + +void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, + int* left, int* top, + int* right, int* bottom) +{ + long* extents = NULL; + + if (window->monitor || !window->decorated) + return; + + if (_glfw.x11.NET_FRAME_EXTENTS == None) + return; + + if (!_glfwPlatformWindowVisible(window) && + _glfw.x11.NET_REQUEST_FRAME_EXTENTS) + { + XEvent event; + double timeout = 0.5; + + // Ensure _NET_FRAME_EXTENTS is set, allowing glfwGetWindowFrameSize to + // function before the window is mapped + sendEventToWM(window, _glfw.x11.NET_REQUEST_FRAME_EXTENTS, + 0, 0, 0, 0, 0); + + // HACK: Use a timeout because earlier versions of some window managers + // (at least Unity, Fluxbox and Xfwm) failed to send the reply + // They have been fixed but broken versions are still in the wild + // If you are affected by this and your window manager is NOT + // listed above, PLEASE report it to their and our issue trackers + while (!XCheckIfEvent(_glfw.x11.display, + &event, + isFrameExtentsEvent, + (XPointer) window)) + { + if (!waitForEvent(&timeout)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue"); + return; + } + } + } + + if (_glfwGetWindowPropertyX11(window->x11.handle, + _glfw.x11.NET_FRAME_EXTENTS, + XA_CARDINAL, + (unsigned char**) &extents) == 4) + { + if (left) + *left = extents[0]; + if (top) + *top = extents[2]; + if (right) + *right = extents[1]; + if (bottom) + *bottom = extents[3]; + } + + if (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) +{ + if (window->x11.overrideRedirect) + { + // Override-redirect windows cannot be iconified or restored, as those + // tasks are performed by the window manager + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Iconification of full screen windows requires a WM that supports EWMH full screen"); + return; + } + + XIconifyWindow(_glfw.x11.display, window->x11.handle, _glfw.x11.screen); + XFlush(_glfw.x11.display); +} + +void _glfwPlatformRestoreWindow(_GLFWwindow* window) +{ + if (window->x11.overrideRedirect) + { + // Override-redirect windows cannot be iconified or restored, as those + // tasks are performed by the window manager + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Iconification of full screen windows requires a WM that supports EWMH full screen"); + return; + } + + if (_glfwPlatformWindowIconified(window)) + { + XMapWindow(_glfw.x11.display, window->x11.handle); + waitForVisibilityNotify(window); + } + else if (_glfwPlatformWindowVisible(window)) + { + if (_glfw.x11.NET_WM_STATE && + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) + { + sendEventToWM(window, + _glfw.x11.NET_WM_STATE, + _NET_WM_STATE_REMOVE, + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ, + 1, 0); + } + } + + XFlush(_glfw.x11.display); +} + +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ + if (!_glfw.x11.NET_WM_STATE || + !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || + !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) + { + return; + } + + if (_glfwPlatformWindowVisible(window)) + { + sendEventToWM(window, + _glfw.x11.NET_WM_STATE, + _NET_WM_STATE_ADD, + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ, + 1, 0); + } + else + { + Atom* states = NULL; + unsigned long count = + _glfwGetWindowPropertyX11(window->x11.handle, + _glfw.x11.NET_WM_STATE, + XA_ATOM, + (unsigned char**) &states); + + // NOTE: We don't check for failure as this property may not exist yet + // and that's fine (and we'll create it implicitly with append) + + Atom missing[2] = + { + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ + }; + unsigned long missingCount = 2; + + for (unsigned long i = 0; i < count; i++) + { + for (unsigned long j = 0; j < missingCount; j++) + { + if (states[i] == missing[j]) + { + missing[j] = missing[missingCount - 1]; + missingCount--; + } + } + } + + if (states) + XFree(states); + + if (!missingCount) + return; + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_STATE, XA_ATOM, 32, + PropModeAppend, + (unsigned char*) missing, + missingCount); + } + + XFlush(_glfw.x11.display); +} + +void _glfwPlatformShowWindow(_GLFWwindow* window) +{ + if (_glfwPlatformWindowVisible(window)) + return; + + XMapWindow(_glfw.x11.display, window->x11.handle); + waitForVisibilityNotify(window); +} + +void _glfwPlatformHideWindow(_GLFWwindow* window) +{ + XUnmapWindow(_glfw.x11.display, window->x11.handle); + XFlush(_glfw.x11.display); +} + +void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) +{ + if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION) + return; + + sendEventToWM(window, + _glfw.x11.NET_WM_STATE, + _NET_WM_STATE_ADD, + _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION, + 0, 1, 0); +} + +void _glfwPlatformFocusWindow(_GLFWwindow* window) +{ + if (_glfw.x11.NET_ACTIVE_WINDOW) + sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0); + else if (_glfwPlatformWindowVisible(window)) + { + XRaiseWindow(_glfw.x11.display, window->x11.handle); + XSetInputFocus(_glfw.x11.display, window->x11.handle, + RevertToParent, CurrentTime); + } + + XFlush(_glfw.x11.display); +} + +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) +{ + if (window->monitor == monitor) + { + if (monitor) + { + if (monitor->window == window) + acquireMonitor(window); + } + else + { + if (!window->resizable) + updateNormalHints(window, width, height); + + XMoveResizeWindow(_glfw.x11.display, window->x11.handle, + xpos, ypos, width, height); + } + + XFlush(_glfw.x11.display); + return; + } + + if (window->monitor) + releaseMonitor(window); + + _glfwInputWindowMonitor(window, monitor); + updateNormalHints(window, width, height); + + if (window->monitor) + { + if (!_glfwPlatformWindowVisible(window)) + { + XMapRaised(_glfw.x11.display, window->x11.handle); + waitForVisibilityNotify(window); + } + + updateWindowMode(window); + acquireMonitor(window); + } + else + { + updateWindowMode(window); + XMoveResizeWindow(_glfw.x11.display, window->x11.handle, + xpos, ypos, width, height); + } + + XFlush(_glfw.x11.display); +} + +int _glfwPlatformWindowFocused(_GLFWwindow* window) +{ + Window focused; + int state; + + XGetInputFocus(_glfw.x11.display, &focused, &state); + return window->x11.handle == focused; +} + +int _glfwPlatformWindowIconified(_GLFWwindow* window) +{ + return getWindowState(window) == IconicState; +} + +int _glfwPlatformWindowVisible(_GLFWwindow* window) +{ + XWindowAttributes wa; + XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &wa); + return wa.map_state == IsViewable; +} + +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + Atom* states; + unsigned long i; + GLFWbool maximized = GLFW_FALSE; + + if (!_glfw.x11.NET_WM_STATE || + !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || + !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) + { + return maximized; + } + + const unsigned long count = + _glfwGetWindowPropertyX11(window->x11.handle, + _glfw.x11.NET_WM_STATE, + XA_ATOM, + (unsigned char**) &states); + + for (i = 0; i < count; i++) + { + if (states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || + states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) + { + maximized = GLFW_TRUE; + break; + } + } + + if (states) + XFree(states); + + return maximized; +} + +int _glfwPlatformWindowHovered(_GLFWwindow* window) +{ + Window w = _glfw.x11.root; + while (w) + { + Window root; + int rootX, rootY, childX, childY; + unsigned int mask; + + if (!XQueryPointer(_glfw.x11.display, w, + &root, &w, &rootX, &rootY, &childX, &childY, &mask)) + { + return GLFW_FALSE; + } + + if (w == window->x11.handle) + return GLFW_TRUE; + } + + return GLFW_FALSE; +} + +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) +{ + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + updateNormalHints(window, width, height); +} + +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +{ + struct + { + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long input_mode; + unsigned long status; + } hints = {0}; + + hints.flags = MWM_HINTS_DECORATIONS; + hints.decorations = enabled ? MWM_DECOR_ALL : 0; + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.MOTIF_WM_HINTS, + _glfw.x11.MOTIF_WM_HINTS, 32, + PropModeReplace, + (unsigned char*) &hints, + sizeof(hints) / sizeof(long)); +} + +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +{ + if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_ABOVE) + return; + + if (_glfwPlatformWindowVisible(window)) + { + const long action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; + sendEventToWM(window, + _glfw.x11.NET_WM_STATE, + action, + _glfw.x11.NET_WM_STATE_ABOVE, + 0, 1, 0); + } + else + { + Atom* states = NULL; + unsigned long i, count; + + count = _glfwGetWindowPropertyX11(window->x11.handle, + _glfw.x11.NET_WM_STATE, + XA_ATOM, + (unsigned char**) &states); + + // NOTE: We don't check for failure as this property may not exist yet + // and that's fine (and we'll create it implicitly with append) + + if (enabled) + { + for (i = 0; i < count; i++) + { + if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE) + break; + } + + if (i < count) + return; + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_STATE, XA_ATOM, 32, + PropModeAppend, + (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE, + 1); + } + else if (states) + { + for (i = 0; i < count; i++) + { + if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE) + break; + } + + if (i == count) + return; + + states[i] = states[count - 1]; + count--; + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_STATE, XA_ATOM, 32, + PropModeReplace, (unsigned char*) states, count); + } + + if (states) + XFree(states); + } + + 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 _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) +{ + if (!_glfw.x11.xi.available) + return; + + if (_glfw.x11.disabledCursorWindow != window) + return; + + if (enabled) + enableRawMouseMotion(window); + else + disableRawMouseMotion(window); +} + +GLFWbool _glfwPlatformRawMouseMotionSupported(void) +{ + return _glfw.x11.xi.available; +} + +void _glfwPlatformPollEvents(void) +{ + _GLFWwindow* window; + +#if defined(__linux__) + _glfwDetectJoystickConnectionLinux(); +#endif + XPending(_glfw.x11.display); + + while (XQLength(_glfw.x11.display)) + { + XEvent event; + XNextEvent(_glfw.x11.display, &event); + processEvent(&event); + } + + window = _glfw.x11.disabledCursorWindow; + if (window) + { + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + + // NOTE: Re-center the cursor only if it has moved since the last call, + // to avoid breaking glfwWaitEvents with MotionNotify + if (window->x11.lastCursorPosX != width / 2 || + window->x11.lastCursorPosY != height / 2) + { + _glfwPlatformSetCursorPos(window, width / 2, height / 2); + } + } + + XFlush(_glfw.x11.display); +} + +void _glfwPlatformWaitEvents(void) +{ + while (!XPending(_glfw.x11.display)) + waitForEvent(NULL); + + _glfwPlatformPollEvents(); +} + +void _glfwPlatformWaitEventsTimeout(double timeout) +{ + while (!XPending(_glfw.x11.display)) + { + if (!waitForEvent(&timeout)) + break; + } + + _glfwPlatformPollEvents(); +} + +void _glfwPlatformPostEmptyEvent(void) +{ + XEvent event = { ClientMessage }; + event.xclient.window = _glfw.x11.helperWindowHandle; + event.xclient.format = 32; // Data is 32-bit longs + event.xclient.message_type = _glfw.x11.NULL_; + + XSendEvent(_glfw.x11.display, _glfw.x11.helperWindowHandle, False, 0, &event); + XFlush(_glfw.x11.display); +} + +void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) +{ + Window root, child; + int rootX, rootY, childX, childY; + unsigned int mask; + + XQueryPointer(_glfw.x11.display, window->x11.handle, + &root, &child, + &rootX, &rootY, &childX, &childY, + &mask); + + if (xpos) + *xpos = childX; + if (ypos) + *ypos = childY; +} + +void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) +{ + // Store the new position so it can be recognized later + window->x11.warpCursorPosX = (int) x; + window->x11.warpCursorPosY = (int) y; + + XWarpPointer(_glfw.x11.display, None, window->x11.handle, + 0,0,0,0, (int) x, (int) y); + XFlush(_glfw.x11.display); +} + +void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) +{ + if (mode == GLFW_CURSOR_DISABLED) + { + if (_glfwPlatformWindowFocused(window)) + disableCursor(window); + } + else if (_glfw.x11.disabledCursorWindow == window) + enableCursor(window); + else + updateCursorImage(window); + + XFlush(_glfw.x11.display); +} + +const char* _glfwPlatformGetScancodeName(int scancode) +{ + if (!_glfw.x11.xkb.available) + return NULL; + + if (scancode < 0 || scancode > 0xff || + _glfw.x11.keycodes[scancode] == GLFW_KEY_UNKNOWN) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode"); + return NULL; + } + + const int key = _glfw.x11.keycodes[scancode]; + const KeySym keysym = XkbKeycodeToKeysym(_glfw.x11.display, + scancode, _glfw.x11.xkb.group, 0); + if (keysym == NoSymbol) + return NULL; + + const long ch = _glfwKeySym2Unicode(keysym); + if (ch == -1) + return NULL; + + const size_t count = encodeUTF8(_glfw.x11.keynames[key], (unsigned int) ch); + if (count == 0) + return NULL; + + _glfw.x11.keynames[key][count] = '\0'; + return _glfw.x11.keynames[key]; +} + +int _glfwPlatformGetKeyScancode(int key) +{ + return _glfw.x11.scancodes[key]; +} + +int _glfwPlatformCreateCursor(_GLFWcursor* cursor, + const GLFWimage* image, + int xhot, int yhot) +{ + cursor->x11.handle = _glfwCreateCursorX11(image, xhot, yhot); + if (!cursor->x11.handle) + return GLFW_FALSE; + + return GLFW_TRUE; +} + +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +{ + if (_glfw.x11.xcursor.handle) + { + char* theme = XcursorGetTheme(_glfw.x11.display); + if (theme) + { + const int size = XcursorGetDefaultSize(_glfw.x11.display); + const char* name = NULL; + + if (shape == GLFW_ARROW_CURSOR) + name = "default"; + else if (shape == GLFW_IBEAM_CURSOR) + name = "text"; + else if (shape == GLFW_CROSSHAIR_CURSOR) + name = "crosshair"; + else if (shape == GLFW_POINTING_HAND_CURSOR) + name = "pointer"; + else if (shape == GLFW_RESIZE_EW_CURSOR) + name = "ew-resize"; + else if (shape == GLFW_RESIZE_NS_CURSOR) + name = "ns-resize"; + else if (shape == GLFW_RESIZE_NWSE_CURSOR) + name = "nwse-resize"; + else if (shape == GLFW_RESIZE_NESW_CURSOR) + name = "nesw-resize"; + else if (shape == GLFW_RESIZE_ALL_CURSOR) + name = "all-scroll"; + else if (shape == GLFW_NOT_ALLOWED_CURSOR) + name = "not-allowed"; + + XcursorImage* image = XcursorLibraryLoadImage(name, theme, size); + if (image) + { + cursor->x11.handle = XcursorImageLoadCursor(_glfw.x11.display, image); + XcursorImageDestroy(image); + } + } + } + + if (!cursor->x11.handle) + { + unsigned int native = 0; + + if (shape == GLFW_ARROW_CURSOR) + native = XC_left_ptr; + else if (shape == GLFW_IBEAM_CURSOR) + native = XC_xterm; + else if (shape == GLFW_CROSSHAIR_CURSOR) + native = XC_crosshair; + else if (shape == GLFW_POINTING_HAND_CURSOR) + native = XC_hand2; + else if (shape == GLFW_RESIZE_EW_CURSOR) + native = XC_sb_h_double_arrow; + else if (shape == GLFW_RESIZE_NS_CURSOR) + native = XC_sb_v_double_arrow; + else if (shape == GLFW_RESIZE_ALL_CURSOR) + native = XC_fleur; + else + { + _glfwInputError(GLFW_CURSOR_UNAVAILABLE, + "X11: Standard cursor shape unavailable"); + return GLFW_FALSE; + } + + cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native); + if (!cursor->x11.handle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to create standard cursor"); + return GLFW_FALSE; + } + } + + return GLFW_TRUE; +} + +void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) +{ + if (cursor->x11.handle) + XFreeCursor(_glfw.x11.display, cursor->x11.handle); +} + +void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) +{ + if (window->cursorMode == GLFW_CURSOR_NORMAL) + { + updateCursorImage(window); + XFlush(_glfw.x11.display); + } +} + +void _glfwPlatformSetClipboardString(const char* string) +{ + free(_glfw.x11.clipboardString); + _glfw.x11.clipboardString = _glfw_strdup(string); + + XSetSelectionOwner(_glfw.x11.display, + _glfw.x11.CLIPBOARD, + _glfw.x11.helperWindowHandle, + CurrentTime); + + if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) != + _glfw.x11.helperWindowHandle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to become owner of clipboard selection"); + } +} + +const char* _glfwPlatformGetClipboardString(void) +{ + return getSelectionString(_glfw.x11.CLIPBOARD); +} + +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) +{ + if (!_glfw.vk.KHR_surface) + return; + + if (!_glfw.vk.KHR_xcb_surface || !_glfw.x11.x11xcb.handle) + { + if (!_glfw.vk.KHR_xlib_surface) + return; + } + + extensions[0] = "VK_KHR_surface"; + + // NOTE: VK_KHR_xcb_surface is preferred due to some early ICDs exposing but + // not correctly implementing VK_KHR_xlib_surface + if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) + extensions[1] = "VK_KHR_xcb_surface"; + else + extensions[1] = "VK_KHR_xlib_surface"; +} + +int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily) +{ + VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display, + _glfw.x11.screen)); + + if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) + { + PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR + vkGetPhysicalDeviceXcbPresentationSupportKHR = + (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR) + vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR"); + if (!vkGetPhysicalDeviceXcbPresentationSupportKHR) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "X11: Vulkan instance missing VK_KHR_xcb_surface extension"); + return GLFW_FALSE; + } + + xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display); + if (!connection) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to retrieve XCB connection"); + return GLFW_FALSE; + } + + return vkGetPhysicalDeviceXcbPresentationSupportKHR(device, + queuefamily, + connection, + visualID); + } + else + { + PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR + vkGetPhysicalDeviceXlibPresentationSupportKHR = + (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR) + vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR"); + if (!vkGetPhysicalDeviceXlibPresentationSupportKHR) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "X11: Vulkan instance missing VK_KHR_xlib_surface extension"); + return GLFW_FALSE; + } + + return vkGetPhysicalDeviceXlibPresentationSupportKHR(device, + queuefamily, + _glfw.x11.display, + visualID); + } +} + +VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, + _GLFWwindow* window, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) +{ + if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) + { + VkResult err; + VkXcbSurfaceCreateInfoKHR sci; + PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR; + + xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display); + if (!connection) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to retrieve XCB connection"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + vkCreateXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR) + vkGetInstanceProcAddr(instance, "vkCreateXcbSurfaceKHR"); + if (!vkCreateXcbSurfaceKHR) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "X11: Vulkan instance missing VK_KHR_xcb_surface extension"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + memset(&sci, 0, sizeof(sci)); + sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; + sci.connection = connection; + sci.window = window->x11.handle; + + err = vkCreateXcbSurfaceKHR(instance, &sci, allocator, surface); + if (err) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to create Vulkan XCB surface: %s", + _glfwGetVulkanResultString(err)); + } + + return err; + } + else + { + VkResult err; + VkXlibSurfaceCreateInfoKHR sci; + PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR; + + vkCreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR) + vkGetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR"); + if (!vkCreateXlibSurfaceKHR) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "X11: Vulkan instance missing VK_KHR_xlib_surface extension"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + memset(&sci, 0, sizeof(sci)); + sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + sci.dpy = _glfw.x11.display; + sci.window = window->x11.handle; + + err = vkCreateXlibSurfaceKHR(instance, &sci, allocator, surface); + if (err) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to create Vulkan X11 surface: %s", + _glfwGetVulkanResultString(err)); + } + + return err; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI Display* glfwGetX11Display(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return _glfw.x11.display; +} + +GLFWAPI Window glfwGetX11Window(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(None); + return window->x11.handle; +} + +GLFWAPI void glfwSetX11SelectionString(const char* string) +{ + _GLFW_REQUIRE_INIT(); + + free(_glfw.x11.primarySelectionString); + _glfw.x11.primarySelectionString = _glfw_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); +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/xkb_unicode.c b/source/MaterialXGraphEditor/External/Glfw/src/xkb_unicode.c new file mode 100644 index 0000000000..2772ea0984 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/xkb_unicode.c @@ -0,0 +1,942 @@ +//======================================================================== +// GLFW 3.4 X11 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2017 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// It is fine to use C99 in this file because it will not be built with VS +//======================================================================== + +#include "internal.h" + + +/* + * Marcus: This code was originally written by Markus G. Kuhn. + * I have made some slight changes (trimmed it down a bit from >60 KB to + * 20 KB), but the functionality is the same. + */ + +/* + * This module converts keysym values into the corresponding ISO 10646 + * (UCS, Unicode) values. + * + * The array keysymtab[] contains pairs of X11 keysym values for graphical + * characters and the corresponding Unicode value. The function + * _glfwKeySym2Unicode() maps a keysym onto a Unicode value using a binary + * search, therefore keysymtab[] must remain SORTED by keysym value. + * + * We allow to represent any UCS character in the range U-00000000 to + * U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff. + * This admittedly does not cover the entire 31-bit space of UCS, but + * it does cover all of the characters up to U-10FFFF, which can be + * represented by UTF-16, and more, and it is very unlikely that higher + * UCS codes will ever be assigned by ISO. So to get Unicode character + * U+ABCD you can directly use keysym 0x0100abcd. + * + * Original author: Markus G. Kuhn , University of + * Cambridge, April 2001 + * + * Special thanks to Richard Verhoeven for preparing + * an initial draft of the mapping table. + * + */ + + +//************************************************************************ +//**** KeySym to Unicode mapping table **** +//************************************************************************ + +static const struct codepair { + unsigned short keysym; + unsigned short ucs; +} keysymtab[] = { + { 0x01a1, 0x0104 }, + { 0x01a2, 0x02d8 }, + { 0x01a3, 0x0141 }, + { 0x01a5, 0x013d }, + { 0x01a6, 0x015a }, + { 0x01a9, 0x0160 }, + { 0x01aa, 0x015e }, + { 0x01ab, 0x0164 }, + { 0x01ac, 0x0179 }, + { 0x01ae, 0x017d }, + { 0x01af, 0x017b }, + { 0x01b1, 0x0105 }, + { 0x01b2, 0x02db }, + { 0x01b3, 0x0142 }, + { 0x01b5, 0x013e }, + { 0x01b6, 0x015b }, + { 0x01b7, 0x02c7 }, + { 0x01b9, 0x0161 }, + { 0x01ba, 0x015f }, + { 0x01bb, 0x0165 }, + { 0x01bc, 0x017a }, + { 0x01bd, 0x02dd }, + { 0x01be, 0x017e }, + { 0x01bf, 0x017c }, + { 0x01c0, 0x0154 }, + { 0x01c3, 0x0102 }, + { 0x01c5, 0x0139 }, + { 0x01c6, 0x0106 }, + { 0x01c8, 0x010c }, + { 0x01ca, 0x0118 }, + { 0x01cc, 0x011a }, + { 0x01cf, 0x010e }, + { 0x01d0, 0x0110 }, + { 0x01d1, 0x0143 }, + { 0x01d2, 0x0147 }, + { 0x01d5, 0x0150 }, + { 0x01d8, 0x0158 }, + { 0x01d9, 0x016e }, + { 0x01db, 0x0170 }, + { 0x01de, 0x0162 }, + { 0x01e0, 0x0155 }, + { 0x01e3, 0x0103 }, + { 0x01e5, 0x013a }, + { 0x01e6, 0x0107 }, + { 0x01e8, 0x010d }, + { 0x01ea, 0x0119 }, + { 0x01ec, 0x011b }, + { 0x01ef, 0x010f }, + { 0x01f0, 0x0111 }, + { 0x01f1, 0x0144 }, + { 0x01f2, 0x0148 }, + { 0x01f5, 0x0151 }, + { 0x01f8, 0x0159 }, + { 0x01f9, 0x016f }, + { 0x01fb, 0x0171 }, + { 0x01fe, 0x0163 }, + { 0x01ff, 0x02d9 }, + { 0x02a1, 0x0126 }, + { 0x02a6, 0x0124 }, + { 0x02a9, 0x0130 }, + { 0x02ab, 0x011e }, + { 0x02ac, 0x0134 }, + { 0x02b1, 0x0127 }, + { 0x02b6, 0x0125 }, + { 0x02b9, 0x0131 }, + { 0x02bb, 0x011f }, + { 0x02bc, 0x0135 }, + { 0x02c5, 0x010a }, + { 0x02c6, 0x0108 }, + { 0x02d5, 0x0120 }, + { 0x02d8, 0x011c }, + { 0x02dd, 0x016c }, + { 0x02de, 0x015c }, + { 0x02e5, 0x010b }, + { 0x02e6, 0x0109 }, + { 0x02f5, 0x0121 }, + { 0x02f8, 0x011d }, + { 0x02fd, 0x016d }, + { 0x02fe, 0x015d }, + { 0x03a2, 0x0138 }, + { 0x03a3, 0x0156 }, + { 0x03a5, 0x0128 }, + { 0x03a6, 0x013b }, + { 0x03aa, 0x0112 }, + { 0x03ab, 0x0122 }, + { 0x03ac, 0x0166 }, + { 0x03b3, 0x0157 }, + { 0x03b5, 0x0129 }, + { 0x03b6, 0x013c }, + { 0x03ba, 0x0113 }, + { 0x03bb, 0x0123 }, + { 0x03bc, 0x0167 }, + { 0x03bd, 0x014a }, + { 0x03bf, 0x014b }, + { 0x03c0, 0x0100 }, + { 0x03c7, 0x012e }, + { 0x03cc, 0x0116 }, + { 0x03cf, 0x012a }, + { 0x03d1, 0x0145 }, + { 0x03d2, 0x014c }, + { 0x03d3, 0x0136 }, + { 0x03d9, 0x0172 }, + { 0x03dd, 0x0168 }, + { 0x03de, 0x016a }, + { 0x03e0, 0x0101 }, + { 0x03e7, 0x012f }, + { 0x03ec, 0x0117 }, + { 0x03ef, 0x012b }, + { 0x03f1, 0x0146 }, + { 0x03f2, 0x014d }, + { 0x03f3, 0x0137 }, + { 0x03f9, 0x0173 }, + { 0x03fd, 0x0169 }, + { 0x03fe, 0x016b }, + { 0x047e, 0x203e }, + { 0x04a1, 0x3002 }, + { 0x04a2, 0x300c }, + { 0x04a3, 0x300d }, + { 0x04a4, 0x3001 }, + { 0x04a5, 0x30fb }, + { 0x04a6, 0x30f2 }, + { 0x04a7, 0x30a1 }, + { 0x04a8, 0x30a3 }, + { 0x04a9, 0x30a5 }, + { 0x04aa, 0x30a7 }, + { 0x04ab, 0x30a9 }, + { 0x04ac, 0x30e3 }, + { 0x04ad, 0x30e5 }, + { 0x04ae, 0x30e7 }, + { 0x04af, 0x30c3 }, + { 0x04b0, 0x30fc }, + { 0x04b1, 0x30a2 }, + { 0x04b2, 0x30a4 }, + { 0x04b3, 0x30a6 }, + { 0x04b4, 0x30a8 }, + { 0x04b5, 0x30aa }, + { 0x04b6, 0x30ab }, + { 0x04b7, 0x30ad }, + { 0x04b8, 0x30af }, + { 0x04b9, 0x30b1 }, + { 0x04ba, 0x30b3 }, + { 0x04bb, 0x30b5 }, + { 0x04bc, 0x30b7 }, + { 0x04bd, 0x30b9 }, + { 0x04be, 0x30bb }, + { 0x04bf, 0x30bd }, + { 0x04c0, 0x30bf }, + { 0x04c1, 0x30c1 }, + { 0x04c2, 0x30c4 }, + { 0x04c3, 0x30c6 }, + { 0x04c4, 0x30c8 }, + { 0x04c5, 0x30ca }, + { 0x04c6, 0x30cb }, + { 0x04c7, 0x30cc }, + { 0x04c8, 0x30cd }, + { 0x04c9, 0x30ce }, + { 0x04ca, 0x30cf }, + { 0x04cb, 0x30d2 }, + { 0x04cc, 0x30d5 }, + { 0x04cd, 0x30d8 }, + { 0x04ce, 0x30db }, + { 0x04cf, 0x30de }, + { 0x04d0, 0x30df }, + { 0x04d1, 0x30e0 }, + { 0x04d2, 0x30e1 }, + { 0x04d3, 0x30e2 }, + { 0x04d4, 0x30e4 }, + { 0x04d5, 0x30e6 }, + { 0x04d6, 0x30e8 }, + { 0x04d7, 0x30e9 }, + { 0x04d8, 0x30ea }, + { 0x04d9, 0x30eb }, + { 0x04da, 0x30ec }, + { 0x04db, 0x30ed }, + { 0x04dc, 0x30ef }, + { 0x04dd, 0x30f3 }, + { 0x04de, 0x309b }, + { 0x04df, 0x309c }, + { 0x05ac, 0x060c }, + { 0x05bb, 0x061b }, + { 0x05bf, 0x061f }, + { 0x05c1, 0x0621 }, + { 0x05c2, 0x0622 }, + { 0x05c3, 0x0623 }, + { 0x05c4, 0x0624 }, + { 0x05c5, 0x0625 }, + { 0x05c6, 0x0626 }, + { 0x05c7, 0x0627 }, + { 0x05c8, 0x0628 }, + { 0x05c9, 0x0629 }, + { 0x05ca, 0x062a }, + { 0x05cb, 0x062b }, + { 0x05cc, 0x062c }, + { 0x05cd, 0x062d }, + { 0x05ce, 0x062e }, + { 0x05cf, 0x062f }, + { 0x05d0, 0x0630 }, + { 0x05d1, 0x0631 }, + { 0x05d2, 0x0632 }, + { 0x05d3, 0x0633 }, + { 0x05d4, 0x0634 }, + { 0x05d5, 0x0635 }, + { 0x05d6, 0x0636 }, + { 0x05d7, 0x0637 }, + { 0x05d8, 0x0638 }, + { 0x05d9, 0x0639 }, + { 0x05da, 0x063a }, + { 0x05e0, 0x0640 }, + { 0x05e1, 0x0641 }, + { 0x05e2, 0x0642 }, + { 0x05e3, 0x0643 }, + { 0x05e4, 0x0644 }, + { 0x05e5, 0x0645 }, + { 0x05e6, 0x0646 }, + { 0x05e7, 0x0647 }, + { 0x05e8, 0x0648 }, + { 0x05e9, 0x0649 }, + { 0x05ea, 0x064a }, + { 0x05eb, 0x064b }, + { 0x05ec, 0x064c }, + { 0x05ed, 0x064d }, + { 0x05ee, 0x064e }, + { 0x05ef, 0x064f }, + { 0x05f0, 0x0650 }, + { 0x05f1, 0x0651 }, + { 0x05f2, 0x0652 }, + { 0x06a1, 0x0452 }, + { 0x06a2, 0x0453 }, + { 0x06a3, 0x0451 }, + { 0x06a4, 0x0454 }, + { 0x06a5, 0x0455 }, + { 0x06a6, 0x0456 }, + { 0x06a7, 0x0457 }, + { 0x06a8, 0x0458 }, + { 0x06a9, 0x0459 }, + { 0x06aa, 0x045a }, + { 0x06ab, 0x045b }, + { 0x06ac, 0x045c }, + { 0x06ae, 0x045e }, + { 0x06af, 0x045f }, + { 0x06b0, 0x2116 }, + { 0x06b1, 0x0402 }, + { 0x06b2, 0x0403 }, + { 0x06b3, 0x0401 }, + { 0x06b4, 0x0404 }, + { 0x06b5, 0x0405 }, + { 0x06b6, 0x0406 }, + { 0x06b7, 0x0407 }, + { 0x06b8, 0x0408 }, + { 0x06b9, 0x0409 }, + { 0x06ba, 0x040a }, + { 0x06bb, 0x040b }, + { 0x06bc, 0x040c }, + { 0x06be, 0x040e }, + { 0x06bf, 0x040f }, + { 0x06c0, 0x044e }, + { 0x06c1, 0x0430 }, + { 0x06c2, 0x0431 }, + { 0x06c3, 0x0446 }, + { 0x06c4, 0x0434 }, + { 0x06c5, 0x0435 }, + { 0x06c6, 0x0444 }, + { 0x06c7, 0x0433 }, + { 0x06c8, 0x0445 }, + { 0x06c9, 0x0438 }, + { 0x06ca, 0x0439 }, + { 0x06cb, 0x043a }, + { 0x06cc, 0x043b }, + { 0x06cd, 0x043c }, + { 0x06ce, 0x043d }, + { 0x06cf, 0x043e }, + { 0x06d0, 0x043f }, + { 0x06d1, 0x044f }, + { 0x06d2, 0x0440 }, + { 0x06d3, 0x0441 }, + { 0x06d4, 0x0442 }, + { 0x06d5, 0x0443 }, + { 0x06d6, 0x0436 }, + { 0x06d7, 0x0432 }, + { 0x06d8, 0x044c }, + { 0x06d9, 0x044b }, + { 0x06da, 0x0437 }, + { 0x06db, 0x0448 }, + { 0x06dc, 0x044d }, + { 0x06dd, 0x0449 }, + { 0x06de, 0x0447 }, + { 0x06df, 0x044a }, + { 0x06e0, 0x042e }, + { 0x06e1, 0x0410 }, + { 0x06e2, 0x0411 }, + { 0x06e3, 0x0426 }, + { 0x06e4, 0x0414 }, + { 0x06e5, 0x0415 }, + { 0x06e6, 0x0424 }, + { 0x06e7, 0x0413 }, + { 0x06e8, 0x0425 }, + { 0x06e9, 0x0418 }, + { 0x06ea, 0x0419 }, + { 0x06eb, 0x041a }, + { 0x06ec, 0x041b }, + { 0x06ed, 0x041c }, + { 0x06ee, 0x041d }, + { 0x06ef, 0x041e }, + { 0x06f0, 0x041f }, + { 0x06f1, 0x042f }, + { 0x06f2, 0x0420 }, + { 0x06f3, 0x0421 }, + { 0x06f4, 0x0422 }, + { 0x06f5, 0x0423 }, + { 0x06f6, 0x0416 }, + { 0x06f7, 0x0412 }, + { 0x06f8, 0x042c }, + { 0x06f9, 0x042b }, + { 0x06fa, 0x0417 }, + { 0x06fb, 0x0428 }, + { 0x06fc, 0x042d }, + { 0x06fd, 0x0429 }, + { 0x06fe, 0x0427 }, + { 0x06ff, 0x042a }, + { 0x07a1, 0x0386 }, + { 0x07a2, 0x0388 }, + { 0x07a3, 0x0389 }, + { 0x07a4, 0x038a }, + { 0x07a5, 0x03aa }, + { 0x07a7, 0x038c }, + { 0x07a8, 0x038e }, + { 0x07a9, 0x03ab }, + { 0x07ab, 0x038f }, + { 0x07ae, 0x0385 }, + { 0x07af, 0x2015 }, + { 0x07b1, 0x03ac }, + { 0x07b2, 0x03ad }, + { 0x07b3, 0x03ae }, + { 0x07b4, 0x03af }, + { 0x07b5, 0x03ca }, + { 0x07b6, 0x0390 }, + { 0x07b7, 0x03cc }, + { 0x07b8, 0x03cd }, + { 0x07b9, 0x03cb }, + { 0x07ba, 0x03b0 }, + { 0x07bb, 0x03ce }, + { 0x07c1, 0x0391 }, + { 0x07c2, 0x0392 }, + { 0x07c3, 0x0393 }, + { 0x07c4, 0x0394 }, + { 0x07c5, 0x0395 }, + { 0x07c6, 0x0396 }, + { 0x07c7, 0x0397 }, + { 0x07c8, 0x0398 }, + { 0x07c9, 0x0399 }, + { 0x07ca, 0x039a }, + { 0x07cb, 0x039b }, + { 0x07cc, 0x039c }, + { 0x07cd, 0x039d }, + { 0x07ce, 0x039e }, + { 0x07cf, 0x039f }, + { 0x07d0, 0x03a0 }, + { 0x07d1, 0x03a1 }, + { 0x07d2, 0x03a3 }, + { 0x07d4, 0x03a4 }, + { 0x07d5, 0x03a5 }, + { 0x07d6, 0x03a6 }, + { 0x07d7, 0x03a7 }, + { 0x07d8, 0x03a8 }, + { 0x07d9, 0x03a9 }, + { 0x07e1, 0x03b1 }, + { 0x07e2, 0x03b2 }, + { 0x07e3, 0x03b3 }, + { 0x07e4, 0x03b4 }, + { 0x07e5, 0x03b5 }, + { 0x07e6, 0x03b6 }, + { 0x07e7, 0x03b7 }, + { 0x07e8, 0x03b8 }, + { 0x07e9, 0x03b9 }, + { 0x07ea, 0x03ba }, + { 0x07eb, 0x03bb }, + { 0x07ec, 0x03bc }, + { 0x07ed, 0x03bd }, + { 0x07ee, 0x03be }, + { 0x07ef, 0x03bf }, + { 0x07f0, 0x03c0 }, + { 0x07f1, 0x03c1 }, + { 0x07f2, 0x03c3 }, + { 0x07f3, 0x03c2 }, + { 0x07f4, 0x03c4 }, + { 0x07f5, 0x03c5 }, + { 0x07f6, 0x03c6 }, + { 0x07f7, 0x03c7 }, + { 0x07f8, 0x03c8 }, + { 0x07f9, 0x03c9 }, + { 0x08a1, 0x23b7 }, + { 0x08a2, 0x250c }, + { 0x08a3, 0x2500 }, + { 0x08a4, 0x2320 }, + { 0x08a5, 0x2321 }, + { 0x08a6, 0x2502 }, + { 0x08a7, 0x23a1 }, + { 0x08a8, 0x23a3 }, + { 0x08a9, 0x23a4 }, + { 0x08aa, 0x23a6 }, + { 0x08ab, 0x239b }, + { 0x08ac, 0x239d }, + { 0x08ad, 0x239e }, + { 0x08ae, 0x23a0 }, + { 0x08af, 0x23a8 }, + { 0x08b0, 0x23ac }, + { 0x08bc, 0x2264 }, + { 0x08bd, 0x2260 }, + { 0x08be, 0x2265 }, + { 0x08bf, 0x222b }, + { 0x08c0, 0x2234 }, + { 0x08c1, 0x221d }, + { 0x08c2, 0x221e }, + { 0x08c5, 0x2207 }, + { 0x08c8, 0x223c }, + { 0x08c9, 0x2243 }, + { 0x08cd, 0x21d4 }, + { 0x08ce, 0x21d2 }, + { 0x08cf, 0x2261 }, + { 0x08d6, 0x221a }, + { 0x08da, 0x2282 }, + { 0x08db, 0x2283 }, + { 0x08dc, 0x2229 }, + { 0x08dd, 0x222a }, + { 0x08de, 0x2227 }, + { 0x08df, 0x2228 }, + { 0x08ef, 0x2202 }, + { 0x08f6, 0x0192 }, + { 0x08fb, 0x2190 }, + { 0x08fc, 0x2191 }, + { 0x08fd, 0x2192 }, + { 0x08fe, 0x2193 }, + { 0x09e0, 0x25c6 }, + { 0x09e1, 0x2592 }, + { 0x09e2, 0x2409 }, + { 0x09e3, 0x240c }, + { 0x09e4, 0x240d }, + { 0x09e5, 0x240a }, + { 0x09e8, 0x2424 }, + { 0x09e9, 0x240b }, + { 0x09ea, 0x2518 }, + { 0x09eb, 0x2510 }, + { 0x09ec, 0x250c }, + { 0x09ed, 0x2514 }, + { 0x09ee, 0x253c }, + { 0x09ef, 0x23ba }, + { 0x09f0, 0x23bb }, + { 0x09f1, 0x2500 }, + { 0x09f2, 0x23bc }, + { 0x09f3, 0x23bd }, + { 0x09f4, 0x251c }, + { 0x09f5, 0x2524 }, + { 0x09f6, 0x2534 }, + { 0x09f7, 0x252c }, + { 0x09f8, 0x2502 }, + { 0x0aa1, 0x2003 }, + { 0x0aa2, 0x2002 }, + { 0x0aa3, 0x2004 }, + { 0x0aa4, 0x2005 }, + { 0x0aa5, 0x2007 }, + { 0x0aa6, 0x2008 }, + { 0x0aa7, 0x2009 }, + { 0x0aa8, 0x200a }, + { 0x0aa9, 0x2014 }, + { 0x0aaa, 0x2013 }, + { 0x0aae, 0x2026 }, + { 0x0aaf, 0x2025 }, + { 0x0ab0, 0x2153 }, + { 0x0ab1, 0x2154 }, + { 0x0ab2, 0x2155 }, + { 0x0ab3, 0x2156 }, + { 0x0ab4, 0x2157 }, + { 0x0ab5, 0x2158 }, + { 0x0ab6, 0x2159 }, + { 0x0ab7, 0x215a }, + { 0x0ab8, 0x2105 }, + { 0x0abb, 0x2012 }, + { 0x0abc, 0x2329 }, + { 0x0abe, 0x232a }, + { 0x0ac3, 0x215b }, + { 0x0ac4, 0x215c }, + { 0x0ac5, 0x215d }, + { 0x0ac6, 0x215e }, + { 0x0ac9, 0x2122 }, + { 0x0aca, 0x2613 }, + { 0x0acc, 0x25c1 }, + { 0x0acd, 0x25b7 }, + { 0x0ace, 0x25cb }, + { 0x0acf, 0x25af }, + { 0x0ad0, 0x2018 }, + { 0x0ad1, 0x2019 }, + { 0x0ad2, 0x201c }, + { 0x0ad3, 0x201d }, + { 0x0ad4, 0x211e }, + { 0x0ad6, 0x2032 }, + { 0x0ad7, 0x2033 }, + { 0x0ad9, 0x271d }, + { 0x0adb, 0x25ac }, + { 0x0adc, 0x25c0 }, + { 0x0add, 0x25b6 }, + { 0x0ade, 0x25cf }, + { 0x0adf, 0x25ae }, + { 0x0ae0, 0x25e6 }, + { 0x0ae1, 0x25ab }, + { 0x0ae2, 0x25ad }, + { 0x0ae3, 0x25b3 }, + { 0x0ae4, 0x25bd }, + { 0x0ae5, 0x2606 }, + { 0x0ae6, 0x2022 }, + { 0x0ae7, 0x25aa }, + { 0x0ae8, 0x25b2 }, + { 0x0ae9, 0x25bc }, + { 0x0aea, 0x261c }, + { 0x0aeb, 0x261e }, + { 0x0aec, 0x2663 }, + { 0x0aed, 0x2666 }, + { 0x0aee, 0x2665 }, + { 0x0af0, 0x2720 }, + { 0x0af1, 0x2020 }, + { 0x0af2, 0x2021 }, + { 0x0af3, 0x2713 }, + { 0x0af4, 0x2717 }, + { 0x0af5, 0x266f }, + { 0x0af6, 0x266d }, + { 0x0af7, 0x2642 }, + { 0x0af8, 0x2640 }, + { 0x0af9, 0x260e }, + { 0x0afa, 0x2315 }, + { 0x0afb, 0x2117 }, + { 0x0afc, 0x2038 }, + { 0x0afd, 0x201a }, + { 0x0afe, 0x201e }, + { 0x0ba3, 0x003c }, + { 0x0ba6, 0x003e }, + { 0x0ba8, 0x2228 }, + { 0x0ba9, 0x2227 }, + { 0x0bc0, 0x00af }, + { 0x0bc2, 0x22a5 }, + { 0x0bc3, 0x2229 }, + { 0x0bc4, 0x230a }, + { 0x0bc6, 0x005f }, + { 0x0bca, 0x2218 }, + { 0x0bcc, 0x2395 }, + { 0x0bce, 0x22a4 }, + { 0x0bcf, 0x25cb }, + { 0x0bd3, 0x2308 }, + { 0x0bd6, 0x222a }, + { 0x0bd8, 0x2283 }, + { 0x0bda, 0x2282 }, + { 0x0bdc, 0x22a2 }, + { 0x0bfc, 0x22a3 }, + { 0x0cdf, 0x2017 }, + { 0x0ce0, 0x05d0 }, + { 0x0ce1, 0x05d1 }, + { 0x0ce2, 0x05d2 }, + { 0x0ce3, 0x05d3 }, + { 0x0ce4, 0x05d4 }, + { 0x0ce5, 0x05d5 }, + { 0x0ce6, 0x05d6 }, + { 0x0ce7, 0x05d7 }, + { 0x0ce8, 0x05d8 }, + { 0x0ce9, 0x05d9 }, + { 0x0cea, 0x05da }, + { 0x0ceb, 0x05db }, + { 0x0cec, 0x05dc }, + { 0x0ced, 0x05dd }, + { 0x0cee, 0x05de }, + { 0x0cef, 0x05df }, + { 0x0cf0, 0x05e0 }, + { 0x0cf1, 0x05e1 }, + { 0x0cf2, 0x05e2 }, + { 0x0cf3, 0x05e3 }, + { 0x0cf4, 0x05e4 }, + { 0x0cf5, 0x05e5 }, + { 0x0cf6, 0x05e6 }, + { 0x0cf7, 0x05e7 }, + { 0x0cf8, 0x05e8 }, + { 0x0cf9, 0x05e9 }, + { 0x0cfa, 0x05ea }, + { 0x0da1, 0x0e01 }, + { 0x0da2, 0x0e02 }, + { 0x0da3, 0x0e03 }, + { 0x0da4, 0x0e04 }, + { 0x0da5, 0x0e05 }, + { 0x0da6, 0x0e06 }, + { 0x0da7, 0x0e07 }, + { 0x0da8, 0x0e08 }, + { 0x0da9, 0x0e09 }, + { 0x0daa, 0x0e0a }, + { 0x0dab, 0x0e0b }, + { 0x0dac, 0x0e0c }, + { 0x0dad, 0x0e0d }, + { 0x0dae, 0x0e0e }, + { 0x0daf, 0x0e0f }, + { 0x0db0, 0x0e10 }, + { 0x0db1, 0x0e11 }, + { 0x0db2, 0x0e12 }, + { 0x0db3, 0x0e13 }, + { 0x0db4, 0x0e14 }, + { 0x0db5, 0x0e15 }, + { 0x0db6, 0x0e16 }, + { 0x0db7, 0x0e17 }, + { 0x0db8, 0x0e18 }, + { 0x0db9, 0x0e19 }, + { 0x0dba, 0x0e1a }, + { 0x0dbb, 0x0e1b }, + { 0x0dbc, 0x0e1c }, + { 0x0dbd, 0x0e1d }, + { 0x0dbe, 0x0e1e }, + { 0x0dbf, 0x0e1f }, + { 0x0dc0, 0x0e20 }, + { 0x0dc1, 0x0e21 }, + { 0x0dc2, 0x0e22 }, + { 0x0dc3, 0x0e23 }, + { 0x0dc4, 0x0e24 }, + { 0x0dc5, 0x0e25 }, + { 0x0dc6, 0x0e26 }, + { 0x0dc7, 0x0e27 }, + { 0x0dc8, 0x0e28 }, + { 0x0dc9, 0x0e29 }, + { 0x0dca, 0x0e2a }, + { 0x0dcb, 0x0e2b }, + { 0x0dcc, 0x0e2c }, + { 0x0dcd, 0x0e2d }, + { 0x0dce, 0x0e2e }, + { 0x0dcf, 0x0e2f }, + { 0x0dd0, 0x0e30 }, + { 0x0dd1, 0x0e31 }, + { 0x0dd2, 0x0e32 }, + { 0x0dd3, 0x0e33 }, + { 0x0dd4, 0x0e34 }, + { 0x0dd5, 0x0e35 }, + { 0x0dd6, 0x0e36 }, + { 0x0dd7, 0x0e37 }, + { 0x0dd8, 0x0e38 }, + { 0x0dd9, 0x0e39 }, + { 0x0dda, 0x0e3a }, + { 0x0ddf, 0x0e3f }, + { 0x0de0, 0x0e40 }, + { 0x0de1, 0x0e41 }, + { 0x0de2, 0x0e42 }, + { 0x0de3, 0x0e43 }, + { 0x0de4, 0x0e44 }, + { 0x0de5, 0x0e45 }, + { 0x0de6, 0x0e46 }, + { 0x0de7, 0x0e47 }, + { 0x0de8, 0x0e48 }, + { 0x0de9, 0x0e49 }, + { 0x0dea, 0x0e4a }, + { 0x0deb, 0x0e4b }, + { 0x0dec, 0x0e4c }, + { 0x0ded, 0x0e4d }, + { 0x0df0, 0x0e50 }, + { 0x0df1, 0x0e51 }, + { 0x0df2, 0x0e52 }, + { 0x0df3, 0x0e53 }, + { 0x0df4, 0x0e54 }, + { 0x0df5, 0x0e55 }, + { 0x0df6, 0x0e56 }, + { 0x0df7, 0x0e57 }, + { 0x0df8, 0x0e58 }, + { 0x0df9, 0x0e59 }, + { 0x0ea1, 0x3131 }, + { 0x0ea2, 0x3132 }, + { 0x0ea3, 0x3133 }, + { 0x0ea4, 0x3134 }, + { 0x0ea5, 0x3135 }, + { 0x0ea6, 0x3136 }, + { 0x0ea7, 0x3137 }, + { 0x0ea8, 0x3138 }, + { 0x0ea9, 0x3139 }, + { 0x0eaa, 0x313a }, + { 0x0eab, 0x313b }, + { 0x0eac, 0x313c }, + { 0x0ead, 0x313d }, + { 0x0eae, 0x313e }, + { 0x0eaf, 0x313f }, + { 0x0eb0, 0x3140 }, + { 0x0eb1, 0x3141 }, + { 0x0eb2, 0x3142 }, + { 0x0eb3, 0x3143 }, + { 0x0eb4, 0x3144 }, + { 0x0eb5, 0x3145 }, + { 0x0eb6, 0x3146 }, + { 0x0eb7, 0x3147 }, + { 0x0eb8, 0x3148 }, + { 0x0eb9, 0x3149 }, + { 0x0eba, 0x314a }, + { 0x0ebb, 0x314b }, + { 0x0ebc, 0x314c }, + { 0x0ebd, 0x314d }, + { 0x0ebe, 0x314e }, + { 0x0ebf, 0x314f }, + { 0x0ec0, 0x3150 }, + { 0x0ec1, 0x3151 }, + { 0x0ec2, 0x3152 }, + { 0x0ec3, 0x3153 }, + { 0x0ec4, 0x3154 }, + { 0x0ec5, 0x3155 }, + { 0x0ec6, 0x3156 }, + { 0x0ec7, 0x3157 }, + { 0x0ec8, 0x3158 }, + { 0x0ec9, 0x3159 }, + { 0x0eca, 0x315a }, + { 0x0ecb, 0x315b }, + { 0x0ecc, 0x315c }, + { 0x0ecd, 0x315d }, + { 0x0ece, 0x315e }, + { 0x0ecf, 0x315f }, + { 0x0ed0, 0x3160 }, + { 0x0ed1, 0x3161 }, + { 0x0ed2, 0x3162 }, + { 0x0ed3, 0x3163 }, + { 0x0ed4, 0x11a8 }, + { 0x0ed5, 0x11a9 }, + { 0x0ed6, 0x11aa }, + { 0x0ed7, 0x11ab }, + { 0x0ed8, 0x11ac }, + { 0x0ed9, 0x11ad }, + { 0x0eda, 0x11ae }, + { 0x0edb, 0x11af }, + { 0x0edc, 0x11b0 }, + { 0x0edd, 0x11b1 }, + { 0x0ede, 0x11b2 }, + { 0x0edf, 0x11b3 }, + { 0x0ee0, 0x11b4 }, + { 0x0ee1, 0x11b5 }, + { 0x0ee2, 0x11b6 }, + { 0x0ee3, 0x11b7 }, + { 0x0ee4, 0x11b8 }, + { 0x0ee5, 0x11b9 }, + { 0x0ee6, 0x11ba }, + { 0x0ee7, 0x11bb }, + { 0x0ee8, 0x11bc }, + { 0x0ee9, 0x11bd }, + { 0x0eea, 0x11be }, + { 0x0eeb, 0x11bf }, + { 0x0eec, 0x11c0 }, + { 0x0eed, 0x11c1 }, + { 0x0eee, 0x11c2 }, + { 0x0eef, 0x316d }, + { 0x0ef0, 0x3171 }, + { 0x0ef1, 0x3178 }, + { 0x0ef2, 0x317f }, + { 0x0ef3, 0x3181 }, + { 0x0ef4, 0x3184 }, + { 0x0ef5, 0x3186 }, + { 0x0ef6, 0x318d }, + { 0x0ef7, 0x318e }, + { 0x0ef8, 0x11eb }, + { 0x0ef9, 0x11f0 }, + { 0x0efa, 0x11f9 }, + { 0x0eff, 0x20a9 }, + { 0x13a4, 0x20ac }, + { 0x13bc, 0x0152 }, + { 0x13bd, 0x0153 }, + { 0x13be, 0x0178 }, + { 0x20ac, 0x20ac }, + { 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*/, ' ' }, + { 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*/, '*' }, + { 0xffab /*XKB_KEY_KP_Add*/, '+' }, + { 0xffac /*XKB_KEY_KP_Separator*/, ',' }, + { 0xffad /*XKB_KEY_KP_Subtract*/, '-' }, + { 0xffae /*XKB_KEY_KP_Decimal*/, '.' }, + { 0xffaf /*XKB_KEY_KP_Divide*/, '/' }, + { 0xffb0 /*XKB_KEY_KP_0*/, 0x0030 }, + { 0xffb1 /*XKB_KEY_KP_1*/, 0x0031 }, + { 0xffb2 /*XKB_KEY_KP_2*/, 0x0032 }, + { 0xffb3 /*XKB_KEY_KP_3*/, 0x0033 }, + { 0xffb4 /*XKB_KEY_KP_4*/, 0x0034 }, + { 0xffb5 /*XKB_KEY_KP_5*/, 0x0035 }, + { 0xffb6 /*XKB_KEY_KP_6*/, 0x0036 }, + { 0xffb7 /*XKB_KEY_KP_7*/, 0x0037 }, + { 0xffb8 /*XKB_KEY_KP_8*/, 0x0038 }, + { 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 }, + { 0xffbd /*XKB_KEY_KP_Equal*/, '=' } +}; + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Convert XKB KeySym to Unicode +// +long _glfwKeySym2Unicode(unsigned int keysym) +{ + int min = 0; + int max = sizeof(keysymtab) / sizeof(struct codepair) - 1; + int mid; + + // First check for Latin-1 characters (1:1 mapping) + if ((keysym >= 0x0020 && keysym <= 0x007e) || + (keysym >= 0x00a0 && keysym <= 0x00ff)) + { + return keysym; + } + + // Also check for directly encoded 24-bit UCS characters + if ((keysym & 0xff000000) == 0x01000000) + return keysym & 0x00ffffff; + + // Binary search in table + while (max >= min) + { + mid = (min + max) / 2; + if (keysymtab[mid].keysym < keysym) + min = mid + 1; + else if (keysymtab[mid].keysym > keysym) + max = mid - 1; + else + return keysymtab[mid].ucs; + } + + // No matching Unicode value found + return -1; +} + diff --git a/source/MaterialXGraphEditor/External/Glfw/src/xkb_unicode.h b/source/MaterialXGraphEditor/External/Glfw/src/xkb_unicode.h new file mode 100644 index 0000000000..76d83ffd15 --- /dev/null +++ b/source/MaterialXGraphEditor/External/Glfw/src/xkb_unicode.h @@ -0,0 +1,28 @@ +//======================================================================== +// GLFW 3.4 Linux - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2014 Jonas Ã…dahl +// +// 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. +// +//======================================================================== + +long _glfwKeySym2Unicode(unsigned int keysym); + diff --git a/source/MaterialXGraphEditor/External/ImGui b/source/MaterialXGraphEditor/External/ImGui new file mode 160000 index 0000000000..9aae45eb4a --- /dev/null +++ b/source/MaterialXGraphEditor/External/ImGui @@ -0,0 +1 @@ +Subproject commit 9aae45eb4a05a5a1f96be1ef37eb503a12ceb889 diff --git a/source/MaterialXGraphEditor/External/ImGuiFileBrowser b/source/MaterialXGraphEditor/External/ImGuiFileBrowser new file mode 160000 index 0000000000..cfccc2aab6 --- /dev/null +++ b/source/MaterialXGraphEditor/External/ImGuiFileBrowser @@ -0,0 +1 @@ +Subproject commit cfccc2aab651cb19cbc2c3ad36be78c36078ec76 diff --git a/source/MaterialXGraphEditor/External/ImGuiNodeEditor b/source/MaterialXGraphEditor/External/ImGuiNodeEditor new file mode 160000 index 0000000000..2f99b2d613 --- /dev/null +++ b/source/MaterialXGraphEditor/External/ImGuiNodeEditor @@ -0,0 +1 @@ +Subproject commit 2f99b2d613a400f6579762bd7e7c343a0d844158 diff --git a/source/MaterialXGraphEditor/Graph.cpp b/source/MaterialXGraphEditor/Graph.cpp new file mode 100644 index 0000000000..12b3901881 --- /dev/null +++ b/source/MaterialXGraphEditor/Graph.cpp @@ -0,0 +1,3811 @@ +// +// Copyright Contributors to the MaterialX Project +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include + +#include + +namespace +{ + +// the default node size is based off the of the size of the dot_color3 node using ed::getNodeSize() on that node +const ImVec2 DEFAULT_NODE_SIZE = ImVec2(138, 116); + +const int DEFAULT_ALPHA = 255; +const int FILTER_ALPHA = 50; + +// Function based off ImRect_Expanded function from ImGui Node Editor blueprints-example.cpp +ImRect expandImRect(const ImRect& rect, float x, float y) +{ + ImRect result = rect; + result.Min.x -= x; + result.Min.y -= y; + result.Max.x += x; + result.Max.y += y; + return result; +} + +} // anonymous namespace + +Graph::Graph(const std::string& materialFilename, const mx::FileSearchPath& searchPath, const mx::FilePathVec& libraryFolders) : + _materialFilename(materialFilename), + _searchPath(searchPath), + _libraryFolders(libraryFolders), + _initial(false), + _delete(false), + _fileDialogSave(ImGuiFileBrowserFlags_EnterNewFilename | ImGuiFileBrowserFlags_CreateNewDir), + _isNodeGraph(false), + _graphTotalSize(0), + _popup(false), + _shaderPopup(false), + _searchNodeId(-1), + _addNewNode(false), + _ctrlClick(false), + _isCut(false), + _autoLayout(false), + _frameCount(INT_MIN), + _pinFilterType(mx::EMPTY_STRING) +{ +} + +void Graph::loadStandardLibraries() +{ + // Initialize the standard library. + try + { + _stdLib = mx::createDocument(); + _xincludeFiles = mx::loadLibraries(_libraryFolders, _searchPath, _stdLib); + if (_xincludeFiles.empty()) + { + std::cerr << "Could not find standard data libraries on the given search path: " << _searchPath.asString() << std::endl; + } + } + catch (std::exception& e) + { + std::cerr << "Failed to load standard data libraries: " << e.what() << std::endl; + return; + } +} + +void Graph::initialize() +{ + loadStandardLibraries(); + setPinColor(); + // load nodes def to create add node ui + std::vector nodeDefs = _stdLib->getNodeDefs(); + for (size_t i = 0; i < nodeDefs.size(); i++) + { + // nodeDef is the key for the map + std::string group = nodeDefs[i]->getNodeGroup(); + if (group == "") + { + group = "extra"; + } + std::unordered_map>::iterator it = _nodesToAdd.find(group); + if (it == _nodesToAdd.end()) + { + std::vector nodes; + _nodesToAdd[group] = nodes; + } + _nodesToAdd[group].push_back(nodeDefs[i]); + } + + mx::FilePath captureFilename = "resources/Materials/Examples/example.png"; + std::string materialFilename = "resources/Materials/Examples/StandardSurface/standard_surface_default.mtlx"; + std::string meshFilename = "resources/Geometry/shaderball.glb"; + std::string envRadianceFilename = "resources/Lights/san_giuseppe_bridge_split.hdr"; + mx::Color3 screenColor(1.0f, 0.3f, 0.32f); + + _renderer = std::make_shared(materialFilename, meshFilename, envRadianceFilename, _searchPath, _libraryFolders, 1280, 960); + _renderer->initialize(); +} + +mx::DocumentPtr Graph::loadDocument(mx::FilePath filename) +{ + std::string materialFilename = filename; + mx::FilePathVec libraryFolders = { "libraries" }; + _libraryFolders = libraryFolders; + mx::XmlReadOptions readOptions; + readOptions.readXIncludeFunction = [](mx::DocumentPtr doc, const mx::FilePath& filename, + const mx::FileSearchPath& searchPath, const mx::XmlReadOptions* options) + { + mx::FilePath resolvedFilename = searchPath.find(filename); + if (resolvedFilename.exists()) + { + readFromXmlFile(doc, resolvedFilename, searchPath, options); + } + else + { + std::cerr << "Include file not found: " << filename.asString() << std::endl; + } + }; + + mx::DocumentPtr doc = mx::createDocument(); + mx::readFromXmlFile(doc, materialFilename, _searchPath, &readOptions); + _graphStack = std::stack>(); + _pinStack = std::stack>(); + return doc; +} + +// populate nodes to add with input output group and nodegraph nodes which are not found in the stdlib +void Graph::addExtraNodes() +{ + std::vector groups{ "Input Nodes", "Output Nodes", "Group Nodes", "Node Graph" }; + std::vector types{ + "float", "integer", "vector2", "vector3", "vector4", "color3", "color4", "string", "filename", "bool" + }; + // need to clear vectors if has previously used tab without there being a document, need to use the current graph doc + for (std::string group : groups) + { + if (_extraNodes[group].size() > 0) + { + _extraNodes[group].clear(); + } + } + for (std::string type : types) + { + + std::string nodeName = "ND_input"; + nodeName += type; + std::vector input{ nodeName, type, "input" }; + _extraNodes["Input Nodes"].push_back(input); + nodeName = "ND_output"; + nodeName += type; + std::vector output{ nodeName, type, "output" }; + _extraNodes["Output Nodes"].push_back(output); + } + // group node + std::vector groupNode{ "ND_group", "", "group" }; + _extraNodes["Group Nodes"].push_back(groupNode); + // node graph nodes + std::vector nodeGraph{ "ND_node graph", "", "nodegraph" }; + _extraNodes["Node Graph"].push_back(nodeGraph); +} + +// return output pin needed to link the inputs and outputs +ed::PinId Graph::getOutputPin(UiNodePtr node, UiNodePtr upNode, Pin input) +{ + if (upNode->getNodeGraph() != nullptr) + { + // for nodegraph need to get the correct ouput pin accorinding to the names of the output nodes + mx::OutputPtr output = input._pinNode->getNode()->getConnectedOutput(input._name); + if (output) + { + std::string outName = input._pinNode->getNode()->getConnectedOutput(input._name)->getName(); + for (Pin outputs : upNode->outputPins) + { + if (outputs._name == outName) + { + return outputs._pinId; + } + } + } + return ed::PinId(); + } + else + { + // every other node can just get the first output pin since there is only one + return (upNode->outputPins[0]._pinId); + } +} + +// connect links via connected nodes in UiNodePtr +void Graph::linkGraph() +{ + _currLinks.clear(); + // start with bottom of graph + for (UiNodePtr node : _graphNodes) + { + std::vector inputs = node->inputPins; + if (node->getInput() == nullptr) + { + for (size_t i = 0; i < inputs.size(); i++) + { + // get upstream node for all inputs + std::string inputName = inputs[i]._name; + + UiNodePtr inputNode = node->getConnectedNode(inputName); + if (inputNode != nullptr) + { + Link link; + // getting the input connections for the current uiNode + ax::NodeEditor::PinId id = inputs[i]._pinId; + inputs[i].setConnected(true); + int end = int(id.Get()); + link._endAttr = end; + // get id number of output of node + + int start = int(getOutputPin(node, inputNode, inputs[i]).Get()); + + if (start >= 0) + { + if (inputNode->outputPins.size() > 0) + { + inputNode->outputPins[0].setConnected(true); + inputNode->outputPins[0].addConnection(inputs[i]); + link._startAttr = start; + + if (!linkExists(link)) + { + _currLinks.push_back(link); + } + } + } + } + else if (inputs[i]._input) + { + if (inputs[i]._input->getInterfaceInput()) + { + + inputs[i].setConnected(true); + } + } + else + { + inputs[i].setConnected(false); + } + } + } + } +} + +// connect all the links via the graph editor library +void Graph::connectLinks() +{ + + for (Link const& link : _currLinks) + { + + ed::Link(link.id, link._startAttr, link._endAttr); + } +} + +// find link position in currLinks vector from link id +int Graph::findLinkPosition(int id) +{ + + int count = 0; + for (size_t i = 0; i < _currLinks.size(); i++) + { + if (_currLinks[i].id == id) + { + return count; + } + count++; + } + return -1; +} +// check if a node has already been assigned a position +bool Graph::checkPosition(UiNodePtr node) +{ + if (node->getMxElement() != nullptr) + { + if (node->getMxElement()->getAttribute("xpos") != "") + { + return true; + } + } + return false; +} +// calculate the total vertical space the node level takes up +float Graph::totalHeight(int level) +{ + float total = 0.f; + for (UiNodePtr node : _levelMap[level]) + { + total += ed::GetNodeSize(node->getId()).y; + } + return total; +} +// set the y position of node based of the starting position and the nodes above it +void Graph::setYSpacing(int level, float startingPos) +{ + // set the y spacing for each node + float currPos = startingPos; + for (UiNodePtr node : _levelMap[level]) + { + ImVec2 oldPos = ed::GetNodePosition(node->getId()); + ed::SetNodePosition(node->getId(), ImVec2(oldPos.x, currPos)); + currPos += ed::GetNodeSize(node->getId()).y + 40; + } +} + +// calculate the average y position for a specific node level +float Graph::findAvgY(const std::vector& nodes) +{ + // find the mid point of node level grou[ + float total = 0.f; + int count = 0; + for (UiNodePtr node : nodes) + { + ImVec2 pos = ed::GetNodePosition(node->getId()); + ImVec2 size = ed::GetNodeSize(node->getId()); + + total += ((size.y + pos.y) + pos.y) / 2; + count++; + } + return (total / count); +} + +void Graph::findYSpacing(float startY) +{ + // assume level 0 is set + // for each level find the average y position of the previous level to use as a spacing guide + int i = 0; + for (std::pair> levelChunk : _levelMap) + { + if (_levelMap[i].size() > 0) + { + if (_levelMap[i][0]->_level > 0) + { + + int prevLevel = _levelMap[i].front()->_level - 1; + float avgY = findAvgY(_levelMap[prevLevel]); + float height = totalHeight(_levelMap[i].front()->_level); + // caculate the starting position to be above the previous level's center so that it is evenly spaced on either side of the center + float startingPos = avgY - ((height + (_levelMap[i].size() * 20)) / 2) + startY; + setYSpacing(_levelMap[i].front()->_level, startingPos); + } + else + { + setYSpacing(_levelMap[i].front()->_level, startY); + } + } + ++i; + } +} + +// layout the x position by assigning the node levels based off its distance from the first node +ImVec2 Graph::layoutPosition(UiNodePtr layoutNode, ImVec2 startingPos, bool initialLayout, int level) +{ + if (checkPosition(layoutNode) && !_autoLayout) + { + for (UiNodePtr node : _graphNodes) + { + // since nodegrpah nodes do not have any materialX info they are placed based off their conneced node + if (node->getNodeGraph() != nullptr) + { + std::vector outputCon = node->getOutputConnections(); + if (outputCon.size() > 0) + { + ImVec2 outputPos = ed::GetNodePosition(outputCon[0]->getId()); + ed::SetNodePosition(node->getId(), ImVec2(outputPos.x - 400, outputPos.y)); + node->setPos(ImVec2(outputPos.x - 400, outputPos.y)); + } + } + else + { + // don't set position of group nodes + if (node->getMessage() == "") + { + float x = std::stof(node->getMxElement()->getAttribute("xpos")); + float y = std::stof(node->getMxElement()->getAttribute("ypos")); + x *= DEFAULT_NODE_SIZE.x; + y *= DEFAULT_NODE_SIZE.y; + ed::SetNodePosition(node->getId(), ImVec2(x, y)); + node->setPos(ImVec2(x, y)); + } + } + } + return ImVec2(0.f, 0.f); + } + else + { + ImVec2 currPos = startingPos; + ImVec2 newPos = currPos; + if (layoutNode->_level != -1) + { + if (layoutNode->_level < level) + { + // remove the old instance of the node from the map + int levelNum = 0; + int removeNum = -1; + for (UiNodePtr levelNode : _levelMap[layoutNode->_level]) + { + if (levelNode->getName() == layoutNode->getName()) + { + removeNum = levelNum; + } + levelNum++; + } + if (removeNum > -1) + { + _levelMap[layoutNode->_level].erase(_levelMap[layoutNode->_level].begin() + removeNum); + } + + layoutNode->_level = level; + } + } + else + { + layoutNode->_level = level; + } + + if (_levelMap.find(layoutNode->_level) != _levelMap.end()) + { + // key already exists add to it + if ((!std::count(_levelMap[layoutNode->_level].begin(), _levelMap[layoutNode->_level].end(), layoutNode))) + { + _levelMap[layoutNode->_level].push_back(layoutNode); + } + } + else + { + // insert new vector into key + std::vector newValue = { layoutNode }; + _levelMap.insert({ layoutNode->_level, newValue }); + } + std::vector pins = layoutNode->inputPins; + if (initialLayout) + { + // check number of inputs that are connected to node + if (layoutNode->getInputConnect() > 0) + { + // not top of node graph stop recursion + if (pins.size() != 0 && layoutNode->getInput() == nullptr) + { + int numNode = 0; + for (size_t i = 0; i < pins.size(); i++) + { + // get upstream node for all inputs + newPos = startingPos; + UiNodePtr nextNode = layoutNode->getConnectedNode(pins[i]._name); + if (nextNode) + { + startingPos.x = 1200.f - ((layoutNode->_level) * 350); + // pos.y = 0; + ed::SetNodePosition(layoutNode->getId(), startingPos); + layoutNode->setPos(ImVec2(startingPos)); + + newPos.x = 1200.f - ((layoutNode->_level + 1) * 75); + ++numNode; + // call layout position on upstream node with newPos as -140 to the left of current node + layoutPosition(nextNode, ImVec2(newPos.x, startingPos.y), initialLayout, layoutNode->_level + 1); + } + } + } + } + else + { + startingPos.x = 1200.f - ((layoutNode->_level) * 350); + layoutNode->setPos(ImVec2(startingPos)); + // set current node position + ed::SetNodePosition(layoutNode->getId(), ImVec2(startingPos)); + } + } + return ImVec2(0.f, 0.f); + } +} + +// extra layout pass for inputs and nodes that do not attach to an output node +void Graph::layoutInputs() +{ + // layout inputs after other nodes so that they can be all in a line on far left side of node graph + if (_levelMap.begin() != _levelMap.end()) + { + int levelCount = -1; + for (std::pair> nodes : _levelMap) + { + ++levelCount; + } + ImVec2 startingPos = ed::GetNodePosition(_levelMap[levelCount].back()->getId()); + startingPos.y += ed::GetNodeSize(_levelMap[levelCount].back()->getId()).y + 20; + + for (UiNodePtr uiNode : _graphNodes) + { + + if (uiNode->getOutputConnections().size() == 0 && (uiNode->getInput() != nullptr)) + { + ed::SetNodePosition(uiNode->getId(), ImVec2(startingPos)); + startingPos.y += ed::GetNodeSize(uiNode->getId()).y; + startingPos.y += 23; + } + // accoutning for extra nodes like in gltf + else if (uiNode->getOutputConnections().size() == 0 && (uiNode->getNode() != nullptr)) + { + if (uiNode->getNode()->getCategory() != mx::SURFACE_MATERIAL_NODE_STRING) + { + layoutPosition(uiNode, ImVec2(1200, 750), _initial, 0); + } + } + } + } +} + +// reutrn pin color based on the type of the value of that pin +void Graph::setPinColor() +{ + _pinColor.insert(std::make_pair("integer", ImColor(255, 255, 28, 255))); + _pinColor.insert(std::make_pair("boolean", ImColor(255, 0, 255, 255))); + _pinColor.insert(std::make_pair("float", ImColor(50, 100, 255, 255))); + _pinColor.insert(std::make_pair("color3", ImColor(178, 34, 34, 255))); + _pinColor.insert(std::make_pair("color4", ImColor(50, 10, 255, 255))); + _pinColor.insert(std::make_pair("vector2", ImColor(100, 255, 100, 255))); + _pinColor.insert(std::make_pair("vector3", ImColor(0, 255, 0, 255))); + _pinColor.insert(std::make_pair("vector4", ImColor(100, 0, 100, 255))); + _pinColor.insert(std::make_pair("matrix33", ImColor(0, 100, 100, 255))); + _pinColor.insert(std::make_pair("matrix44", ImColor(50, 255, 100, 255))); + _pinColor.insert(std::make_pair("filename", ImColor(255, 184, 28, 255))); + _pinColor.insert(std::make_pair("string", ImColor(100, 100, 50, 255))); + _pinColor.insert(std::make_pair("geomname", ImColor(121, 60, 180, 255))); + _pinColor.insert(std::make_pair("BSDF", ImColor(10, 181, 150, 255))); + _pinColor.insert(std::make_pair("EDF", ImColor(255, 50, 100, 255))); + _pinColor.insert(std::make_pair("VDF", ImColor(0, 100, 151, 255))); + _pinColor.insert(std::make_pair("surfaceshader", ImColor(150, 255, 255, 255))); + _pinColor.insert(std::make_pair("material", ImColor(255, 255, 255, 255))); + _pinColor.insert(std::make_pair(mx::DISPLACEMENT_SHADER_TYPE_STRING, ImColor(155, 50, 100, 255))); + _pinColor.insert(std::make_pair(mx::VOLUME_SHADER_TYPE_STRING, ImColor(155, 250, 100, 255))); + _pinColor.insert(std::make_pair(mx::LIGHT_SHADER_TYPE_STRING, ImColor(100, 150, 100, 255))); + _pinColor.insert(std::make_pair("none", ImColor(140, 70, 70, 255))); + _pinColor.insert(std::make_pair(mx::MULTI_OUTPUT_TYPE_STRING, ImColor(70, 70, 70, 255))); + _pinColor.insert(std::make_pair("integerarray", ImColor(200, 10, 100, 255))); + _pinColor.insert(std::make_pair("floatarray", ImColor(25, 250, 100))); + _pinColor.insert(std::make_pair("color3array", ImColor(25, 200, 110))); + _pinColor.insert(std::make_pair("color4array", ImColor(50, 240, 110))); + _pinColor.insert(std::make_pair("vector2array", ImColor(50, 200, 75))); + _pinColor.insert(std::make_pair("vector3array", ImColor(20, 200, 100))); + _pinColor.insert(std::make_pair("vector4array", ImColor(100, 200, 100))); + _pinColor.insert(std::make_pair("geomnamearray", ImColor(150, 200, 100))); + _pinColor.insert(std::make_pair("stringarray", ImColor(120, 180, 100))); +} + +// based off of showLabel from ImGui Node Editor blueprints-example.cpp +auto showLabel = [](const char* label, ImColor color) +{ + ImGui::SetCursorPosY(ImGui::GetCursorPosY() - ImGui::GetTextLineHeight()); + auto size = ImGui::CalcTextSize(label); + + auto padding = ImGui::GetStyle().FramePadding; + auto spacing = ImGui::GetStyle().ItemSpacing; + + ImGui::SetCursorPos(ImGui::GetCursorPos() + ImVec2(spacing.x, -spacing.y)); + + auto rectMin = ImGui::GetCursorScreenPos() - padding; + auto rectMax = ImGui::GetCursorScreenPos() + size + padding; + + auto drawList = ImGui::GetWindowDrawList(); + drawList->AddRectFilled(rectMin, rectMax, color, size.y * 0.15f); + ImGui::TextUnformatted(label); +}; + +void Graph::selectMaterial(UiNodePtr uiNode) +{ + // find renderable element that correspond with material uiNode + std::vector elems; + mx::findRenderableElements(_graphDoc, elems); + mx::TypedElementPtr typedElem = nullptr; + for (mx::TypedElementPtr elem : elems) + { + mx::TypedElementPtr renderableElem = elem; + mx::NodePtr node = elem->asA(); + if (node == uiNode->getNode()) + { + typedElem = elem; + } + } + _renderer->setMaterial(typedElem); +} + +// set the node to display in render veiw based off the selected node or nodegraph +void Graph::setRenderMaterial(UiNodePtr node) +{ + // set render node right away is node is a material + if (node->getNode() && node->getNode()->getType() == "material") + { + // only set new render node if different material has been selected + if (_currRenderNode != node) + { + _currRenderNode = node; + _frameCount = ImGui::GetFrameCount(); + _renderer->setMaterialCompilation(true); + } + } + else + { + // continue downstream using output connections until a material node is found + std::vector outNodes = node->getOutputConnections(); + if (outNodes.size() > 0) + { + if (outNodes[0]->getNode()) + { + if (outNodes[0]->getNode()->getType() == mx::SURFACE_SHADER_TYPE_STRING) + { + std::vector shaderOut = outNodes[0]->getOutputConnections(); + if (shaderOut.size() > 0) + { + if (shaderOut[0]) + { + if (shaderOut[0]->getNode()->getType() == "material") + { + if (_currRenderNode != shaderOut[0]) + { + _currRenderNode = shaderOut[0]; + _frameCount = ImGui::GetFrameCount(); + _renderer->setMaterialCompilation(true); + } + } + } + } + else + { + _currRenderNode = nullptr; + } + } + else if (outNodes[0]->getNode()->getType() == mx::MATERIAL_TYPE_STRING) + { + if (_currRenderNode != outNodes[0]) + { + _currRenderNode = outNodes[0]; + _frameCount = ImGui::GetFrameCount(); + _renderer->setMaterialCompilation(true); + } + } + } + else + { + _currRenderNode = nullptr; + } + } + else + { + _currRenderNode = nullptr; + } + } +} + +void Graph::updateMaterials(mx::InputPtr input, mx::ValuePtr value) +{ + + std::string renderablePaths; + std::vector elems; + std::vector materialNodes; + mx::TypedElementPtr renderableElem; + mx::findRenderableElements(_graphDoc, elems); + if (elems.size() > 0 && _renderer->getMaterials().size() == 0) + { + _renderer->updateMaterials(_graphDoc, nullptr); + } + + size_t num = 0; + int num2 = 0; + for (mx::TypedElementPtr elem : elems) + { + renderableElem = elem; + mx::NodePtr node = elem->asA(); + if (node) + { + if (_currRenderNode) + { + if (node->getName() == _currRenderNode->getName()) + { + materialNodes.push_back(node->getType() == mx::MATERIAL_TYPE_STRING ? node : nullptr); + renderablePaths = renderableElem->getNamePath(); + break; + } + } + else + { + materialNodes.push_back(node->getType() == mx::MATERIAL_TYPE_STRING ? node : nullptr); + renderablePaths = renderableElem->getNamePath(); + } + } + else + { + materialNodes.push_back(nullptr); + renderablePaths = renderableElem->getNamePath(); + if (num2 == 2) + { + break; + } + num2++; + } + } + if (input == nullptr) + { + if (renderablePaths != "") + { + mx::ElementPtr elem = _graphDoc->getDescendant(renderablePaths); + mx::TypedElementPtr typedElem = elem ? elem->asA() : nullptr; + _renderer->updateMaterials(_graphDoc, typedElem); + } + } + else + { + if (renderablePaths != "") + { + std::string name = input->getNamePath(); + // need to use exact interface name in order for input + mx::InputPtr interfaceInput = findInput(input, input->getName()); + if (interfaceInput) + { + name = interfaceInput->getNamePath(); + } + _renderer->getMaterials()[num]->modifyUniform(name, value); + } + } +} +// set the value of the selected node constants in the node property editor +void Graph::setConstant(UiNodePtr node, mx::InputPtr& input) +{ + std::string inName = input->getName(); + float labelWidth = ImGui::CalcTextSize(inName.c_str()).x; + // if input is a float set the float slider Ui to the value + if (input->getType() == "float") + { + mx::ValuePtr val = input->getValue(); + + if (val && val->isA()) + { + // updates the value to the default for new nodes + float prev = val->asA(), temp = val->asA(); + ImGui::SameLine(); + ImGui::PushItemWidth(labelWidth + 20); + ImGui::DragFloat("##hidelabel", &temp, 0.01f, 0.f, 100.f); + ImGui::PopItemWidth(); + // set input value and update materials if different from previous value + if (prev != temp) + { + addNodeInput(_currUiNode, input); + input->setValue(temp, input->getType()); + updateMaterials(input, input->getValue()); + } + } + } + else if (input->getType() == "integer") + { + mx::ValuePtr val = input->getValue(); + if (val && val->isA()) + { + int prev = val->asA(), temp = val->asA(); + ImGui::SameLine(); + ImGui::PushItemWidth(labelWidth + 20); + ImGui::DragInt("##hidelabel", &temp, 1, 0, 100); + ImGui::PopItemWidth(); + // set input value and update materials if different from previous value + if (prev != temp) + { + addNodeInput(_currUiNode, input); + input->setValue(temp, input->getType()); + updateMaterials(input, input->getValue()); + } + } + } + else if (input->getType() == "color3") + { + mx::ValuePtr val = input->getValue(); + if (val && val->isA()) + { + mx::Color3 prev = val->asA(), temp = val->asA(); + ImGui::SameLine(); + ImGui::PushItemWidth(labelWidth + 100); + ImGui::DragFloat3("##hidelabel", &temp[0], 0.01f, 0.f, 100.f); + ImGui::SameLine(); + ImGui::ColorEdit3("##color", &temp[0], ImGuiColorEditFlags_NoInputs); + ImGui::PopItemWidth(); + + // set input value and update materials if different from previous value + if (prev != temp) + { + addNodeInput(_currUiNode, input); + input->setValue(temp, input->getType()); + updateMaterials(input, input->getValue()); + } + } + } + else if (input->getType() == "color4") + { + mx::ValuePtr val = input->getValue(); + if (val && val->isA()) + { + mx::Color4 prev = val->asA(), temp = val->asA(); + ImGui::SameLine(); + ImGui::PushItemWidth(labelWidth + 100); + ImGui::DragFloat4("##hidelabel", &temp[0], 0.01f, 0.f, 100.f); + ImGui::SameLine(); + // color edit for the color picker to the right of the color floats + ImGui::ColorEdit4("##color", &temp[0], ImGuiColorEditFlags_NoInputs); + ImGui::PopItemWidth(); + // set input value and update materials if different from previous value + if (temp != prev) + { + addNodeInput(_currUiNode, input); + input->setValue(temp, input->getType()); + updateMaterials(input, input->getValue()); + } + } + } + else if (input->getType() == "vector2") + { + mx::ValuePtr val = input->getValue(); + if (val && val->isA()) + { + mx::Vector2 prev = val->asA(), temp = val->asA(); + ImGui::SameLine(); + ImGui::PushItemWidth(labelWidth + 100); + ImGui::DragFloat2("##hidelabel", &temp[0], 0.01f, 0.f, 100.f); + ImGui::PopItemWidth(); + // set input value and update materials if different from previous value + if (prev != temp) + { + addNodeInput(_currUiNode, input); + input->setValue(temp, input->getType()); + updateMaterials(input, input->getValue()); + } + } + } + else if (input->getType() == "vector3") + { + mx::ValuePtr val = input->getValue(); + if (val && val->isA()) + { + mx::Vector3 prev = val->asA(), temp = val->asA(); + ImGui::SameLine(); + ImGui::PushItemWidth(labelWidth + 100); + ImGui::DragFloat3("##hidelabel", &temp[0], 0.01f, 0.f, 100.f); + ImGui::PopItemWidth(); + // set input value and update materials if different from previous value + if (prev != temp) + { + addNodeInput(_currUiNode, input); + input->setValue(temp, input->getType()); + updateMaterials(input, input->getValue()); + } + } + } + else if (input->getType() == "vector4") + { + mx::ValuePtr val = input->getValue(); + if (val && val->isA()) + { + mx::Vector4 prev = val->asA(), temp = val->asA(); + ImGui::SameLine(); + ImGui::PushItemWidth(labelWidth + 90); + ImGui::DragFloat4("##hidelabel", &temp[0], 0.01f, 0.f, 100.f); + ImGui::PopItemWidth(); + // set input value and update materials if different from previous value + if (prev != temp) + { + addNodeInput(_currUiNode, input); + input->setValue(temp, input->getType()); + updateMaterials(input, input->getValue()); + } + } + } + else if (input->getType() == "string") + { + mx::ValuePtr val = input->getValue(); + if (val && val->isA()) + { + std::string prev = val->asA(), temp = val->asA(); + ImGui::SameLine(); + ImGui::PushItemWidth(labelWidth); + ImGui::InputText("##constant", &temp); + ImGui::PopItemWidth(); + // set input value and update materials if different from previous value + if (prev != temp) + { + addNodeInput(_currUiNode, input); + input->setValue(temp, input->getType()); + updateMaterials(); + } + } + } + else if (input->getType() == "filename") + { + mx::ValuePtr val = input->getValue(); + + if (val && val->isA()) + { + std::string temp = val->asA(), prev = val->asA(); + ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.15f, .15f, .15f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.2f, .4f, .6f, 1.0f)); + // browser button to select new file + if (ImGui::Button("Browse")) + { + _fileDialogConstant.SetTitle("Node Input Dialog"); + _fileDialogConstant.Open(); + } + ImGui::SameLine(); + ImGui::PushItemWidth(labelWidth); + ImGui::Text("%s", mx::FilePath(temp).getBaseName().c_str()); + ImGui::PopItemWidth(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + + // create and load document from selected file + if (_fileDialogConstant.HasSelected()) + { + // set the new filename to the complete file path + mx::FilePath fileName = mx::FilePath(_fileDialogConstant.GetSelected().string()); + temp = fileName; + // need to set the file prefix for the input to "" so that it can find the new file + input->setAttribute(input->FILE_PREFIX_ATTRIBUTE, ""); + _fileDialogConstant.ClearSelected(); + } + + // set input value and update materials if different from previous value + if (prev != temp) + { + addNodeInput(_currUiNode, input); + input->setValueString(temp); + input->setValue(temp, input->getType()); + updateMaterials(); + } + } + } + else if (input->getType() == "boolean") + { + mx::ValuePtr val = input->getValue(); + if (val && val->isA()) + { + bool prev = val->asA(), temp = val->asA(); + ImGui::SameLine(); + ImGui::PushItemWidth(labelWidth); + ImGui::Checkbox("", &temp); + ImGui::PopItemWidth(); + // set input value and update materials if different from previous value + if (prev != temp) + { + addNodeInput(_currUiNode, input); + input->setValue(temp, input->getType()); + updateMaterials(input, input->getValue()); + } + } + } +} +// build the initial graph of a loaded mtlx document including shader, material and nodegraph node +void Graph::setUiNodeInfo(UiNodePtr node, std::string type, std::string category) +{ + node->setType(type); + node->setCategory(category); + ++_graphTotalSize; + // create pins + if (node->getNodeGraph()) + { + std::vector outputs = node->getNodeGraph()->getOutputs(); + for (mx::OutputPtr out : outputs) + { + Pin outPin = Pin(_graphTotalSize, &*out->getName().begin(), out->getType(), node, ax::NodeEditor::PinKind::Output, nullptr, nullptr); + ++_graphTotalSize; + node->outputPins.push_back(outPin); + _currPins.push_back(outPin); + } + + for (mx::InputPtr input : node->getNodeGraph()->getInputs()) + { + Pin inPin = Pin(_graphTotalSize, &*input->getName().begin(), input->getType(), node, ax::NodeEditor::PinKind::Input, input, nullptr); + node->inputPins.push_back(inPin); + _currPins.push_back(inPin); + ++_graphTotalSize; + } + } + else + { + if (node->getNode()) + { + mx::NodeDefPtr nodeDef = node->getNode()->getNodeDef(node->getNode()->getName()); + for (mx::InputPtr input : nodeDef->getActiveInputs()) + { + if (node->getNode()->getInput(input->getName())) + { + input = node->getNode()->getInput(input->getName()); + } + Pin inPin = Pin(_graphTotalSize, &*input->getName().begin(), input->getType(), node, ax::NodeEditor::PinKind::Input, input, nullptr); + node->inputPins.push_back(inPin); + _currPins.push_back(inPin); + ++_graphTotalSize; + } + } + else if (node->getInput()) + { + Pin inPin = Pin(_graphTotalSize, &*("Value"), node->getInput()->getType(), node, ax::NodeEditor::PinKind::Input, node->getInput(), nullptr); + node->inputPins.push_back(inPin); + _currPins.push_back(inPin); + ++_graphTotalSize; + } + else if (node->getOutput()) + { + Pin inPin = Pin(_graphTotalSize, &*("input"), node->getOutput()->getType(), node, ax::NodeEditor::PinKind::Input, nullptr, node->getOutput()); + node->inputPins.push_back(inPin); + _currPins.push_back(inPin); + ++_graphTotalSize; + } + Pin outPin = Pin(_graphTotalSize, &*("output"), type, node, ax::NodeEditor::PinKind::Output, nullptr, nullptr); + ++_graphTotalSize; + node->outputPins.push_back(outPin); + _currPins.push_back(outPin); + } + + _graphNodes.push_back(std::move(node)); +} +// build the UiNode node graph based off of loading a document +void Graph::buildUiBaseGraph(const std::vector& nodeGraphs, const std::vector& docNodes, const std::vector& inputNodes, const std::vector& outputNodes) +{ + _graphNodes.clear(); + _currLinks.clear(); + _currEdge.clear(); + _newLinks.clear(); + _currPins.clear(); + _graphTotalSize = 1; + // creating uiNodes for nodes that belong to the document so they are not in a nodegraph + for (mx::NodePtr node : docNodes) + { + std::string name = node->getName(); + auto currNode = std::make_shared(name, _graphTotalSize); + currNode->setNode(node); + setUiNodeInfo(currNode, node->getType(), node->getCategory()); + } + // creating uiNodes for the nodegraph + for (mx::NodeGraphPtr nodeGraph : nodeGraphs) + { + std::string name = nodeGraph->getName(); + auto currNode = std::make_shared(name, _graphTotalSize); + currNode->setNodeGraph(nodeGraph); + setUiNodeInfo(currNode, "", "nodegraph"); + } + for (mx::InputPtr input : inputNodes) + { + auto currNode = std::make_shared(input->getName(), _graphTotalSize); + currNode->setInput(input); + setUiNodeInfo(currNode, input->getType(), input->getCategory()); + } + for (mx::OutputPtr output : outputNodes) + { + auto currNode = std::make_shared(output->getName(), _graphTotalSize); + currNode->setOutput(output); + setUiNodeInfo(currNode, output->getType(), output->getCategory()); + } + // creating edges for nodegraphs + for (mx::NodeGraphPtr graph : nodeGraphs) + { + for (mx::InputPtr input : graph->getActiveInputs()) + { + int downNum = -1; + int upNum = -1; + mx::NodePtr connectedNode = input->getConnectedNode(); + if (connectedNode) + { + downNum = findNode(graph->getName(), "nodegraph"); + upNum = findNode(connectedNode->getName(), "node"); + if (upNum > -1) + { + UiEdge newEdge = UiEdge(_graphNodes[upNum], _graphNodes[downNum], input); + if (!edgeExists(newEdge)) + { + _graphNodes[downNum]->edges.push_back(newEdge); + _graphNodes[downNum]->setInputNodeNum(1); + _graphNodes[upNum]->setOutputConnection(_graphNodes[downNum]); + _currEdge.push_back(newEdge); + } + } + } + } + } + // creating edges for surface and material nodes + for (mx::NodePtr node : docNodes) + { + mx::NodeDefPtr nD = node->getNodeDef(node->getName()); + for (mx::InputPtr input : node->getActiveInputs()) + { + + mx::string nodeGraphName = input->getNodeGraphString(); + mx::NodePtr connectedNode = input->getConnectedNode(); + mx::OutputPtr connectedOutput = input->getConnectedOutput(); + int upNum = -1; + int downNum = -1; + if (nodeGraphName != "") + { + + upNum = findNode(nodeGraphName, "nodegraph"); + downNum = findNode(node->getName(), "node"); + } + else if (connectedNode) + { + upNum = findNode(connectedNode->getName(), "node"); + downNum = findNode(node->getName(), "node"); + } + else if (connectedOutput) + { + upNum = findNode(connectedOutput->getName(), "output"); + downNum = findNode(node->getName(), "node"); + } + else if (input->getInterfaceName() != "") + { + upNum = findNode(input->getInterfaceName(), "input"); + downNum = findNode(node->getName(), "node"); + } + if (upNum != -1) + { + + UiEdge newEdge = UiEdge(_graphNodes[upNum], _graphNodes[downNum], input); + if (!edgeExists(newEdge)) + { + _graphNodes[downNum]->edges.push_back(newEdge); + _graphNodes[downNum]->setInputNodeNum(1); + _graphNodes[upNum]->setOutputConnection(_graphNodes[downNum]); + _currEdge.push_back(newEdge); + } + } + } + } +} +// build the UiNode node graph based off of diving into a node graph node +void Graph::buildUiNodeGraph(const mx::NodeGraphPtr& nodeGraphs) +{ + + // clear all values so that ids can start with 0 or 1 + _graphNodes.clear(); + _currLinks.clear(); + _currEdge.clear(); + _newLinks.clear(); + _currPins.clear(); + _graphTotalSize = 1; + if (nodeGraphs) + { + mx::NodeGraphPtr nodeGraph = nodeGraphs; + std::vector children = nodeGraph->topologicalSort(); + // Write out all nodes. + + mx::NodeDefPtr nodeDef = nodeGraph->getNodeDef(); + mx::NodeDefPtr currNodeDef; + + // create input nodes + if (nodeDef) + { + std::vector inputs = nodeDef->getActiveInputs(); + + for (mx::InputPtr input : inputs) + { + auto currNode = std::make_shared(input->getName(), _graphTotalSize); + currNode->setInput(input); + setUiNodeInfo(currNode, input->getType(), input->getCategory()); + } + } + + // search node graph children to create uiNodes + for (mx::ElementPtr elem : children) + { + mx::NodePtr node = elem->asA(); + mx::InputPtr input = elem->asA(); + mx::OutputPtr output = elem->asA(); + std::string name = elem->getName(); + auto currNode = std::make_shared(name, _graphTotalSize); + if (node) + { + currNode->setNode(node); + setUiNodeInfo(currNode, node->getType(), node->getCategory()); + } + else if (input) + { + currNode->setInput(input); + setUiNodeInfo(currNode, input->getType(), input->getCategory()); + } + else if (output) + { + currNode->setOutput(output); + setUiNodeInfo(currNode, output->getType(), output->getCategory()); + } + } + + // Write out all connections. + std::set processedEdges; + for (mx::OutputPtr output : nodeGraph->getOutputs()) + { + for (mx::Edge edge : output->traverseGraph()) + { + if (!processedEdges.count(edge)) + { + mx::ElementPtr upstreamElem = edge.getUpstreamElement(); + mx::ElementPtr downstreamElem = edge.getDownstreamElement(); + mx::ElementPtr connectingElem = edge.getConnectingElement(); + + mx::NodePtr upstreamNode = upstreamElem->asA(); + mx::NodePtr downstreamNode = downstreamElem->asA(); + mx::InputPtr upstreamInput = upstreamElem->asA(); + mx::InputPtr downstreamInput = downstreamElem->asA(); + mx::OutputPtr upstreamOutput = upstreamElem->asA(); + mx::OutputPtr downstreamOutput = downstreamElem->asA(); + std::string downName = downstreamElem->getName(); + std::string upName = upstreamElem->getName(); + std::string upstreamType; + std::string downstreamType; + if (upstreamNode) + { + upstreamType = "node"; + } + else if (upstreamInput) + { + upstreamType = "input"; + } + else if (upstreamOutput) + { + upstreamType = "output"; + } + if (downstreamNode) + { + downstreamType = "node"; + } + else if (downstreamInput) + { + downstreamType = "input"; + } + else if (downstreamOutput) + { + downstreamType = "output"; + } + int upNode = findNode(upName, upstreamType); + int downNode = findNode(downName, downstreamType); + if (_graphNodes[downNode]->getOutput() != nullptr) + { + // creating edges for the output nodes + UiEdge newEdge = UiEdge(_graphNodes[upNode], _graphNodes[downNode], nullptr); + if (!edgeExists(newEdge)) + { + _graphNodes[downNode]->edges.push_back(newEdge); + _graphNodes[downNode]->setInputNodeNum(1); + _graphNodes[upNode]->setOutputConnection(_graphNodes[downNode]); + _currEdge.push_back(newEdge); + } + } + else if (connectingElem) + { + + mx::InputPtr connectingInput = connectingElem->asA(); + + if (connectingInput) + { + if ((upNode >= 0) && (downNode >= 0)) + { + UiEdge newEdge = UiEdge(_graphNodes[upNode], _graphNodes[downNode], connectingInput); + if (!edgeExists(newEdge)) + { + _graphNodes[downNode]->edges.push_back(newEdge); + _graphNodes[downNode]->setInputNodeNum(1); + _graphNodes[upNode]->setOutputConnection(_graphNodes[downNode]); + _currEdge.push_back(newEdge); + } + } + } + } + if (upstreamNode) + { + std::vector ins = upstreamNode->getActiveInputs(); + for (mx::InputPtr input : ins) + { + // connecting input nodes + if (input->hasInterfaceName()) + { + std::string interfaceName = input->getInterfaceName(); + int newUp = findNode(interfaceName, "input"); + if (newUp >= 0) + { + mx::InputPtr inputP = std::make_shared(downstreamElem, input->getName()); + UiEdge newEdge = UiEdge(_graphNodes[newUp], _graphNodes[upNode], input); + if (!edgeExists(newEdge)) + { + _graphNodes[upNode]->edges.push_back(newEdge); + _graphNodes[upNode]->setInputNodeNum(1); + _graphNodes[newUp]->setOutputConnection(_graphNodes[upNode]); + _currEdge.push_back(newEdge); + } + } + } + } + } + + processedEdges.insert(edge); + } + } + } + + // second pass to catch all of the connections that arent part of an output + for (mx::ElementPtr elem : children) + { + mx::NodePtr node = elem->asA(); + mx::InputPtr inputElem = elem->asA(); + mx::OutputPtr output = elem->asA(); + if (node) + { + std::vector inputs = node->getActiveInputs(); + for (mx::InputPtr input : inputs) + { + mx::NodePtr upNode = input->getConnectedNode(); + if (upNode) + { + int upNum = findNode(upNode->getName(), "node"); + int downNode = findNode(node->getName(), "node"); + if ((upNum >= 0) && (downNode >= 0)) + { + + UiEdge newEdge = UiEdge(_graphNodes[upNum], _graphNodes[downNode], input); + if (!edgeExists(newEdge)) + { + _graphNodes[downNode]->edges.push_back(newEdge); + _graphNodes[downNode]->setInputNodeNum(1); + _graphNodes[upNum]->setOutputConnection(_graphNodes[downNode]); + _currEdge.push_back(newEdge); + } + } + } + else if (input->getInterfaceInput()) + { + int upNum = findNode(input->getInterfaceInput()->getName(), "input"); + int downNode = findNode(node->getName(), "node"); + if ((upNum >= 0) && (downNode >= 0)) + { + + UiEdge newEdge = UiEdge(_graphNodes[upNum], _graphNodes[downNode], input); + if (!edgeExists(newEdge)) + { + _graphNodes[downNode]->edges.push_back(newEdge); + _graphNodes[downNode]->setInputNodeNum(1); + _graphNodes[upNum]->setOutputConnection(_graphNodes[downNode]); + _currEdge.push_back(newEdge); + } + } + } + } + } + else if (output) + { + mx::NodePtr upNode = output->getConnectedNode(); + if (upNode) + { + int upNum = findNode(upNode->getName(), "node"); + int downNode = findNode(output->getName(), "output"); + UiEdge newEdge = UiEdge(_graphNodes[upNum], _graphNodes[downNode], nullptr); + if (!edgeExists(newEdge)) + { + _graphNodes[downNode]->edges.push_back(newEdge); + _graphNodes[downNode]->setInputNodeNum(1); + _graphNodes[upNum]->setOutputConnection(_graphNodes[downNode]); + _currEdge.push_back(newEdge); + } + } + } + } + } +} + +// return node position in _graphNodes based off node name and type to account for input/output UiNodes with same names as mx Nodes +int Graph::findNode(std::string name, std::string type) +{ + int count = 0; + for (size_t i = 0; i < _graphNodes.size(); i++) + { + if (_graphNodes[i]->getName() == name) + { + if (type == "node" && _graphNodes[i]->getNode() != nullptr) + { + return count; + } + else if (type == "input" && _graphNodes[i]->getInput() != nullptr) + { + return count; + } + else if (type == "output" && _graphNodes[i]->getOutput() != nullptr) + { + return count; + } + else if (type == "nodegraph" && _graphNodes[i]->getNodeGraph() != nullptr) + { + return count; + } + } + count++; + } + return -1; +} + +// set position of pasted nodes based off of original node position +void Graph::positionPasteBin(ImVec2 pos) +{ + ImVec2 totalPos = ImVec2(0, 0); + ImVec2 avgPos = ImVec2(0, 0); + + // get average position of original nodes + for (auto pasteNode : _copiedNodes) + { + ImVec2 origPos = ed::GetNodePosition(pasteNode.first->getId()); + totalPos.x += origPos.x; + totalPos.y += origPos.y; + } + avgPos.x = totalPos.x / (int) _copiedNodes.size(); + avgPos.y = totalPos.y / (int) _copiedNodes.size(); + + // get offset from clciked position + ImVec2 offset = ImVec2(0, 0); + offset.x = pos.x - avgPos.x; + offset.y = pos.y - avgPos.y; + for (auto pasteNode : _copiedNodes) + { + ImVec2 newPos = ImVec2(0, 0); + newPos.x = ed::GetNodePosition(pasteNode.first->getId()).x + offset.x; + newPos.y = ed::GetNodePosition(pasteNode.first->getId()).y + offset.y; + ed::SetNodePosition(pasteNode.second->getId(), newPos); + } +} +void Graph::createEdge(UiNodePtr upNode, UiNodePtr downNode, mx::InputPtr connectingInput) +{ + if (downNode->getOutput()) + { + // creating edges for the output nodes + UiEdge newEdge = UiEdge(upNode, downNode, nullptr); + if (!edgeExists(newEdge)) + { + downNode->edges.push_back(newEdge); + downNode->setInputNodeNum(1); + upNode->setOutputConnection(downNode); + _currEdge.push_back(newEdge); + } + } + else if (connectingInput) + { + UiEdge newEdge = UiEdge(upNode, downNode, connectingInput); + downNode->edges.push_back(newEdge); + downNode->setInputNodeNum(1); + upNode->setOutputConnection(downNode); + _currEdge.push_back(newEdge); + } +} + +void Graph::copyUiNode(UiNodePtr node) +{ + UiNodePtr copyNode = std::make_shared(int(_graphTotalSize + 1)); + ++_graphTotalSize; + if (node->getMxElement()) + { + std::string newName = node->getMxElement()->getParent()->createValidChildName(node->getName()); + if (node->getNode()) + { + mx::NodePtr mxNode; + mxNode = _currGraphElem->addNodeInstance(node->getNode()->getNodeDef()); + mxNode->copyContentFrom(node->getNode()); + mxNode->setName(newName); + copyNode->setNode(mxNode); + } + else if (node->getInput()) + { + mx::InputPtr mxInput; + mxInput = _currGraphElem->addInput(newName); + mxInput->copyContentFrom(node->getInput()); + copyNode->setInput(mxInput); + } + else if (node->getOutput()) + { + mx::OutputPtr mxOutput; + mxOutput = _currGraphElem->addOutput(newName); + mxOutput->copyContentFrom(node->getOutput()); + mxOutput->setName(newName); + copyNode->setOutput(mxOutput); + } + copyNode->getMxElement()->setName(newName); + copyNode->setName(newName); + } + else if (node->getNodeGraph()) + { + _graphDoc->addNodeGraph(); + std::string nodeGraphName = _graphDoc->getNodeGraphs().back()->getName(); + copyNode->setNodeGraph(_graphDoc->getNodeGraphs().back()); + copyNode->setName(nodeGraphName); + copyNodeGraph(node, copyNode); + } + setUiNodeInfo(copyNode, node->getType(), node->getCategory()); + _copiedNodes[node] = copyNode; + _graphNodes.push_back(copyNode); +} +void Graph::copyNodeGraph(UiNodePtr origGraph, UiNodePtr copyGraph) +{ + copyGraph->getNodeGraph()->copyContentFrom(origGraph->getNodeGraph()); + std::vector inputs = copyGraph->getNodeGraph()->getActiveInputs(); + for (mx::InputPtr input : inputs) + { + std::string newName = _graphDoc->createValidChildName(input->getName()); + input->setName(newName); + } +} +void Graph::copyInputs() +{ + for (std::map::iterator iter = _copiedNodes.begin(); iter != _copiedNodes.end(); ++iter) + { + int count = 0; + UiNodePtr origNode = iter->first; + UiNodePtr copyNode = iter->second; + for (Pin pin : origNode->inputPins) + { + + if (origNode->getConnectedNode(pin._name) && !_ctrlClick) + { + // if original node is connected check if connect node is in copied nodes + if (_copiedNodes.find(origNode->getConnectedNode(pin._name)) != _copiedNodes.end()) + { + // set copy node connected to the value at this key + // create an edge + createEdge(_copiedNodes[origNode->getConnectedNode(pin._name)], copyNode, copyNode->inputPins[count]._input); + UiNodePtr upNode = _copiedNodes[origNode->getConnectedNode(pin._name)]; + if (copyNode->getNode() || copyNode->getNodeGraph()) + { + + mx::InputPtr connectingInput = nullptr; + copyNode->inputPins[count]._input->copyContentFrom(pin._input); + // update value to be empty + if (copyNode->getNode() && copyNode->getNode()->getType() == mx::SURFACE_SHADER_TYPE_STRING) + { + if (upNode->getOutput()) + { + copyNode->inputPins[count]._input->setConnectedOutput(upNode->getOutput()); + } + else if (upNode->getInput()) + { + + copyNode->inputPins[count]._input->setInterfaceName(upNode->getName()); + } + else + { + // node graph + if (upNode->getNodeGraph()) + { + ed::PinId outputId = getOutputPin(copyNode, upNode, copyNode->inputPins[count]); + for (Pin outPin : upNode->outputPins) + { + if (outPin._pinId == outputId) + { + mx::OutputPtr outputs = upNode->getNodeGraph()->getOutput(outPin._name); + copyNode->inputPins[count]._input->setConnectedOutput(outputs); + } + } + } + else + { + copyNode->inputPins[count]._input->setConnectedNode(upNode->getNode()); + } + } + } + else + { + if (upNode->getInput()) + { + + copyNode->inputPins[count]._input->setInterfaceName(upNode->getName()); + } + else + { + copyNode->inputPins[count]._input->setConnectedNode(upNode->getNode()); + } + } + + copyNode->inputPins[count].setConnected(true); + copyNode->inputPins[count]._input->removeAttribute(mx::ValueElement::VALUE_ATTRIBUTE); + } + else if (copyNode->getOutput() != nullptr) + { + mx::InputPtr connectingInput = nullptr; + copyNode->getOutput()->setConnectedNode(upNode->getNode()); + } + + // update input node num and output connections + copyNode->setInputNodeNum(1); + upNode->setOutputConnection(copyNode); + } + else if (pin._input) + { + if (pin._input->getInterfaceInput()) + { + copyNode->inputPins[count]._input->removeAttribute(mx::ValueElement::INTERFACE_NAME_ATTRIBUTE); + } + copyNode->inputPins[count].setConnected(false); + setDefaults(copyNode->inputPins[count]._input); + copyNode->inputPins[count]._input->setConnectedNode(nullptr); + copyNode->inputPins[count]._input->setConnectedOutput(nullptr); + } + } + count++; + } + } +} +// add node to graphNodes based off of node def information +void Graph::addNode(std::string category, std::string name, std::string type) +{ + mx::NodePtr node = nullptr; + std::vector matchingNodeDefs; + // create document or node graph is there is not already one + if (category == "output") + { + std::string outName = ""; + mx::OutputPtr newOut; + // add output as child of correct parent and create valid name + outName = _currGraphElem->createValidChildName(name); + newOut = _currGraphElem->addOutput(outName, type); + auto outputNode = std::make_shared(outName, int(++_graphTotalSize)); + outputNode->setOutput(newOut); + setUiNodeInfo(outputNode, type, category); + return; + } + if (category == "input") + { + std::string inName = ""; + mx::InputPtr newIn = nullptr; + // add input as child of correct parent and create valid name + inName = _currGraphElem->createValidChildName(name); + newIn = _currGraphElem->addInput(inName, type); + auto inputNode = std::make_shared(inName, int(++_graphTotalSize)); + setDefaults(newIn); + inputNode->setInput(newIn); + setUiNodeInfo(inputNode, type, category); + return; + } + else if (category == "group") + { + auto groupNode = std::make_shared(name, int(++_graphTotalSize)); + // set message of group uinode in order to identify it as such + groupNode->setMessage("Comment"); + setUiNodeInfo(groupNode, type, "group"); + // create ui portions of group node + buildGroupNode(_graphNodes.back()); + return; + } + else if (category == "nodegraph") + { + // create new mx::NodeGraph and set as current node graph + _graphDoc->addNodeGraph(); + std::string nodeGraphName = _graphDoc->getNodeGraphs().back()->getName(); + auto nodeGraphNode = std::make_shared(nodeGraphName, int(++_graphTotalSize)); + // set mx::Nodegraph as node graph for uiNode + nodeGraphNode->setNodeGraph(_graphDoc->getNodeGraphs().back()); + + setUiNodeInfo(nodeGraphNode, type, "nodegraph"); + return; + } + // if shader or material we want to add to the document instead of the node graph + else if (type == mx::SURFACE_SHADER_TYPE_STRING) + { + matchingNodeDefs = _graphDoc->getMatchingNodeDefs(category); + for (mx::NodeDefPtr nodedef : matchingNodeDefs) + { + std::string nodedefName = nodedef->getName(); + std::string sub = nodedefName.substr(3, nodedefName.length()); + if (sub == name) + { + node = _graphDoc->addNodeInstance(nodedef); + node->setName(_graphDoc->createValidChildName(name)); + break; + } + } + } + else if (type == mx::MATERIAL_TYPE_STRING) + { + matchingNodeDefs = _graphDoc->getMatchingNodeDefs(category); + for (mx::NodeDefPtr nodedef : matchingNodeDefs) + { + std::string nodedefName = nodedef->getName(); + std::string sub = nodedefName.substr(3, nodedefName.length()); + if (sub == name) + { + node = _graphDoc->addNodeInstance(nodedef); + node->setName(_graphDoc->createValidChildName(name)); + break; + } + } + } + else + { + matchingNodeDefs = _graphDoc->getMatchingNodeDefs(category); + for (mx::NodeDefPtr nodedef : matchingNodeDefs) + { + // use substring of name in order to remove ND_ + std::string nodedefName = nodedef->getName(); + std::string sub = nodedefName.substr(3, nodedefName.length()); + if (sub == name) + { + + node = _currGraphElem->addNodeInstance(nodedef); + node->setName(_currGraphElem->createValidChildName(name)); + } + } + } + if (node) + { + int num = 0; + int countDef = 0; + for (size_t i = 0; i < matchingNodeDefs.size(); i++) + { + // use substring of name in order to remove ND_ + std::string nodedefName = matchingNodeDefs[i]->getName(); + std::string sub = nodedefName.substr(3, nodedefName.length()); + if (sub == name) + { + num = countDef; + } + countDef++; + } + std::vector defInputs = matchingNodeDefs[num]->getActiveInputs(); + // adding inputs to ui node as pins so that we can later add them to the node if necessary + auto newNode = std::make_shared(node->getName(), int(++_graphTotalSize)); + newNode->setCategory(category); + newNode->setType(type); + newNode->setNode(node); + newNode->_showAllInputs = true; + node->setType(type); + ++_graphTotalSize; + for (mx::InputPtr input : defInputs) + { + Pin inPin = Pin(_graphTotalSize, &*input->getName().begin(), input->getType(), newNode, ax::NodeEditor::PinKind::Input, input, nullptr); + newNode->inputPins.push_back(inPin); + _currPins.push_back(inPin); + ++_graphTotalSize; + } + // output pin + Pin outPin = Pin(_graphTotalSize, &*("ouput"), newNode->getType(), newNode, ax::NodeEditor::PinKind::Output, nullptr, nullptr); + ++_graphTotalSize; + newNode->outputPins.push_back(outPin); + _currPins.push_back(outPin); + + _graphNodes.push_back(std::move(newNode)); + updateMaterials(); + } +} +// return node pos +int Graph::getNodeId(ed::PinId pinId) +{ + for (Pin pin : _currPins) + { + if (pin._pinId == pinId) + { + return findNode(pin._pinNode->getId()); + } + } + return -1; +} + +// return pin based off of Pin id +Pin Graph::getPin(ed::PinId pinId) +{ + for (Pin pin : _currPins) + { + if (pin._pinId == pinId) + { + return pin; + } + } + Pin nullPin = Pin(-10000, "nullPin", "null", nullptr, ax::NodeEditor::PinKind::Output, nullptr, nullptr); + return nullPin; +} + +// This function is based off of the pin icon function in the ImGui Node Editor blueprints-example.cpp +void Graph::DrawPinIcon(std::string type, bool connected, int alpha) +{ + ax::Drawing::IconType iconType = ax::Drawing::IconType::Circle; + ImColor color = ImColor(0, 0, 0, 255); + if (_pinColor.find(type) != _pinColor.end()) + { + color = _pinColor[type]; + } + + color.Value.w = alpha / 255.0f; + + ax::Widgets::Icon(ImVec2(24, 24), iconType, connected, color, ImColor(32, 32, 32, alpha)); +} + +// This function is based off of the comment node in the ImGui Node Editor blueprints-example.cpp +void Graph::buildGroupNode(UiNodePtr node) +{ + const float commentAlpha = 0.75f; + + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, commentAlpha); + ed::PushStyleColor(ed::StyleColor_NodeBg, ImColor(255, 255, 255, 64)); + ed::PushStyleColor(ed::StyleColor_NodeBorder, ImColor(255, 255, 255, 64)); + + ed::BeginNode(node->getId()); + ImGui::PushID(node->getId()); + + std::string original = node->getMessage(); + std::string temp = original; + ImVec2 messageSize = ImGui::CalcTextSize(temp.c_str()); + ImGui::PushItemWidth(messageSize.x + 15); + ImGui::InputText("##edit", &temp); + node->setMessage(temp); + ImGui::PopItemWidth(); + ed::Group(ImVec2(300, 200)); + ImGui::PopID(); + ed::EndNode(); + ed::PopStyleColor(2); + ImGui::PopStyleVar(); + if (ed::BeginGroupHint(node->getId())) + { + auto bgAlpha = static_cast(ImGui::GetStyle().Alpha * 255); + auto min = ed::GetGroupMin(); + + ImGui::SetCursorScreenPos(min - ImVec2(-8, ImGui::GetTextLineHeightWithSpacing() + 4)); + ImGui::BeginGroup(); + ImGui::PushID(node->getId() + 1000); + std::string tempName = node->getName(); + ImVec2 nameSize = ImGui::CalcTextSize(temp.c_str()); + ImGui::PushItemWidth(nameSize.x); + ImGui::InputText("##edit", &tempName); + node->setName(tempName); + ImGui::PopID(); + ImGui::EndGroup(); + + auto drawList = ed::GetHintBackgroundDrawList(); + + ImRect hintBounds = ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); + ImRect hintFrameBounds = expandImRect(hintBounds, 8, 4); + + drawList->AddRectFilled( + hintFrameBounds.GetTL(), + hintFrameBounds.GetBR(), + IM_COL32(255, 255, 255, 64 * bgAlpha / 255), 4.0f); + + drawList->AddRect( + hintFrameBounds.GetTL(), + hintFrameBounds.GetBR(), + IM_COL32(0, 255, 255, 128 * bgAlpha / 255), 4.0f); + } + ed::EndGroupHint(); +} +bool Graph::readOnly() +{ + // if the sources are not the same then the current graph cannot be modified + return _currGraphElem->getActiveSourceUri() != _graphDoc->getActiveSourceUri(); +} +mx::InputPtr Graph::findInput(mx::InputPtr nodeInput, std::string name) +{ + if (_isNodeGraph) + { + for (UiNodePtr node : _graphNodes) + { + if (node->getNode()) + { + for (mx::InputPtr input : node->getNode()->getActiveInputs()) + { + if (input->getInterfaceInput()) + { + + if (input->getInterfaceInput() == nodeInput) + { + return input; + } + } + } + } + } + } + else + { + if (_currUiNode->getNodeGraph()) + { + for (mx::NodePtr node : _currUiNode->getNodeGraph()->getNodes()) + { + for (mx::InputPtr input : node->getActiveInputs()) + { + if (input->getInterfaceInput()) + { + + if (input->getInterfaceName() == name) + { + return input; + } + } + } + } + } + } + return nullptr; +} +// This function is based off the splitter function in the ImGui Node Editor blueprints-example.cpp +static bool Splitter(bool split_vertically, float thickness, float* size1, float* size2, float min_size1, float min_size2, float splitter_long_axis_size = -1.0f) +{ + using namespace ImGui; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiID id = window->GetID("##Splitter"); + ImRect bb; + bb.Min = window->DC.CursorPos + (split_vertically ? ImVec2(*size1, 0.0f) : ImVec2(0.0f, *size1)); + bb.Max = bb.Min + CalcItemSize(split_vertically ? ImVec2(thickness, splitter_long_axis_size) : ImVec2(splitter_long_axis_size, thickness), 0.0f, 0.0f); + return SplitterBehavior(bb, id, split_vertically ? ImGuiAxis_X : ImGuiAxis_Y, size1, size2, min_size1, min_size2, 0.0f); +} + +void Graph::outputPin(UiNodePtr node) +{ + // create output pin + float nodeWidth = 20 + ImGui::CalcTextSize(node->getName().c_str()).x; + if (nodeWidth < 75) + { + nodeWidth = 75; + } + const float labelWidth = ImGui::CalcTextSize("output").x; + + // create node editor pin + for (Pin pin : node->outputPins) + { + ImGui::Indent(nodeWidth - labelWidth); + ed::BeginPin(pin._pinId, ed::PinKind::Output); + ImGui::Text("%s", pin._name.c_str()); + ImGui::SameLine(); + if (!_pinFilterType.empty()) + { + if (_pinFilterType == pin._type) + { + DrawPinIcon(pin._type, true, DEFAULT_ALPHA); + } + else + { + DrawPinIcon(pin._type, true, FILTER_ALPHA); + } + } + else + { + DrawPinIcon(pin._type, true, DEFAULT_ALPHA); + } + + ed::EndPin(); + ImGui::Unindent(nodeWidth - labelWidth); + } +} + +void Graph::createInputPin(Pin pin) +{ + ed::BeginPin(pin._pinId, ed::PinKind::Input); + ImGui::PushID(int(pin._pinId.Get())); + if (!_pinFilterType.empty()) + { + if (_pinFilterType == pin._type) + { + DrawPinIcon(pin._type, true, DEFAULT_ALPHA); + } + else + { + DrawPinIcon(pin._type, true, FILTER_ALPHA); + } + } + else + { + DrawPinIcon(pin._type, true, DEFAULT_ALPHA); + } + + ImGui::SameLine(); + ImGui::TextUnformatted(pin._name.c_str()); + ed::EndPin(); + ImGui::PopID(); +} + +std::vector Graph::createNodes(bool nodegraph) +{ + std::vector outputNum; + + for (UiNodePtr node : _graphNodes) + { + if (node->getCategory() == "group") + { + buildGroupNode(node); + } + else + { + // color for output pin + std::string outputType; + if (node->getNode() != nullptr) + { + ed::BeginNode(node->getId()); + ImGui::PushID(node->getId()); + ImGui::SetWindowFontScale(1.2f); + ImGui::GetWindowDrawList()->AddRectFilled( + ImGui::GetCursorScreenPos() + ImVec2(-7.0, -8.0), + ImGui::GetCursorScreenPos() + ImVec2(ed::GetNodeSize(node->getId()).x - 9.f, ImGui::GetTextLineHeight() + 2.f), + ImColor(ImColor(55, 55, 55, 255)), 12.f); + ImGui::GetWindowDrawList()->AddRectFilled( + ImGui::GetCursorScreenPos() + ImVec2(-7.0, 3), + ImGui::GetCursorScreenPos() + ImVec2(ed::GetNodeSize(node->getId()).x - 9.f, ImGui::GetTextLineHeight() + 2.f), + ImColor(ImColor(55, 55, 55, 255)), 0.f); + ImGui::Text("%s", node->getName().c_str()); + ImGui::SetWindowFontScale(1); + + outputPin(node); + for (Pin pin : node->inputPins) + { + UiNodePtr upUiNode = node->getConnectedNode(pin._name); + if (upUiNode) + { + if (upUiNode->outputPins.size() > 0) + { + upUiNode->outputPins[0].addConnection(pin); + } + pin.setConnected(true); + } + if (node->_showAllInputs || (pin.getConnected() || node->getNode()->getInput(pin._name))) + { + createInputPin(pin); + } + } + // set color of output pin + + if (node->getNode()->getType() == mx::SURFACE_SHADER_TYPE_STRING) + { + if (node->getOutputConnections().size() > 0) + { + for (UiNodePtr outputCon : node->getOutputConnections()) + { + outputNum.push_back(findNode(outputCon->getId())); + } + } + } + } + else if (node->getInput() != nullptr) + { + ed::BeginNode(node->getId()); + ImGui::PushID(node->getId()); + ImGui::SetWindowFontScale(1.2f); + ImGui::GetWindowDrawList()->AddRectFilled( + ImGui::GetCursorScreenPos() + ImVec2(-7.0f, -8.0f), + ImGui::GetCursorScreenPos() + ImVec2(ed::GetNodeSize(node->getId()).x - 9.f, ImGui::GetTextLineHeight() + 2.f), + ImColor(ImColor(85, 85, 85, 255)), 12.f); + ImGui::GetWindowDrawList()->AddRectFilled( + ImGui::GetCursorScreenPos() + ImVec2(-7.0f, 3.f), + ImGui::GetCursorScreenPos() + ImVec2(ed::GetNodeSize(node->getId()).x - 9.f, ImGui::GetTextLineHeight() + 2.f), + ImColor(ImColor(85, 85, 85, 255)), 0.f); + ImGui::Text("%s", node->getName().c_str()); + ImGui::SetWindowFontScale(1); + + outputType = node->getInput()->getType(); + outputPin(node); + for (Pin pin : node->inputPins) + { + UiNodePtr upUiNode = node->getConnectedNode(node->getName()); + if (upUiNode) + { + if (upUiNode->outputPins.size()) + { + upUiNode->outputPins[0].addConnection(pin); + } + pin.setConnected(true); + } + ed::BeginPin(pin._pinId, ed::PinKind::Input); + if (!_pinFilterType.empty()) + { + if (_pinFilterType == pin._type) + { + DrawPinIcon(pin._type, true, DEFAULT_ALPHA); + } + else + { + DrawPinIcon(pin._type, true, FILTER_ALPHA); + } + } + else + { + DrawPinIcon(pin._type, true, DEFAULT_ALPHA); + } + + ImGui::SameLine(); + ImGui::TextUnformatted("value"); + ed::EndPin(); + } + } + else if (node->getOutput() != nullptr) + { + + ed::BeginNode(node->getId()); + ImGui::PushID(node->getId()); + ImGui::SetWindowFontScale(1.2f); + ImGui::GetWindowDrawList()->AddRectFilled( + ImGui::GetCursorScreenPos() + ImVec2(-7.0, -8.0), + ImGui::GetCursorScreenPos() + ImVec2(ed::GetNodeSize(node->getId()).x - 9.f, ImGui::GetTextLineHeight() + 2.f), + ImColor(ImColor(35, 35, 35, 255)), 12.f); + ImGui::GetWindowDrawList()->AddRectFilled( + ImGui::GetCursorScreenPos() + ImVec2(-7.0, 3), + ImGui::GetCursorScreenPos() + ImVec2(ed::GetNodeSize(node->getId()).x - 9.f, ImGui::GetTextLineHeight() + 2.f), + ImColor(ImColor(35, 35, 35, 255)), 0); + ImGui::Text("%s", node->getName().c_str()); + ImGui::SetWindowFontScale(1.0); + + outputType = node->getOutput()->getType(); + outputPin(node); + + for (Pin pin : node->inputPins) + { + UiNodePtr upUiNode = node->getConnectedNode(""); + if (upUiNode) + { + if (upUiNode->outputPins.size()) + { + upUiNode->outputPins[0].addConnection(pin); + } + } + + ed::BeginPin(pin._pinId, ed::PinKind::Input); + if (!_pinFilterType.empty()) + { + if (_pinFilterType == pin._type) + { + DrawPinIcon(pin._type, true, DEFAULT_ALPHA); + } + else + { + DrawPinIcon(pin._type, true, FILTER_ALPHA); + } + } + else + { + DrawPinIcon(pin._type, true, DEFAULT_ALPHA); + } + ImGui::SameLine(); + ImGui::TextUnformatted("input"); + + ed::EndPin(); + } + if (nodegraph) + { + outputNum.push_back(findNode(node->getId())); + } + } + else if (node->getNodeGraph() != nullptr) + { + ed::BeginNode(node->getId()); + ImGui::PushID(node->getId()); + ImGui::SetWindowFontScale(1.2f); + ImGui::GetWindowDrawList()->AddRectFilled( + ImGui::GetCursorScreenPos() + ImVec2(-7.0, -8.0), + ImGui::GetCursorScreenPos() + ImVec2(ed::GetNodeSize(node->getId()).x - 9.f, ImGui::GetTextLineHeight() + 2.f), + ImColor(ImColor(35, 35, 35, 255)), 12.f); + ImGui::GetWindowDrawList()->AddRectFilled( + ImGui::GetCursorScreenPos() + ImVec2(-7.0, 3), + ImGui::GetCursorScreenPos() + ImVec2(ed::GetNodeSize(node->getId()).x - 9.f, ImGui::GetTextLineHeight() + 2.f), + ImColor(ImColor(35, 35, 35, 255)), 0); + ImGui::Text("%s", node->getName().c_str()); + ImGui::SetWindowFontScale(1.0); + for (Pin pin : node->inputPins) + { + if (node->getConnectedNode(pin._name) != nullptr) + { + pin.setConnected(true); + } + if (node->_showAllInputs || (pin.getConnected() || node->getNodeGraph()->getInput(pin._name))) + { + createInputPin(pin); + } + } + outputPin(node); + } + ImGui::PopID(); + ed::EndNode(); + } + } + ImGui::SetWindowFontScale(1.0); + return outputNum; +} + +// add mx::InputPtr to node based off of input pin +void Graph::addNodeInput(UiNodePtr node, mx::InputPtr& input) +{ + if (node->getNode()) + { + if (!node->getNode()->getInput(input->getName())) + { + input = node->getNode()->addInput(input->getName(), input->getType()); + input->setConnectedNode(nullptr); + } + } +} +void Graph::setDefaults(mx::InputPtr input) +{ + if (input->getType() == "float") + { + + input->setValue(0.f, "float"); + } + else if (input->getType() == "integer") + { + + input->setValue(0, "integer"); + } + else if (input->getType() == "color3") + { + + input->setValue(mx::Color3(0.f, 0.f, 0.f), "color3"); + } + else if (input->getType() == "color4") + { + input->setValue(mx::Color4(0.f, 0.f, 0.f, 1.f), "color4"); + } + else if (input->getType() == "vector2") + { + input->setValue(mx::Vector2(0.f, 0.f), "vector2"); + } + else if (input->getType() == "vector3") + { + input->setValue(mx::Vector3(0.f, 0.f, 0.f), "vector3"); + } + else if (input->getType() == "vector4") + { + + input->setValue(mx::Vector4(0.f, 0.f, 0.f, 0.f), "vector4"); + } + else if (input->getType() == "string") + { + input->setValue("", "string"); + } + else if (input->getType() == "filename") + { + + input->setValue("", "filename"); + } + else if (input->getType() == "boolean") + { + + input->setValue(false, "boolean"); + } +} + +// add link to nodegraph and set up connections between UiNodes and MaterialX Nodes to update shader +void Graph::AddLink(ed::PinId inputPinId, ed::PinId outputPinId) +{ + int end_attr = int(outputPinId.Get()); + int start_attr = int(inputPinId.Get()); + Pin inputPin = getPin(outputPinId); + Pin outputPin = getPin(inputPinId); + if (inputPinId && outputPinId && (outputPin._type == inputPin._type)) + { + if (inputPin._connected == false) + { + + int upNode = getNodeId(inputPinId); + int downNode = getNodeId(outputPinId); + + // make sure there is an implementation for node + const mx::ShaderGenerator& shadergen = _renderer->getGenContext().getShaderGenerator(); + + // Find the implementation for this nodedef if not an input or output uinode + if (_graphNodes[downNode]->getInput() && _isNodeGraph) + { + ed::RejectNewItem(); + showLabel("Cannot connect to inputs inside of graph", ImColor(50, 50, 50, 255)); + return; + } + else if (_graphNodes[upNode]->getNode()) + { + mx::ShaderNodeImplPtr impl = shadergen.getImplementation(*_graphNodes[upNode]->getNode()->getNodeDef(), _renderer->getGenContext()); + if (!impl) + { + ed::RejectNewItem(); + showLabel("Invalid Connection: Node does not have an implementation", ImColor(50, 50, 50, 255)); + return; + } + } + + if (ed::AcceptNewItem()) + { + // Since we accepted new link, lets add one to our list of links. + Link link; + link._startAttr = start_attr; + link._endAttr = end_attr; + _currLinks.push_back(link); + _frameCount = ImGui::GetFrameCount(); + _renderer->setMaterialCompilation(true); + + if (_graphNodes[downNode]->getNode() || _graphNodes[downNode]->getNodeGraph()) + { + + mx::InputPtr connectingInput = nullptr; + for (Pin& pin : _graphNodes[downNode]->inputPins) + { + if (pin._pinId == outputPinId) + { + addNodeInput(_graphNodes[downNode], pin._input); + // update value to be empty + if (_graphNodes[downNode]->getNode() && _graphNodes[downNode]->getNode()->getType() == mx::SURFACE_SHADER_TYPE_STRING) + { + if (_graphNodes[upNode]->getOutput() != nullptr) + { + pin._input->setConnectedOutput(_graphNodes[upNode]->getOutput()); + } + else + { + // node graph + if (_graphNodes[upNode]->getNodeGraph() != nullptr) + { + for (Pin outPin : _graphNodes[upNode]->outputPins) + { + // set pin connection to correct output + if (outPin._pinId == inputPinId) + { + mx::OutputPtr outputs = _graphNodes[upNode]->getNodeGraph()->getOutput(outPin._name); + pin._input->setConnectedOutput(outputs); + } + } + } + else + { + pin._input->setConnectedNode(_graphNodes[upNode]->getNode()); + } + } + } + else + { + if (_graphNodes[upNode]->getInput()) + { + + pin._input->setInterfaceName(_graphNodes[upNode]->getName()); + } + else + { + if (_graphNodes[upNode]->getNode()) + { + pin._input->setConnectedNode(_graphNodes[upNode]->getNode()); + } + else if (_graphNodes[upNode]->getNodeGraph()) + { + for (Pin outPin : _graphNodes[upNode]->outputPins) + { + // set pin connection to correct output + if (outPin._pinId == inputPinId) + { + mx::OutputPtr outputs = _graphNodes[upNode]->getNodeGraph()->getOutput(outPin._name); + pin._input->setConnectedOutput(outputs); + } + } + } + } + } + + pin.setConnected(true); + pin._input->removeAttribute(mx::ValueElement::VALUE_ATTRIBUTE); + connectingInput = pin._input; + break; + } + } + // create new edge and set edge information + createEdge(_graphNodes[upNode], _graphNodes[downNode], connectingInput); + } + else if (_graphNodes[downNode]->getOutput() != nullptr) + { + mx::InputPtr connectingInput = nullptr; + _graphNodes[downNode]->getOutput()->setConnectedNode(_graphNodes[upNode]->getNode()); + + // create new edge and set edge information + createEdge(_graphNodes[upNode], _graphNodes[downNode], connectingInput); + } + else + { + // create new edge and set edge info + UiEdge newEdge = UiEdge(_graphNodes[upNode], _graphNodes[downNode], nullptr); + if (!edgeExists(newEdge)) + { + _graphNodes[downNode]->edges.push_back(newEdge); + _currEdge.push_back(newEdge); + + // update input node num and output connections + _graphNodes[downNode]->setInputNodeNum(1); + _graphNodes[upNode]->setOutputConnection(_graphNodes[downNode]); + } + } + } + } + else + { + ed::RejectNewItem(); + } + } + else + { + ed::RejectNewItem(); + showLabel("Invalid Connection due to Mismatch Types", ImColor(50, 50, 50, 255)); + } +} + +void Graph::deleteLinkInfo(int startAttr, int endAttr) +{ + int upNode = getNodeId(startAttr); + int downNode = getNodeId(endAttr); + int num = _graphNodes[downNode]->getEdgeIndex(_graphNodes[upNode]->getId()); + if (num != -1) + { + if (_graphNodes[downNode]->edges.size() == 1) + { + _graphNodes[downNode]->edges.erase(_graphNodes[downNode]->edges.begin() + 0); + } + else if (_graphNodes[downNode]->edges.size() > 1) + { + _graphNodes[downNode]->edges.erase(_graphNodes[downNode]->edges.begin() + num); + } + } + + // downNode set node num -1 + _graphNodes[downNode]->setInputNodeNum(-1); + // upNode remove outputconnection + _graphNodes[upNode]->removeOutputConnection(_graphNodes[downNode]->getName()); + // change input so that is default val + // change informtion of actual mx::Node + if (_graphNodes[downNode]->getNode()) + { + mx::NodeDefPtr nodeDef = _graphNodes[downNode]->getNode()->getNodeDef(_graphNodes[downNode]->getNode()->getName()); + + for (Pin& pin : _graphNodes[downNode]->inputPins) + { + + if ((int) pin._pinId.Get() == endAttr) + { + + mx::ValuePtr val = nodeDef->getActiveInput(pin._input->getName())->getValue(); + if (_graphNodes[downNode]->getNode()->getType() == mx::SURFACE_SHADER_TYPE_STRING && _graphNodes[upNode]->getNodeGraph()) + { + pin._input->setConnectedOutput(nullptr); + } + else + { + pin._input->setConnectedNode(nullptr); + } + if (_graphNodes[upNode]->getInput()) + { + // remove interface value in order to set the default of the input + pin._input->removeAttribute(mx::ValueElement::INTERFACE_NAME_ATTRIBUTE); + setDefaults(pin._input); + setDefaults(_graphNodes[upNode]->getInput()); + } + + pin.setConnected(false); + // if a value exists update the input with it + if (val) + { + pin._input->setValueString(val->getValueString()); + } + } + } + } + else if (_graphNodes[downNode]->getNodeGraph()) + { + // set default values for nodegraph node pins ie nodegraph inputs + mx::NodeDefPtr nodeDef = _graphNodes[downNode]->getNodeGraph()->getNodeDef(); + for (Pin pin : _graphNodes[downNode]->inputPins) + { + + if ((int) pin._pinId.Get() == endAttr) + { + + if (_graphNodes[upNode]->getInput()) + { + _graphNodes[downNode]->getNodeGraph()->getInput(pin._name)->removeAttribute(mx::ValueElement::INTERFACE_NAME_ATTRIBUTE); + setDefaults(_graphNodes[upNode]->getInput()); + } + pin._input->setConnectedNode(nullptr); + pin.setConnected(false); + setDefaults(pin._input); + } + } + } + else if (_graphNodes[downNode]->getOutput()) + { + for (Pin pin : _graphNodes[downNode]->inputPins) + { + _graphNodes[downNode]->getOutput()->removeAttribute("nodename"); + pin.setConnected(false); + } + } +} +// delete link from currLink vector and remove any connections in UiNode or MaterialX Nodes to update shader +void Graph::deleteLink(ed::LinkId deletedLinkId) +{ + // If you agree that link can be deleted, accept deletion. + if (ed::AcceptDeletedItem()) + { + _renderer->setMaterialCompilation(true); + _frameCount = ImGui::GetFrameCount(); + int link_id = int(deletedLinkId.Get()); + // Then remove link from your data. + int pos = findLinkPosition(link_id); + + // link start -1 equals node num + Link currLink = _currLinks[pos]; + deleteLinkInfo(currLink._startAttr, currLink._endAttr); + _currLinks.erase(_currLinks.begin() + pos); + } +} + +void Graph::deleteNode(UiNodePtr node) +{ + // delete link + for (Pin inputPins : node->inputPins) + { + UiNodePtr upNode = node->getConnectedNode(inputPins._name); + if (upNode) + { + upNode->removeOutputConnection(node->getName()); + int num = node->getEdgeIndex(upNode->getId()); + // erase edge between node and up node + if (num != -1) + { + if (node->edges.size() == 1) + { + node->edges.erase(node->edges.begin() + 0); + } + else if (node->edges.size() > 1) + { + node->edges.erase(node->edges.begin() + num); + } + } + } + } + // update downNode info + std::vector outputConnections = node->outputPins.front().getConnection(); + + for (Pin pin : outputConnections) + { + mx::ValuePtr val; + if (pin._pinNode->getNode()) + { + mx::NodeDefPtr nodeDef = pin._pinNode->getNode()->getNodeDef(pin._pinNode->getNode()->getName()); + val = nodeDef->getActiveInput(pin._input->getName())->getValue(); + if (pin._pinNode->getNode()->getType() == "surfaceshader") + { + pin._input->setConnectedOutput(nullptr); + } + else + { + pin._input->setConnectedNode(nullptr); + } + } + else if (pin._pinNode->getNodeGraph()) + { + if (node->getInput()) + { + pin._pinNode->getNodeGraph()->getInput(pin._name)->removeAttribute(mx::ValueElement::INTERFACE_NAME_ATTRIBUTE); + setDefaults(node->getInput()); + } + pin._input->setConnectedNode(nullptr); + pin.setConnected(false); + setDefaults(pin._input); + } + + pin.setConnected(false); + if (val) + { + pin._input->setValueString(val->getValueString()); + } + + int num = pin._pinNode->getEdgeIndex(node->getId()); + if (num != -1) + { + if (pin._pinNode->edges.size() == 1) + { + pin._pinNode->edges.erase(pin._pinNode->edges.begin() + 0); + } + else if (pin._pinNode->edges.size() > 1) + { + pin._pinNode->edges.erase(pin._pinNode->edges.begin() + num); + } + } + + pin._pinNode->setInputNodeNum(-1); + // not really necessary since it will be deleted + node->removeOutputConnection(pin._pinNode->getName()); + } + + // remove from NodeGraph + // all link information is handled in delete link which is called before this + int nodeNum = findNode(node->getId()); + _currGraphElem->removeChild(node->getName()); + _graphNodes.erase(_graphNodes.begin() + nodeNum); +} + +// create pins for outputs/inputs added while inside the node graph +void Graph::addNodeGraphPins() +{ + for (UiNodePtr node : _graphNodes) + { + if (node->getNodeGraph()) + { + if (node->inputPins.size() != node->getNodeGraph()->getInputs().size()) + { + for (mx::InputPtr input : node->getNodeGraph()->getInputs()) + { + std::string name = input->getName(); + auto result = std::find_if(node->inputPins.begin(), node->inputPins.end(), [name](const Pin& x) + { + return x._name == name; + }); + if (result == node->inputPins.end()) + { + Pin inPin = Pin(++_graphTotalSize, &*input->getName().begin(), input->getType(), node, ax::NodeEditor::PinKind::Input, input, nullptr); + node->inputPins.push_back(inPin); + _currPins.push_back(inPin); + ++_graphTotalSize; + } + } + } + if (node->outputPins.size() != node->getNodeGraph()->getOutputs().size()) + { + for (mx::OutputPtr output : node->getNodeGraph()->getOutputs()) + { + std::string name = output->getName(); + auto result = std::find_if(node->outputPins.begin(), node->outputPins.end(), [name](const Pin& x) + { + return x._name == name; + }); + if (result == node->outputPins.end()) + { + Pin outPin = Pin(++_graphTotalSize, &*output->getName().begin(), output->getType(), node, ax::NodeEditor::PinKind::Output, nullptr, nullptr); + ++_graphTotalSize; + node->outputPins.push_back(outPin); + _currPins.push_back(outPin); + } + } + } + } + } +} + +void Graph::upNodeGraph() +{ + if (!_graphStack.empty()) + { + savePosition(); + _graphNodes = _graphStack.top(); + _currPins = _pinStack.top(); + _graphTotalSize = _sizeStack.top(); + addNodeGraphPins(); + _graphStack.pop(); + _pinStack.pop(); + _sizeStack.pop(); + _currGraphName.pop_back(); + _initial = true; + ed::NavigateToContent(); + if (_currUiNode) + { + ed::DeselectNode(_currUiNode->getId()); + _currUiNode = nullptr; + } + _prevUiNode = nullptr; + _isNodeGraph = false; + _currGraphElem = _graphDoc; + _initial = true; + } +} + +void Graph::graphButtons() +{ + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.15f, .15f, .15f, 1.0f)); + + // buttons for loading and saving a .mtlx + // new Material button + if (ImGui::Button("New Material")) + { + _graphNodes.clear(); + _currLinks.clear(); + _currEdge.clear(); + _newLinks.clear(); + _currPins.clear(); + _graphDoc = nullptr; + if (_currUiNode != nullptr) + { + ed::DeselectNode(_currUiNode->getId()); + _currUiNode = nullptr; + } + _prevUiNode = nullptr; + _currRenderNode = nullptr; + _isNodeGraph = false; + _currGraphName.clear(); + } + ImGui::SameLine(); + if (ImGui::Button("Load Material")) + { + // deselect node before loading new file + if (_currUiNode != nullptr) + { + ed::DeselectNode(_currUiNode->getId()); + _currUiNode = nullptr; + } + + _fileDialog.SetTitle("Open File Window"); + _fileDialog.Open(); + } + ImGui::SameLine(); + if (ImGui::Button("Save Material")) + { + _fileDialogSave.SetTitle("Save File Window"); + _fileDialogSave.Open(); + } + ImGui::SameLine(); + if (ImGui::Button("Auto Layout")) + { + _autoLayout = true; + } + + // split window into panes for NodeEditor + static float leftPaneWidth = 375.0f; + static float rightPaneWidth = 750.0f; + Splitter(true, 4.0f, &leftPaneWidth, &rightPaneWidth, 20.0f, 20.0f); + // create back button and graph hiearchy name display + ImGui::Indent(leftPaneWidth + 15.f); + if (ImGui::Button("<")) + { + upNodeGraph(); + } + ImGui::SameLine(); + if (!_currGraphName.empty()) + { + for (std::string name : _currGraphName) + { + ImGui::Text("%s", name.c_str()); + ImGui::SameLine(); + if (name != _currGraphName.back()) + { + ImGui::Text(">"); + ImGui::SameLine(); + } + } + } + ImVec2 windowPos2 = ImGui::GetWindowPos(); + ImGui::Unindent(leftPaneWidth + 15.f); + ImGui::PopStyleColor(); + ImGui::NewLine(); + // creating two windows using splitter + float paneWidth = (leftPaneWidth - 2.0f); + ImGui::BeginChild("Selection", ImVec2(paneWidth, 0)); + ImVec2 windowPos = ImGui::GetWindowPos(); + // renderView window + ImVec2 wsize = ImVec2((float) _renderer->_screenWidth, (float) _renderer->_screenHeight); + float aspectRatio = _renderer->_pixelRatio; + ImVec2 screenSize = ImVec2(paneWidth, paneWidth / aspectRatio); + _renderer->_screenWidth = (unsigned int) screenSize[0]; + _renderer->_screenHeight = (unsigned int) screenSize[1]; + + if (_renderer != nullptr) + { + + glEnable(GL_FRAMEBUFFER_SRGB); + _renderer->getViewCamera()->setViewportSize(mx::Vector2(screenSize[0], screenSize[1])); + GLuint64 my_image_texture = _renderer->_textureID; + mx::Vector2 vec = _renderer->getViewCamera()->getViewportSize(); + // current image has correct color space but causes problems for gui + ImGui::Image((ImTextureID) my_image_texture, screenSize, ImVec2(0, 1), ImVec2(1, 0)); + } + ImGui::Separator(); + + // property editor for current nodes + propertyEditor(); + ImGui::EndChild(); + ImGui::SameLine(0.0f, 12.0f); + + handleRenderViewInputs(windowPos, screenSize[0], screenSize[1]); +} +void Graph::propertyEditor() +{ + ImGui::Text("Node Property Editor"); + if (_currUiNode) + { + // set and edit name + ImGui::Text("Name: "); + ImGui::SameLine(); + std::string original = _currUiNode->getName(); + std::string temp = original; + ImGui::PushItemWidth(100.0f); + ImGui::InputText("##edit", &temp); + ImGui::PopItemWidth(); + std::string docString = "NodeDef Doc String: \n"; + if (_currUiNode->getNode()) + { + if (temp != original) + { + + std::string name = _currUiNode->getNode()->getParent()->createValidChildName(temp); + + std::vector downstreamNodes = _currUiNode->getOutputConnections(); + for (UiNodePtr nodes : downstreamNodes) + { + if (nodes->getInput() == nullptr) + { + for (mx::InputPtr input : nodes->getNode()->getActiveInputs()) + { + if (input->getConnectedNode() == _currUiNode->getNode()) + { + _currUiNode->getNode()->setName(name); + nodes->getNode()->setConnectedNode(input->getName(), _currUiNode->getNode()); + } + } + } + } + _currUiNode->setName(name); + _currUiNode->getNode()->setName(name); + } + } + else if (_currUiNode->getInput()) + { + if (temp != original) + { + + std::string name = _currUiNode->getInput()->getParent()->createValidChildName(temp); + + std::vector downstreamNodes = _currUiNode->getOutputConnections(); + for (UiNodePtr nodes : downstreamNodes) + { + if (nodes->getInput() == nullptr) + { + if (nodes->getNode()) + { + for (mx::InputPtr input : nodes->getNode()->getActiveInputs()) + { + if (input->getInterfaceInput() == _currUiNode->getInput()) + { + _currUiNode->getInput()->setName(name); + mx::ValuePtr val = _currUiNode->getInput()->getValue(); + input->setInterfaceName(name); + mx::InputPtr pt = input->getInterfaceInput(); + } + } + } + else + { + nodes->getOutput()->setConnectedNode(_currUiNode->getNode()); + } + } + } + + _currUiNode->getInput()->setName(name); + _currUiNode->setName(name); + } + } + else if (_currUiNode->getOutput()) + { + if (temp != original) + { + std::string name = _currUiNode->getOutput()->getParent()->createValidChildName(temp); + _currUiNode->getOutput()->setName(name); + _currUiNode->setName(name); + } + } + else if (_currUiNode->getCategory() == "group") + { + _currUiNode->setName(temp); + } + else if (_currUiNode->getCategory() == "nodegraph") + { + if (temp != original) + { + std::string name = _currUiNode->getNodeGraph()->getParent()->createValidChildName(temp); + _currUiNode->getNodeGraph()->setName(name); + _currUiNode->setName(name); + } + } + + ImGui::Text("Category:"); + ImGui::SameLine(); + // change button color to match background + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.096f, .096f, .096f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.1f, .1f, .1f, 1.0f)); + if (_currUiNode->getNode()) + { + ImGui::Text("%s", _currUiNode->getNode()->getCategory().c_str()); + docString += _currUiNode->getNode()->getCategory().c_str(); + docString += ":"; + docString += _currUiNode->getNode()->getNodeDef()->getDocString() + "\n"; + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) + { + ImGui::SetTooltip("%s", _currUiNode->getNode()->getNodeDef()->getDocString().c_str()); + } + + ImGui::Text("Inputs:"); + ImGui::Indent(); + + for (Pin& input : _currUiNode->inputPins) + { + if (_currUiNode->_showAllInputs || (input.getConnected() || _currUiNode->getNode()->getInput(input._name))) + { + mx::OutputPtr out = input._input->getConnectedOutput(); + // setting comment help box + ImGui::PushID(int(input._pinId.Get())); + ImGui::Text("%s", input._input->getName().c_str()); + mx::InputPtr tempInt = _currUiNode->getNode()->getNodeDef()->getActiveInput(input._input->getName()); + docString += input._name; + docString += ": "; + if (tempInt) + { + std::string newStr = _currUiNode->getNode()->getNodeDef()->getActiveInput(input._input->getName())->getDocString(); + if (newStr != mx::EMPTY_STRING) + { + docString += newStr; + } + } + docString += "\t \n"; + ImGui::SameLine(); + std::string typeText = " [" + input._input->getType() + "]"; + ImGui::Text("%s", typeText.c_str()); + + // setting constant sliders for input values + if (!input.getConnected()) + { + setConstant(_currUiNode, input._input); + } + + ImGui::PopID(); + } + } + + ImGui::Unindent(); + ImGui::Checkbox("Show all inputs", &_currUiNode->_showAllInputs); + } + + else if (_currUiNode->getInput() != nullptr) + { + ImGui::Text("%s", _currUiNode->getCategory().c_str()); + std::vector inputs = _currUiNode->inputPins; + ImGui::Text("Inputs:"); + ImGui::Indent(); + for (size_t i = 0; i < inputs.size(); i++) + { + + // setting comment help box + ImGui::PushID(int(inputs[i]._pinId.Get())); + ImGui::Text("%s", inputs[i]._input->getName().c_str()); + + ImGui::SameLine(); + std::string typeText = " [" + inputs[i]._input->getType() + "]"; + ImGui::Text("%s", typeText.c_str()); + // setting constant sliders for input values + if (!inputs[i].getConnected()) + { + setConstant(_currUiNode, inputs[i]._input); + } + ImGui::PopID(); + } + ImGui::Unindent(); + } + else if (_currUiNode->getOutput() != nullptr) + { + ImGui::Text("%s", _currUiNode->getOutput()->getCategory().c_str()); + } + else if (_currUiNode->getNodeGraph() != nullptr) + { + std::vector inputs = _currUiNode->inputPins; + ImGui::Text("%s", _currUiNode->getCategory().c_str()); + ImGui::Text("Inputs:"); + ImGui::Indent(); + int count = 0; + for (Pin input : inputs) + { + if (_currUiNode->_showAllInputs || (input.getConnected() || _currUiNode->getNodeGraph()->getInput(input._name))) + { + // setting comment help box + ImGui::PushID(int(input._pinId.Get())); + ImGui::Text("%s", input._input->getName().c_str()); + + docString += _currUiNode->getNodeGraph()->getActiveInput(input._input->getName())->getDocString(); + + ImGui::SameLine(); + std::string typeText = " [" + input._input->getType() + "]"; + ImGui::Text("%s", typeText.c_str()); + if (!input._input->getConnectedNode() && _currUiNode->getNodeGraph()->getActiveInput(input._input->getName())) + { + setConstant(_currUiNode, input._input); + } + + ImGui::PopID(); + count++; + } + } + ImGui::Unindent(); + ImGui::Checkbox("Show all inputs", &_currUiNode->_showAllInputs); + } + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + + if (ImGui::Button("Node Info")) + { + ImGui::OpenPopup("docstring"); + } + + if (ImGui::BeginPopup("docstring")) + { + ImGui::Text("%s", docString.c_str()); + ImGui::EndPopup(); + } + } +} +void Graph::addNodePopup(bool cursor) +{ + bool open_AddPopup = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && ImGui::IsKeyReleased(GLFW_KEY_TAB); + if (open_AddPopup) + { + cursor = true; + ImGui::OpenPopup("add node"); + } + if (ImGui::BeginPopup("add node")) + { + + // check if there is a document + if (_graphDoc == nullptr) + { + // when creating files from scratch + mx::DocumentPtr doc = mx::createDocument(); + doc->importLibrary(_stdLib); + _graphDoc = doc; + _currGraphElem = _graphDoc; + addExtraNodes(); + } + + ImGui::Text("Add Node"); + ImGui::Separator(); + static char input[16]{ "" }; + if (cursor) + { + ImGui::SetKeyboardFocusHere(); + } + ImGui::InputText("##input", input, sizeof(input)); + std::string subs(input); + // input string length + // filter extra nodes - includes inputs, outputs, groups, and node graphs + for (std::unordered_map>>::iterator it = _extraNodes.begin(); it != _extraNodes.end(); ++it) + { + // filter out list of nodes + if (subs.size() > 0) + { + for (size_t i = 0; i < it->second.size(); i++) + { + std::string str(it->second[i][0]); + std::string nodeName = it->second[i][0]; + if (str.find(subs) != std::string::npos) + { + if (ImGui::MenuItem(nodeName.substr(3, nodeName.length()).c_str()) || (ImGui::IsItemFocused() && ImGui::IsKeyPressedMap(ImGuiKey_Enter))) + { + addNode(it->second[i][2], nodeName.substr(3, nodeName.length()), it->second[i][1]); + _addNewNode = true; + memset(input, '\0', sizeof(input)); + } + } + } + } + else + { + ImGui::SetNextWindowSizeConstraints(ImVec2(100, 10), ImVec2(250, 300)); + if (ImGui::BeginMenu(it->first.c_str())) + { + for (size_t j = 0; j < it->second.size(); j++) + { + std::string name = it->second[j][0]; + if (ImGui::MenuItem(name.substr(3, name.length()).c_str()) || (ImGui::IsItemFocused() && ImGui::IsKeyPressedMap(ImGuiKey_Enter))) + { + addNode(it->second[j][2], name.substr(3, name.length()), it->second[j][1]); + _addNewNode = true; + } + } + ImGui::EndMenu(); + } + } + } + // filter nodedefs and add to menu if matches filter + for (std::unordered_map>::iterator it = _nodesToAdd.begin(); it != _nodesToAdd.end(); ++it) + { + // filter out list of nodes + if (subs.size() > 0) + { + for (size_t i = 0; i < it->second.size(); i++) + { + std::string str(it->second[i]->getName()); + std::string nodeName = it->second[i]->getName(); + if (str.find(subs) != std::string::npos) + { + if (ImGui::MenuItem(it->second[i]->getName().substr(3, nodeName.length()).c_str()) || (ImGui::IsItemFocused() && ImGui::IsKeyPressedMap(ImGuiKey_Enter))) + { + addNode(it->second[i]->getNodeString(), it->second[i]->getName().substr(3, nodeName.length()), it->second[i]->getType()); + _addNewNode = true; + memset(input, '\0', sizeof(input)); + } + } + } + } + else + { + ImGui::SetNextWindowSizeConstraints(ImVec2(100, 10), ImVec2(250, 300)); + if (ImGui::BeginMenu(it->first.c_str())) + { + for (size_t i = 0; i < it->second.size(); i++) + { + + std::string name = it->second[i]->getName(); + if (ImGui::MenuItem(it->second[i]->getName().substr(3, name.length()).c_str()) || (ImGui::IsItemFocused() && ImGui::IsKeyPressedMap(ImGuiKey_Enter))) + { + addNode(it->second[i]->getNodeString(), it->second[i]->getName().substr(3, name.length()), it->second[i]->getType()); + _addNewNode = true; + } + } + ImGui::EndMenu(); + } + } + } + cursor = false; + ImGui::EndPopup(); + open_AddPopup = false; + } +} +void Graph::searchNodePopup(bool cursor) +{ + const bool open_search = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && ImGui::IsKeyDown(GLFW_KEY_F) && ImGui::IsKeyDown(GLFW_KEY_LEFT_CONTROL); + if (open_search) + { + cursor = true; + ImGui::OpenPopup("search"); + } + if (ImGui::BeginPopup("search")) + { + ed::NavigateToSelection(); + static ImGuiTextFilter filter; + ImGui::Text("Search for Node:"); + static char input[16]{ "" }; + ImGui::SameLine(); + if (cursor) + { + ImGui::SetKeyboardFocusHere(); + } + ImGui::InputText("##input", input, sizeof(input)); + + if (std::string(input).size() > 0) + { + + for (UiNodePtr node : _graphNodes) + { + if (node->getName().find(std::string(input)) != std::string::npos) + { + + if (ImGui::MenuItem(node->getName().c_str()) || (ImGui::IsItemFocused() && ImGui::IsKeyPressedMap(ImGuiKey_Enter))) + { + _searchNodeId = node->getId(); + memset(input, '\0', sizeof(input)); + } + } + } + } + cursor = false; + ImGui::EndPopup(); + } +} + +void Graph::readOnlyPopup() +{ + if (_popup) + { + ImGui::SetNextWindowSize(ImVec2(200, 100)); + ImGui::OpenPopup("Read Only"); + _popup = false; + } + if (ImGui::BeginPopup("Read Only")) + { + ImGui::Text("This graph is Read Only"); + ImGui::EndPopup(); + } +} + +// compiling shaders message +void Graph::shaderPopup() +{ + if (_renderer->getMaterialCompilation()) + { + ImGui::SetNextWindowPos(ImVec2((float) _renderer->_screenWidth - 135, (float) _renderer->_screenHeight + 5)); + ImGui::SetNextWindowBgAlpha(80.f); + ImGui::OpenPopup("Shaders"); + } + if (ImGui::BeginPopup("Shaders")) + { + ImGui::Text("Compiling Shaders"); + if (!_renderer->getMaterialCompilation()) + { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } +} + +// allow for camera manipulation of render view window +void Graph::handleRenderViewInputs(ImVec2 minValue, float width, float height) +{ + ImVec2 mousePos = ImGui::GetMousePos(); + mx::Vector2 mxMousePos = mx::Vector2(mousePos.x, mousePos.y); + ImVec2 dragDelta = ImGui::GetMouseDragDelta(); + float scrollAmt = ImGui::GetIO().MouseWheel; + int button = -1; + bool down = false; + if (mousePos.x > minValue.x && mousePos.x < (minValue.x + width) && mousePos.y > minValue.y && mousePos.y < (minValue.y + height)) + { + if (ImGui::IsMouseDragging(0) || ImGui::IsMouseDragging(1)) + { + _renderer->setMouseMotionEvent(mxMousePos); + } + if (ImGui::IsMouseClicked(0)) + { + button = 0; + down = true; + _renderer->setMouseButtonEvent(button, down, mxMousePos); + } + else if (ImGui::IsMouseClicked(1)) + { + button = 1; + down = true; + _renderer->setMouseButtonEvent(button, down, mxMousePos); + } + else if (ImGui::IsMouseReleased(0)) + { + button = 0; + _renderer->setMouseButtonEvent(button, down, mxMousePos); + } + else if (ImGui::IsMouseReleased(1)) + { + button = 1; + _renderer->setMouseButtonEvent(button, down, mxMousePos); + } + else if (ImGui::IsKeyPressed(ImGuiKey_KeypadAdd)) + { + _renderer->setKeyEvent(ImGuiKey_KeypadAdd); + } + else if (ImGui::IsKeyPressed(ImGuiKey_KeypadSubtract)) + { + _renderer->setKeyEvent(ImGuiKey_KeypadSubtract); + } + // scrolling not possible if open or save file dialog is open + if (scrollAmt != 0 && !_fileDialogSave.IsOpened() && !_fileDialog.IsOpened()) + { + _renderer->setScrollEvent(scrollAmt); + } + } +} +// sets up graph editor +void Graph::drawGraph(ImVec2 mousePos) +{ + + if (_searchNodeId > 0) + { + ed::SelectNode(_searchNodeId); + ed::NavigateToSelection(); + _searchNodeId = -1; + } + + bool TextCursor = false; + // center imgui window and setting size + ImGuiIO& io2 = ImGui::GetIO(); + ImGui::SetNextWindowSize(io2.DisplaySize); + ImGui::SetNextWindowPos(ImVec2(io2.DisplaySize.x * 0.5f, io2.DisplaySize.y * 0.5f), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); + ImGui::Begin("MaterialX", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings); + + io2.ConfigFlags = ImGuiConfigFlags_IsSRGB | ImGuiConfigFlags_NavEnableKeyboard; + io2.MouseDoubleClickTime = .5; + // increase default font size + ImFont* f = ImGui::GetFont(); + f->FontSize = 14; + + graphButtons(); + + ed::Begin("My Editor"); + { + ed::Suspend(); + // set up pop ups for adding a node when tab is pressed + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f)); + ImGui::SetNextWindowSize({ 250.0f, 300.0f }); + addNodePopup(TextCursor); + searchNodePopup(TextCursor); + readOnlyPopup(); + ImGui::PopStyleVar(); + + ed::Resume(); + + // Gathering selected nodes / links - from ImGui Node Editor blueprints-example.cpp + std::vector selectedNodes; + std::vector selectedLinks; + selectedNodes.resize(ed::GetSelectedObjectCount()); + selectedLinks.resize(ed::GetSelectedObjectCount()); + + int nodeCount = ed::GetSelectedNodes(selectedNodes.data(), static_cast(selectedNodes.size())); + int linkCount = ed::GetSelectedLinks(selectedLinks.data(), static_cast(selectedLinks.size())); + + selectedNodes.resize(nodeCount); + selectedLinks.resize(linkCount); + if (io2.KeyCtrl && io2.MouseDown[0]) + { + _ctrlClick = true; + } + + // setting current node based off of selected node + if (selectedNodes.size() > 0) + { + int graphPos = findNode(int(selectedNodes[0].Get())); + if (graphPos > -1) + { + // only selected not if its not the same as previously selected + if (!_prevUiNode || (_prevUiNode->getName() != _graphNodes[graphPos]->getName())) + { + _currUiNode = _graphNodes[graphPos]; + // update render material if needed + if (_currUiNode->getNode()) + { + if (_currUiNode->getNode()->getType() == mx::SURFACE_SHADER_TYPE_STRING || _currUiNode->getNode()->getType() == mx::MATERIAL_TYPE_STRING) + { + setRenderMaterial(_currUiNode); + } + } + else if (_currUiNode->getNodeGraph()) + { + setRenderMaterial(_currUiNode); + } + else if (_currUiNode->getOutput()) + { + setRenderMaterial(_currUiNode); + } + _prevUiNode = _currUiNode; + } + } + } + + // check if keyboard shortcuts for copy/cut/paste have been used + if (ed::BeginShortcut()) + { + if (ed::AcceptCopy()) + { + _copiedNodes.clear(); + for (ed::NodeId selected : selectedNodes) + { + int pos = findNode((int) selected.Get()); + if (pos >= 0) + { + _copiedNodes.insert(std::pair(_graphNodes[pos], nullptr)); + } + } + } + else if (ed::AcceptCut()) + { + if (!readOnly()) + { + _copiedNodes.clear(); + // same as copy but remove from graphNodes + for (ed::NodeId selected : selectedNodes) + { + int pos = findNode((int) selected.Get()); + if (pos >= 0) + { + _copiedNodes.insert(std::pair(_graphNodes[pos], nullptr)); + } + } + _isCut = true; + } + else + { + _popup = true; + } + } + else if (ed::AcceptPaste()) + { + if (!readOnly()) + { + for (std::map::iterator iter = _copiedNodes.begin(); iter != _copiedNodes.end(); iter++) + { + copyUiNode(iter->first); + } + _addNewNode = true; + } + else + { + _popup = true; + } + } + } + + // set y position of first node + std::vector outputNum = createNodes(_isNodeGraph); + + // address copy information if applicable and relink graph if a new node has been added + if (_addNewNode) + { + copyInputs(); + linkGraph(); + ImVec2 canvasPos = ed::ScreenToCanvas(mousePos); + // place the copied nodes or the individual new nodes + if ((int) _copiedNodes.size() > 0) + { + positionPasteBin(canvasPos); + } + else + { + ed::SetNodePosition(_graphNodes.back()->getId(), canvasPos); + } + _copiedNodes.clear(); + _addNewNode = false; + } + // layout and link graph during the initial call of drawGraph() + if (_initial || _autoLayout) + { + _currLinks.clear(); + float y = 0.f; + _levelMap = std::unordered_map>(); + // start layout with output or material nodes since layout algorithm works right to left + for (int outN : outputNum) + { + layoutPosition(_graphNodes[outN], ImVec2(1200.f, y), true, 0); + y += 350; + } + // if there are no output or material nodes but the nodes have position layout each individual node + if (_graphNodes.size() > 0) + { + + if (outputNum.size() == 0 && _graphNodes[0]->getMxElement()) + { + if (_graphNodes[0]->getMxElement()->hasAttribute("xpos")) + { + for (UiNodePtr node : _graphNodes) + { + layoutPosition(node, ImVec2(0, 0), true, 0); + } + } + } + } + linkGraph(); + findYSpacing(0.f); + layoutInputs(); + // automatically frame node graph upon loading + ed::NavigateToContent(); + } + if (_delete) + { + linkGraph(); + + _delete = false; + } + connectLinks(); + // set to false after intial layout so that nodes can be moved + _initial = false; + _autoLayout = false; + // delete selected nodes and their links if delete key is pressed or if the shortcut for cut is used + if (ImGui::IsKeyReleased(GLFW_KEY_DELETE) || _isCut) + { + + if (selectedNodes.size() > 0) + { + _frameCount = ImGui::GetFrameCount(); + _renderer->setMaterialCompilation(true); + for (ed::NodeId id : selectedNodes) + { + + if (int(id.Get()) > 0) + { + int pos = findNode(int(id.Get())); + if (pos >= 0 && !readOnly()) + { + deleteNode(_graphNodes[pos]); + _delete = true; + ed::DeselectNode(id); + ed::DeleteNode(id); + _currUiNode = nullptr; + } + else if (readOnly()) + { + _popup = true; + } + } + } + linkGraph(); + } + _isCut = false; + } + + // hotkey to frame selected node(s) + if (ImGui::IsKeyReleased(GLFW_KEY_F) && !_fileDialogSave.IsOpened()) + { + ed::NavigateToSelection(); + } + + // go back up from inside a subgraph + if (ImGui::IsKeyReleased(GLFW_KEY_U) && (!ImGui::IsPopupOpen("add node")) && (!ImGui::IsPopupOpen("search")) && !_fileDialogSave.IsOpened()) + { + upNodeGraph(); + } + // adding new link + if (ed::BeginCreate()) + { + ed::PinId inputPinId, outputPinId, filterPinId; + if (ed::QueryNewLink(&inputPinId, &outputPinId)) + { + if (!readOnly()) + { + + AddLink(inputPinId, outputPinId); + } + else + { + _popup = true; + } + } + if (ed::QueryNewNode(&filterPinId)) + { + if (getPin(filterPinId)._type != "null") + { + _pinFilterType = getPin(filterPinId)._type; + } + } + } + else + { + _pinFilterType = mx::EMPTY_STRING; + } + ed::EndCreate(); + // deleting link + if (ed::BeginDelete()) + { + ed::LinkId deletedLinkId; + while (ed::QueryDeletedLink(&deletedLinkId)) + { + if (!readOnly()) + { + deleteLink(deletedLinkId); + } + else + { + _popup = true; + } + } + } + ed::EndDelete(); + } + + // diving into a node that has a subgraph + ed::NodeId clickedNode = ed::GetDoubleClickedNode(); + if (clickedNode.Get() > 0) + { + if (_currUiNode != nullptr) + { + if (_currUiNode->getNode() != nullptr) + { + + mx::InterfaceElementPtr impl = _currUiNode->getNode()->getImplementation(); + // only dive if current node is a node graph + if (impl && impl->isA()) + { + savePosition(); + _graphStack.push(_graphNodes); + _pinStack.push(_currPins); + _sizeStack.push(_graphTotalSize); + mx::NodeGraphPtr implGraph = impl->asA(); + _initial = true; + _graphNodes.clear(); + ed::DeselectNode(_currUiNode->getId()); + _currUiNode = nullptr; + _currGraphElem = implGraph; + if (readOnly()) + { + std::string graphName = implGraph->getName() + " (Read Only)"; + _currGraphName.push_back(graphName); + _popup = true; + } + else + { + + _currGraphName.push_back(implGraph->getName()); + } + buildUiNodeGraph(implGraph); + ed::NavigateToContent(); + } + } + else if (_currUiNode->getNodeGraph() != nullptr) + { + savePosition(); + _graphStack.push(_graphNodes); + _pinStack.push(_currPins); + _sizeStack.push(_graphTotalSize); + mx::NodeGraphPtr implGraph = _currUiNode->getNodeGraph(); + _initial = true; + _graphNodes.clear(); + _isNodeGraph = true; + setRenderMaterial(_currUiNode); + ed::DeselectNode(_currUiNode->getId()); + _currUiNode = nullptr; + _currGraphElem = implGraph; + if (readOnly()) + { + + std::string graphName = implGraph->getName() + " (Read Only)"; + _currGraphName.push_back(graphName); + _popup = true; + } + else + { + _currGraphName.push_back(implGraph->getName()); + } + buildUiNodeGraph(implGraph); + ed::NavigateToContent(); + } + } + } + + shaderPopup(); + if (ImGui::GetFrameCount() == (_frameCount + 2)) + { + updateMaterials(); + _renderer->setMaterialCompilation(false); + } + + ed::Suspend(); + _fileDialogSave.Display(); + // saving file + if (_fileDialogSave.HasSelected()) + { + + std::string message; + if (!_graphDoc->validate(&message)) + { + std::cerr << "*** Validation warnings for " << _materialFilename.getBaseName() << " ***" << std::endl; + std::cerr << message; + } + std::string fileName = _fileDialogSave.GetSelected().string(); + mx::FilePath name = _fileDialogSave.GetSelected().string(); + ed::Resume(); + savePosition(); + + writeText(fileName, name); + _fileDialogSave.ClearSelected(); + } + else + { + ed::Resume(); + } + + ed::End(); + ImGui::End(); + _fileDialog.Display(); + // create and load document from selected file + if (_fileDialog.HasSelected()) + { + mx::FilePath fileName = mx::FilePath(_fileDialog.GetSelected().string()); + _currGraphName.clear(); + std::string graphName = fileName.getBaseName(); + _currGraphName.push_back(graphName.substr(0, graphName.length() - 5)); + mx::DocumentPtr doc = loadDocument(fileName); + _graphDoc = doc; + _initial = true; + std::vector nodeGraphs = _graphDoc->getNodeGraphs(); + std::vector inputNodes = _graphDoc->getActiveInputs(); + std::vector outputNodes = _graphDoc->getOutputs(); + std::vector docNodes = _graphDoc->getNodes(); + + _graphDoc->importLibrary(_stdLib); + buildUiBaseGraph(nodeGraphs, docNodes, inputNodes, outputNodes); + _renderer->loadDocument(fileName, _stdLib); + if (_nodesToAdd.size() == 0) + { + std::vector nodeDefs = _stdLib->getNodeDefs(); + for (size_t i = 0; i < nodeDefs.size(); i++) + { + // nodeDef group is the key for the map + std::string group = nodeDefs[i]->getNodeGroup(); + std::unordered_map>::iterator it = _nodesToAdd.find(group); + if (it == _nodesToAdd.end()) + { + std::vector nodes; + _nodesToAdd[group] = nodes; + } + _nodesToAdd[group].push_back(nodeDefs[i]); + } + } + addExtraNodes(); + _currGraphElem = _graphDoc; + _prevUiNode = nullptr; + _fileDialog.ClearSelected(); + } + + _fileDialogConstant.Display(); +} + +// return node location in graphNodes vector based off of node id +int Graph::findNode(int nodeId) +{ + int count = 0; + for (size_t i = 0; i < _graphNodes.size(); i++) + { + if (_graphNodes[i]->getId() == nodeId) + { + return count; + } + count++; + } + return -1; +} + +// find a link based on an attribute id +std::vector Graph::findLinkId(int id) +{ + std::vector ids; + for (const Link& link : _currLinks) + { + if (link._startAttr == id || link._endAttr == id) + { + ids.push_back(link.id); + } + } + return ids; +} +// check if current edge is already in edge vector +bool Graph::edgeExists(UiEdge newEdge) +{ + if (_currEdge.size() > 0) + { + for (UiEdge edge : _currEdge) + { + if (edge.getDown()->getId() == newEdge.getDown()->getId()) + { + if (edge.getUp()->getId() == newEdge.getUp()->getId()) + { + if (edge.getInput() == newEdge.getInput()) + { + return true; + } + } + } + else if (edge.getUp()->getId() == newEdge.getDown()->getId()) + { + if (edge.getDown()->getId() == newEdge.getUp()->getId()) + { + if (edge.getInput() == newEdge.getInput()) + { + return true; + } + } + } + } + } + else + { + return false; + } + return false; +} + +// check if a link exists in currLink vector +bool Graph::linkExists(Link newLink) +{ + for (const auto& link : _currLinks) + { + if (link._startAttr == newLink._startAttr) + { + if (link._endAttr == newLink._endAttr) + { + // link exists + return true; + } + } + else if (link._startAttr == newLink._endAttr) + { + if (link._endAttr == newLink._startAttr) + { + // link exists + return true; + } + } + } + return false; +} + +// set materialX attribute positions for nodes which changed pos +void Graph::savePosition() +{ + for (UiNodePtr node : _graphNodes) + { + if (node->getMxElement() != nullptr) + { + ImVec2 pos = ed::GetNodePosition(node->getId()); + pos.x /= DEFAULT_NODE_SIZE.x; + pos.y /= DEFAULT_NODE_SIZE.y; + node->getMxElement()->setAttribute("xpos", std::to_string(pos.x)); + node->getMxElement()->setAttribute("ypos", std::to_string(pos.y)); + if (node->getMxElement()->hasAttribute("nodedef")) + { + node->getMxElement()->removeAttribute("nodedef"); + } + } + } +} +void Graph::writeText(std::string fileName, mx::FilePath filePath) +{ + if (filePath.getExtension() != mx::MTLX_EXTENSION) + { + filePath.addExtension(mx::MTLX_EXTENSION); + } + + mx::XmlWriteOptions writeOptions; + writeOptions.elementPredicate = _renderer->getElementPredicate(); + mx::writeToXmlFile(_graphDoc, filePath, &writeOptions); +} diff --git a/source/MaterialXGraphEditor/Graph.h b/source/MaterialXGraphEditor/Graph.h new file mode 100644 index 0000000000..49f1b5984a --- /dev/null +++ b/source/MaterialXGraphEditor/Graph.h @@ -0,0 +1,269 @@ +// +// Copyright Contributors to the MaterialX Project +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef MATERIALX_GRAPH_H +#define MATERIALX_GRAPH_H + +#if defined(_WIN32) + #ifndef NOMINMAX + #define NOMINMAX + #endif +#endif + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ed = ax::NodeEditor; +namespace mx = MaterialX; +class UiNode; +class Pin; +using UiNodePtr = std::shared_ptr; +using RenderViewPtr = std::shared_ptr; + +// A link connects two pins and includes a unique id and the ids of the two pins it connects +// Based off Link struct from ImGui Node Editor blueprints-examples.cpp +struct Link +{ + int id; + int _startAttr, _endAttr; + Link() : + _startAttr(-1), + _endAttr(-1) + { + static int _id = 0; + id = ++_id; + } +}; + +// class for edges between uiNodes +class UiEdge +{ + // an edge is made up of two UiNodes and their connecting input + public: + UiEdge(UiNodePtr uiDown, UiNodePtr uiUp, mx::InputPtr input) : + _uiDown(uiDown), + _uiUp(uiUp), + _input(input) + { + } + mx::InputPtr getInput() + { + return _input; + } + UiNodePtr getDown() + { + return _uiDown; + } + UiNodePtr getUp() + { + return _uiUp; + } + std::string getInputName() + { + if (_input != nullptr) + { + return _input->getName(); + } + else + { + return mx::EMPTY_STRING; + } + } + UiNodePtr _uiDown; + UiNodePtr _uiUp; + mx::InputPtr _input; +}; + +class Graph +{ + public: + Graph(const std::string& materialFilename, const mx::FileSearchPath& searchPath, const mx::FilePathVec& libraryFolders); + + RenderViewPtr getRenderer() + { + + return _renderer; + } + void initialize(); + void drawGraph(ImVec2 mousePos); + mx::DocumentPtr loadDocument(mx::FilePath filename); + + ~Graph(){}; + + private: + void loadStandardLibraries(); + void buildUiBaseGraph(const std::vector& nodeGraphs, const std::vector& docNodes, const std::vector& inputNodes, const std::vector& outputNodes); + void buildUiNodeGraph(const mx::NodeGraphPtr& nodeGraphs); + void buildGroupNode(UiNodePtr node); + + // handling link information + void linkGraph(); + void connectLinks(); + int findLinkPosition(int id); + std::vector findLinkId(int attrId); + bool linkExists(Link newLink); + void AddLink(ed::PinId inputPinId, ed::PinId outputPinId); + void deleteLink(ed::LinkId deletedLinkId); + void deleteLinkInfo(int startAtrr, int endAttr); + + // functions for the layout of the nodes + ImVec2 layoutPosition(UiNodePtr node, ImVec2 pos, bool initialLayout, int level); + void layoutInputs(); + void findYSpacing(float startPos); + float totalHeight(int level); + void setYSpacing(int level, float startingPos); + float findAvgY(const std::vector& nodes); + + // pin information + void setPinColor(); + void DrawPinIcon(std::string type, bool connected, int alpha); + Pin getPin(ed::PinId id); + void createInputPin(Pin pin); + ed::PinId getOutputPin(UiNodePtr node, UiNodePtr inputNode, Pin input); + void outputPin(UiNodePtr node); + void addNodeGraphPins(); + + // UiNode functions + std::vector createNodes(bool nodegraph); + int getNodeId(ed::PinId pinId); + int findNode(int nodeId); + int findNode(std::string name, std::string type); + void addNode(std::string category, std::string name, std::string type); + void deleteNode(UiNodePtr node); + void setUiNodeInfo(UiNodePtr node, std::string type, std::string category); + + // UiEdge functions + bool edgeExists(UiEdge edge); + void createEdge(UiNodePtr upNode, UiNodePtr downNode, mx::InputPtr connectingInput); + + void writeText(std::string filename, mx::FilePath filePath); + void savePosition(); + bool checkPosition(UiNodePtr node); + + void addNodeInput(UiNodePtr node, mx::InputPtr& input); + mx::InputPtr findInput(mx::InputPtr input, std::string name); + + // travel up from inside a node graph + void upNodeGraph(); + + // property editor information + void setConstant(UiNodePtr node, mx::InputPtr& input); + void propertyEditor(); + void setDefaults(mx::InputPtr input); + + // set up Ui information for add node popup + void addExtraNodes(); + + // copy and paste functions + void copyInputs(); + void positionPasteBin(ImVec2 pos); + void copyNodeGraph(UiNodePtr origGraph, UiNodePtr copyGraph); + void copyUiNode(UiNodePtr node); + + // renderview window and buttons + void graphButtons(); + + // popup information + void addNodePopup(bool cursor); + void searchNodePopup(bool cursor); + bool readOnly(); + void readOnlyPopup(); + void shaderPopup(); + + // modifying materials + void updateMaterials(mx::InputPtr input = nullptr, mx::ValuePtr value = nullptr); + void selectMaterial(UiNodePtr node); + void handleRenderViewInputs(ImVec2 minValue, float width, float height); + void setRenderMaterial(UiNodePtr node); + + RenderViewPtr _renderer; + + // document and intializing information + mx::FilePath _materialFilename; + mx::DocumentPtr _graphDoc; + mx::StringSet _xincludeFiles; + + mx::FileSearchPath _searchPath; + mx::FilePathVec _libraryFolders; + mx::DocumentPtr _stdLib; + + // image information + mx::ImagePtr _image; + mx::ImageHandlerPtr _imageHandler; + + // containers of node informatin + std::vector _graphNodes; + std::vector _currPins; + std::vector _currLinks; + std::vector _newLinks; + std::vector _currEdge; + std::unordered_map> _downstreamInputs; + std::unordered_map _pinColor; + + // current nodes and nodegraphs + UiNodePtr _currUiNode; + UiNodePtr _prevUiNode; + mx::GraphElementPtr _currGraphElem; + UiNodePtr _currRenderNode; + std::vector _currGraphName; + + // for adding new nodes + std::unordered_map> _nodesToAdd; + std::unordered_map>> _extraNodes; + + // stacks to dive into and out of node graphs + std::stack> _graphStack; + std::stack> _pinStack; + // this stack keeps track of the graph total size + std::stack _sizeStack; + + // map to group and layout nodes + std::unordered_map> _levelMap; + + // map for copied nodes + std::map _copiedNodes; + + bool _initial; + bool _delete; + + // file dialog information + ImGui::FileBrowser _fileDialog; + ImGui::FileBrowser _fileDialogSave; + ImGui::FileBrowser _fileDialogConstant; + + bool _isNodeGraph; + + int _graphTotalSize; + + // popup up variables + bool _popup; + bool _shaderPopup; + int _searchNodeId; + bool _addNewNode; + bool _ctrlClick; + bool _isCut; + // auto layout button clicked + bool _autoLayout; + + // used when updating materials + int _frameCount; + // used for filtering pins when connecting links + std::string _pinFilterType; +}; + +#endif diff --git a/source/MaterialXGraphEditor/Main.cpp b/source/MaterialXGraphEditor/Main.cpp new file mode 100644 index 0000000000..806e6f9a64 --- /dev/null +++ b/source/MaterialXGraphEditor/Main.cpp @@ -0,0 +1,236 @@ +// +// Copyright Contributors to the MaterialX Project +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include + +#include "imgui_impl_opengl3.h" + +#include + +namespace +{ + +ed::EditorContext* g_Context = nullptr; +bool g_FirstFrame = true; + +static void errorCallback(int error, const char* description) +{ + fprintf(stderr, "Glfw Error %d: %s\n", error, description); +} + +mx::FileSearchPath getDefaultSearchPath() +{ + mx::FilePath modulePath = mx::FilePath::getModulePath(); + mx::FilePath installRootPath = modulePath.getParentPath(); + mx::FilePath devRootPath = installRootPath.getParentPath().getParentPath(); + + mx::FileSearchPath searchPath; + if ((devRootPath / "libraries").exists()) + { + searchPath.append(devRootPath); + } + else + { + searchPath.append(installRootPath); + } + + return searchPath; +} + +const std::string options = + " Options: \n" + " --path [FILEPATH] Specify an additional absolute search path location (e.g. '/projects/MaterialX'). This path will be queried when locating standard data libraries, XInclude references, and referenced images.\n" + " --library [FILEPATH] Specify an additional relative path to a custom data library folder (e.g. 'libraries/custom'). MaterialX files at the root of this folder will be included in all content documents.\n" + " --help Display the complete list of command-line options\n"; + +template void parseToken(std::string token, std::string type, T& res) +{ + if (token.empty()) + { + return; + } + + mx::ValuePtr value = mx::Value::createValueFromStrings(token, type); + if (!value) + { + std::cout << "Unable to parse token " << token << " as type " << type << std::endl; + return; + } + + res = value->asA(); +} + +} // anonymous namespace + +int main(int argc, char* const argv[]) +{ + std::vector tokens; + for (int i = 1; i < argc; i++) + { + tokens.emplace_back(argv[i]); + } + + std::string materialFilename = "resources//Materials//Examples//StandardSurface//"; + mx::FileSearchPath searchPath = getDefaultSearchPath(); + mx::FilePathVec libraryFolders = { "libraries" }; + + for (size_t i = 0; i < tokens.size(); i++) + { + const std::string& token = tokens[i]; + const std::string& nextToken = i + 1 < tokens.size() ? tokens[i + 1] : mx::EMPTY_STRING; + + if (token == "--path") + { + searchPath.append(mx::FileSearchPath(nextToken)); + } + else if (token == "--library") + { + libraryFolders.push_back(nextToken); + } + else if (token == "--help") + { + std::cout << " MaterialXGraphEditor version " << mx::getVersionString() << std::endl; + std::cout << options << std::endl; + return 0; + } + else + { + std::cout << "Unrecognized command-line option: " << token << std::endl; + std::cout << "Launch the graph editor with '--help' for a complete list of supported options." << std::endl; + continue; + } + + if (nextToken.empty()) + { + std::cout << "Expected another token following command-line option: " << token << std::endl; + } + else + { + i++; + } + } + + // Setup window + glfwSetErrorCallback(errorCallback); + if (!glfwInit()) + { + return 1; + } + + // Determine GL and GLSL versions +#if defined(__APPLE__) + // GL 3.2 + GLSL 150 + const char* glsl_version = "#version 150"; + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac +#else + // GL 3.0 + GLSL 130 + const char* glsl_version = "#version 130"; + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); +#endif + + // Create window with graphics context + GLFWwindow* window = glfwCreateWindow(1280, 960, "MaterialX Graph Editor", NULL, NULL); + if (!window) + { + return 1; + } + glfwMakeContextCurrent(window); + glfwSwapInterval(1); // Enable vsync + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); + (void) io; + io.Fonts->AddFontDefault(); + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + + // Setup Platform/Renderer backends + ImGui_ImplGlfw_InitForOpenGL(window, true); + ImGui_ImplOpenGL3_Init(glsl_version); + + bool initialized = false; + Graph* graph = new Graph(materialFilename, searchPath, libraryFolders); + ImVec4 clearColor = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + + // Main loop + double xpos, ypos = 0.0; + xpos = 0.0; + + while (!glfwWindowShouldClose(window)) + { + + glfwPollEvents(); + + // Start the Dear ImGui frame + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + + // Node setup + if (g_FirstFrame) + { + ed::Config config; + config.SettingsFile = "BasicInteraction.json"; + g_Context = ed::CreateEditor(&config); + const float zoomLevels[] = { + 0.1f, + 0.15f, + 0.20f, + 0.25f, + 0.33f, + 0.5f, + 0.75f, + 1.0f, + }; + + for (auto& level : zoomLevels) + config.CustomZoomLevels.push_back(level); + } + ed::SetCurrentEditor(g_Context); + if (g_FirstFrame) + ed::NavigateToContent(0.0f); + + g_FirstFrame = false; + + if (!initialized) + { + initialized = true; + graph->initialize(); + } + + graph->getRenderer()->drawContents(); + int display_w, display_h; + glfwGetFramebufferSize(window, &display_w, &display_h); + graph->drawGraph(ImVec2((float) xpos, (float) ypos)); + ImGui::Render(); + glViewport(0, 0, display_w, display_h); + glClearColor(clearColor.x * clearColor.w, clearColor.y * clearColor.w, clearColor.z * clearColor.w, clearColor.w); + glClear(GL_COLOR_BUFFER_BIT); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + glfwGetCursorPos(window, &xpos, &ypos); + glfwSwapBuffers(window); + } + + // Cleanup + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + ImGui::DestroyContext(); + if (g_Context) + { + ed::DestroyEditor(g_Context); + g_Context = nullptr; + } + glfwDestroyWindow(window); + glfwTerminate(); + + return 0; +} diff --git a/source/MaterialXGraphEditor/Material.cpp b/source/MaterialXGraphEditor/Material.cpp new file mode 100644 index 0000000000..ea1e060770 --- /dev/null +++ b/source/MaterialXGraphEditor/Material.cpp @@ -0,0 +1,355 @@ +// +// Copyright Contributors to the MaterialX Project +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include "MaterialXRenderGlsl/GLTextureHandler.h" +#include "MaterialXRenderGlsl/GLUtil.h" +#include "MaterialXRenderGlsl/External/Glad/glad.h" + +#include + +#include + +// +// Material methods +// + +// this is all from the materialXView material class. May try to create a separate file to use for both in the future + +void Material::updateUniformsList() +{ + _uniformVariable.clear(); + if (!_glProgram) + { + return; + } + + for (const auto& pair : _glProgram->getUniformsList()) + { + _uniformVariable.insert(pair.first); + } +} + +void Material::clearShader() +{ + _hwShader = nullptr; + _glProgram = nullptr; + _uniformVariable.clear(); +} + +bool Material::generateShader(mx::GenContext& context) +{ + if (!_elem) + { + return false; + } + + _hasTransparency = mx::isTransparentSurface(_elem, context.getShaderGenerator().getTarget()); + + mx::GenContext materialContext = context; + materialContext.getOptions().hwTransparency = _hasTransparency; + + // Initialize in case creation fails and throws an exception + clearShader(); + + _hwShader = createShader("Shader", materialContext, _elem); + if (!_hwShader) + { + return false; + } + + _glProgram = mx::GlslProgram::create(); + _glProgram->setStages(_hwShader); + _glProgram->build(); + + updateUniformsList(); + return true; +} + +bool Material::generateShader(mx::ShaderPtr hwShader) +{ + _hwShader = hwShader; + + _glProgram = mx::GlslProgram::create(); + _glProgram->setStages(hwShader); + _glProgram->build(); + + updateUniformsList(); + + return true; +} + +void Material::bindShader() +{ + if (_glProgram) + { + _glProgram->bind(); + } +} + +void Material::bindMesh(mx::MeshPtr mesh) +{ + if (!mesh || !_glProgram) + { + return; + } + + _glProgram->bind(); + if (_boundMesh && mesh->getName() != _boundMesh->getName()) + { + _glProgram->unbindGeometry(); + } + _glProgram->bindMesh(mesh); + _boundMesh = mesh; +} + +bool Material::bindPartition(mx::MeshPartitionPtr part) const +{ + if (!_glProgram) + { + return false; + } + + _glProgram->bind(); + _glProgram->bindPartition(part); + + return true; +} + +void Material::bindViewInformation(mx::CameraPtr camera) +{ + if (!_glProgram) + { + return; + } + + _glProgram->bindViewInformation(camera); +} + +void Material::unbindImages(mx::ImageHandlerPtr imageHandler) +{ + for (mx::ImagePtr image : _boundImages) + { + imageHandler->unbindImage(image); + } +} + +void Material::bindImages(mx::ImageHandlerPtr imageHandler, const mx::FileSearchPath& searchPath, bool enableMipmaps) +{ + if (!_glProgram) + { + return; + } + + _boundImages.clear(); + + const mx::VariableBlock* publicUniforms = getPublicUniforms(); + if (!publicUniforms) + { + return; + } + for (const auto& uniform : publicUniforms->getVariableOrder()) + { + if (uniform->getType() != mx::Type::FILENAME) + { + continue; + } + const std::string& uniformVariable = uniform->getVariable(); + std::string filename; + if (uniform->getValue()) + { + filename = searchPath.find(uniform->getValue()->getValueString()); + } + + // Extract out sampling properties + mx::ImageSamplingProperties samplingProperties; + samplingProperties.setProperties(uniformVariable, *publicUniforms); + + // Set the requested mipmap sampling property, + samplingProperties.enableMipmaps = enableMipmaps; + + mx::ImagePtr image = bindImage(filename, uniformVariable, imageHandler, samplingProperties); + if (image) + { + _boundImages.push_back(image); + } + } +} + +mx::ImagePtr Material::bindImage(const mx::FilePath& filePath, const std::string& uniformName, mx::ImageHandlerPtr imageHandler, + const mx::ImageSamplingProperties& samplingProperties) +{ + if (!_glProgram) + { + return nullptr; + } + + // Create a filename resolver for geometric properties. + mx::StringResolverPtr resolver = mx::StringResolver::create(); + if (!getUdim().empty()) + { + resolver->setUdimString(getUdim()); + } + imageHandler->setFilenameResolver(resolver); + + // Acquire the given image. + mx::ImagePtr image = imageHandler->acquireImage(filePath); + if (!image) + { + return nullptr; + } + + // Bind the image and set its sampling properties. + if (imageHandler->bindImage(image, samplingProperties)) + { + mx::GLTextureHandlerPtr textureHandler = std::static_pointer_cast(imageHandler); + int textureLocation = textureHandler->getBoundTextureLocation(image->getResourceId()); + if (textureLocation >= 0) + { + _glProgram->bindUniform(uniformName, mx::Value::createValue(textureLocation), false); + return image; + } + } + return nullptr; +} + +void Material::bindLighting(mx::LightHandlerPtr lightHandler, mx::ImageHandlerPtr imageHandler, const ShadowState& shadowState) +{ + if (!_glProgram) + { + return; + } + + // Bind environment and local lighting. + _glProgram->bindLighting(lightHandler, imageHandler); + + // Bind shadow map properties + if (shadowState.shadowMap && _glProgram->hasUniform(mx::HW::SHADOW_MAP)) + { + mx::ImageSamplingProperties samplingProperties; + samplingProperties.uaddressMode = mx::ImageSamplingProperties::AddressMode::CLAMP; + samplingProperties.vaddressMode = mx::ImageSamplingProperties::AddressMode::CLAMP; + samplingProperties.filterType = mx::ImageSamplingProperties::FilterType::LINEAR; + + // Bind the shadow map. + if (imageHandler->bindImage(shadowState.shadowMap, samplingProperties)) + { + mx::GLTextureHandlerPtr textureHandler = std::static_pointer_cast(imageHandler); + int textureLocation = textureHandler->getBoundTextureLocation(shadowState.shadowMap->getResourceId()); + if (textureLocation >= 0) + { + _glProgram->bindUniform(mx::HW::SHADOW_MAP, mx::Value::createValue(textureLocation)); + } + } + _glProgram->bindUniform(mx::HW::SHADOW_MATRIX, mx::Value::createValue(shadowState.shadowMatrix)); + } + + // Bind ambient occlusion properties. + if (shadowState.ambientOcclusionMap && _glProgram->hasUniform(mx::HW::AMB_OCC_MAP)) + { + mx::ImageSamplingProperties samplingProperties; + samplingProperties.uaddressMode = mx::ImageSamplingProperties::AddressMode::PERIODIC; + samplingProperties.vaddressMode = mx::ImageSamplingProperties::AddressMode::PERIODIC; + samplingProperties.filterType = mx::ImageSamplingProperties::FilterType::LINEAR; + + // Bind the ambient occlusion map. + if (imageHandler->bindImage(shadowState.ambientOcclusionMap, samplingProperties)) + { + mx::GLTextureHandlerPtr textureHandler = std::static_pointer_cast(imageHandler); + int textureLocation = textureHandler->getBoundTextureLocation(shadowState.ambientOcclusionMap->getResourceId()); + if (textureLocation >= 0) + { + _glProgram->bindUniform(mx::HW::AMB_OCC_MAP, mx::Value::createValue(textureLocation)); + } + } + _glProgram->bindUniform(mx::HW::AMB_OCC_GAIN, mx::Value::createValue(shadowState.ambientOcclusionGain)); + } +} + +void Material::drawPartition(mx::MeshPartitionPtr part) const +{ + if (!part || !bindPartition(part)) + { + return; + } + mx::MeshIndexBuffer& indexData = part->getIndices(); + glDrawElements(GL_TRIANGLES, (GLsizei) indexData.size(), GL_UNSIGNED_INT, (void*) 0); +} + +void Material::unbindGeometry() +{ + if (_glProgram) + { + _glProgram->bind(); + _glProgram->unbindGeometry(); + } + _boundMesh = nullptr; +} + +mx::VariableBlock* Material::getPublicUniforms() const +{ + if (!_hwShader) + { + return nullptr; + } + + mx::ShaderStage& stage = _hwShader->getStage(mx::Stage::PIXEL); + mx::VariableBlock& block = stage.getUniformBlock(mx::HW::PUBLIC_UNIFORMS); + + return █ +} + +mx::ShaderPort* Material::findUniform(const std::string& path) const +{ + mx::ShaderPort* port = nullptr; + mx::VariableBlock* publicUniforms = getPublicUniforms(); + if (publicUniforms) + { + // Scan block based on path match predicate + port = publicUniforms->find( + [path](mx::ShaderPort* port) + { + return (port && mx::stringEndsWith(port->getPath(), path)); + }); + + // Check if the uniform exists in the shader program + if (port && !_uniformVariable.count(port->getVariable())) + { + port = nullptr; + } + } + return port; +} + +void Material::modifyUniform(const std::string& path, mx::ConstValuePtr value, std::string valueString) +{ + mx::ShaderPort* uniform = findUniform(path); + if (!uniform) + { + return; + } + + _glProgram->bind(); + _glProgram->bindUniform(uniform->getVariable(), value); + + if (valueString.empty()) + { + valueString = value->getValueString(); + } + uniform->setValue(mx::Value::createValueFromStrings(valueString, uniform->getType()->getName())); + if (_doc) + { + mx::ElementPtr element = _doc->getDescendant(uniform->getPath()); + if (element) + { + mx::ValueElementPtr valueElement = element->asA(); + if (valueElement) + { + valueElement->setValueString(valueString); + } + } + } +} diff --git a/source/MaterialXGraphEditor/Material.h b/source/MaterialXGraphEditor/Material.h new file mode 100644 index 0000000000..813e050830 --- /dev/null +++ b/source/MaterialXGraphEditor/Material.h @@ -0,0 +1,197 @@ +// +// Copyright Contributors to the MaterialX Project +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef MATERIALXEDITOR_MATERIAL_H +#define MATERIALXEDITOR_MATERIAL_H + +#include +#include +#include + +namespace mx = MaterialX; +using MaterialPtr = std::shared_ptr; + +class DocumentModifiers +{ + public: + mx::StringMap remapElements; + mx::StringSet skipElements; + std::string filePrefixTerminator; +}; + +class LightingState +{ + public: + mx::Matrix44 lightTransform; + bool directLighting = true; + bool indirectLighting = true; + int envSamples = 16; +}; + +class ShadowState +{ + public: + mx::ImagePtr shadowMap; + mx::Matrix44 shadowMatrix; + mx::ImagePtr ambientOcclusionMap; + float ambientOcclusionGain = 0.0f; +}; + +class Material +{ + public: + Material() : + _hasTransparency(false) + { + } + ~Material() { } + + static MaterialPtr create() + { + return std::make_shared(); + } + + // Return the document associated with this material + mx::DocumentPtr getDocument() const + { + return _doc; + } + + // Set the renderable element associated with this material + void setDocument(mx::DocumentPtr doc) + { + _doc = doc; + } + + // Return the renderable element associated with this material + mx::TypedElementPtr getElement() const + { + return _elem; + } + + // Set the renderable element associated with this material + void setElement(mx::TypedElementPtr val) + { + _elem = val; + } + + // Return the material node associated with this material + mx::NodePtr getMaterialNode() const + { + return _materialNode; + } + + // Set the material node associated with this material + void setMaterialNode(mx::NodePtr node) + { + _materialNode = node; + } + + // Get any associated udim identifier + const std::string& getUdim() + { + return _udim; + } + + // Set udim identifier + void setUdim(const std::string& val) + { + _udim = val; + } + + // Generate a shader from our currently stored element and + // the given generator context. + bool generateShader(mx::GenContext& context); + + // Generate a shader from the given hardware shader. + bool generateShader(mx::ShaderPtr hwShader); + + // Copy shader from one material to this one + void copyShader(MaterialPtr material) + { + _hwShader = material->_hwShader; + _glProgram = material->_glProgram; + } + + // Return the underlying hardware shader. + mx::ShaderPtr getShader() const + { + return _hwShader; + } + + // Return the underlying GLSL program. + mx::GlslProgramPtr getProgram() const + { + return _glProgram; + } + + // Return true if this material has transparency. + bool hasTransparency() const + { + return _hasTransparency; + } + + // Bind shader + void bindShader(); + + // Bind viewing information for this material. + void bindViewInformation(mx::CameraPtr Camera); + + // Bind all images for this material. + void bindImages(mx::ImageHandlerPtr imageHandler, const mx::FileSearchPath& searchPath, bool enableMipmaps = true); + + // Unbbind all images for this material. + void unbindImages(mx::ImageHandlerPtr imageHandler); + + // Bind a single image. + mx::ImagePtr bindImage(const mx::FilePath& filePath, const std::string& uniformName, mx::ImageHandlerPtr imageHandler, + const mx::ImageSamplingProperties& samplingProperties); + + // Bind lights to shader. + void bindLighting(mx::LightHandlerPtr lightHandler, mx::ImageHandlerPtr imageHandler, const ShadowState& shadowState); + + // Bind the given mesh to this material. + void bindMesh(mx::MeshPtr mesh); + + // Bind a mesh partition to this material. + bool bindPartition(mx::MeshPartitionPtr part) const; + + // Draw the given mesh partition. + void drawPartition(mx::MeshPartitionPtr part) const; + + // Unbind all geometry from this material. + void unbindGeometry(); + + // Return the block of public uniforms for this material. + mx::VariableBlock* getPublicUniforms() const; + + // Find a public uniform from its MaterialX path. + mx::ShaderPort* findUniform(const std::string& path) const; + + // Modify the value of the uniform with the given path. + void modifyUniform(const std::string& path, mx::ConstValuePtr value, std::string valueString = mx::EMPTY_STRING); + + protected: + void clearShader(); + void updateUniformsList(); + + protected: + mx::ShaderPtr _hwShader; + mx::GlslProgramPtr _glProgram; + + mx::MeshPtr _boundMesh; + + mx::DocumentPtr _doc; + mx::TypedElementPtr _elem; + mx::NodePtr _materialNode; + + std::string _udim; + bool _hasTransparency; + mx::StringSet _uniformVariable; + + mx::ImageVec _boundImages; +}; + +#endif diff --git a/source/MaterialXGraphEditor/RenderView.cpp b/source/MaterialXGraphEditor/RenderView.cpp new file mode 100644 index 0000000000..bd8af7237b --- /dev/null +++ b/source/MaterialXGraphEditor/RenderView.cpp @@ -0,0 +1,1341 @@ +// +// Copyright Contributors to the MaterialX Project +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include "MaterialXRenderGlsl/GLTextureHandler.h" +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include + +const mx::Vector3 DEFAULT_CAMERA_POSITION(0.0f, 0.0f, 5.0f); +const float DEFAULT_CAMERA_VIEW_ANGLE = 45.0f; +const float DEFAULT_CAMERA_ZOOM = 1.f; + +const int SHADOW_MAP_SIZE = 2048; +const int IRRADIANCE_MAP_WIDTH = 256; +const int IRRADIANCE_MAP_HEIGHT = 128; + +const float ORTHO_VIEW_DISTANCE = 1000.0f; +const float ORTHO_PROJECTION_HEIGHT = 1.8f; + +const std::string DIR_LIGHT_NODE_CATEGORY = "directional_light"; +const std::string IRRADIANCE_MAP_FOLDER = "irradiance"; + +const float IDEAL_MESH_SPHERE_RADIUS = 2.0f; + +const float PI = std::acos(-1.0f); + +// this is mostly taken from MaterialXView Viewer.cpp but only a subset of the functions with some changes and additions + +void applyModifiers(mx::DocumentPtr doc, const DocumentModifiers& modifiers) +{ + for (mx::ElementPtr elem : doc->traverseTree()) + { + if (modifiers.remapElements.count(elem->getCategory())) + { + elem->setCategory(modifiers.remapElements.at(elem->getCategory())); + } + if (modifiers.remapElements.count(elem->getName())) + { + elem->setName(modifiers.remapElements.at(elem->getName())); + } + mx::StringVec attrNames = elem->getAttributeNames(); + for (const std::string& attrName : attrNames) + { + if (modifiers.remapElements.count(elem->getAttribute(attrName))) + { + elem->setAttribute(attrName, modifiers.remapElements.at(elem->getAttribute(attrName))); + } + } + if (elem->hasFilePrefix() && !modifiers.filePrefixTerminator.empty()) + { + std::string filePrefix = elem->getFilePrefix(); + if (!mx::stringEndsWith(filePrefix, modifiers.filePrefixTerminator)) + { + elem->setFilePrefix(filePrefix + modifiers.filePrefixTerminator); + } + } + std::vector children = elem->getChildren(); + for (mx::ElementPtr child : children) + { + if (modifiers.skipElements.count(child->getCategory()) || + modifiers.skipElements.count(child->getName())) + { + elem->removeChild(child->getName()); + } + } + } + + // Remap references to unimplemented shader nodedefs. + for (mx::NodePtr materialNode : doc->getMaterialNodes()) + { + for (mx::NodePtr shader : getShaderNodes(materialNode)) + { + mx::NodeDefPtr nodeDef = shader->getNodeDef(); + if (nodeDef && !nodeDef->getImplementation()) + { + std::vector altNodeDefs = doc->getMatchingNodeDefs(nodeDef->getNodeString()); + for (mx::NodeDefPtr altNodeDef : altNodeDefs) + { + if (altNodeDef->getImplementation()) + { + shader->setNodeDefString(altNodeDef->getName()); + } + } + } + } + } + + // Remap unsupported texture coordinate indices. + for (mx::ElementPtr elem : doc->traverseTree()) + { + mx::NodePtr node = elem->asA(); + if (node && node->getCategory() == "texcoord") + { + mx::InputPtr index = node->getInput("index"); + mx::ValuePtr value = index ? index->getValue() : nullptr; + if (value && value->isA() && value->asA() != 0) + { + index->setValue(0); + } + } + } +} + +RenderView::RenderView(const std::string& materialFilename, + const std::string& meshFilename, + const std::string& envRadianceFilename, + const mx::FileSearchPath& searchPath, + const mx::FilePathVec& libraryFolders, + unsigned int screenWidth, + unsigned int screenHeight) : + _textureID(0), + _pixelRatio(1.0f), + _screenWidth(screenWidth), + _screenHeight(screenHeight), + _renderFrame(nullptr), + _materialFilename(materialFilename), + _meshFilename(meshFilename), + _envRadianceFilename(envRadianceFilename), + _searchPath(searchPath), + _libraryFolders(libraryFolders), + _meshScale(1.0f), + _cameraPosition(DEFAULT_CAMERA_POSITION), + _cameraUp(0.0f, 1.0f, 0.0f), + _cameraViewAngle(DEFAULT_CAMERA_VIEW_ANGLE), + _cameraNearDist(0.05f), + _cameraFarDist(5000.0f), + _cameraZoom(DEFAULT_CAMERA_ZOOM), + _userCameraEnabled(true), + _userTranslationActive(false), + _lightRotation(0.0f), + _shadowSoftness(1), + _ambientOcclusionGain(0.6f), + _selectedGeom(0), + _selectedMaterial(0), + _viewCamera(mx::Camera::create()), + _envCamera(mx::Camera::create()), + _shadowCamera(mx::Camera::create()), + _lightHandler(mx::LightHandler::create()), + _genContext(mx::GlslShaderGenerator::create()), + _unitRegistry(mx::UnitConverterRegistry::create()), + _splitByUdims(true), + _mergeMaterials(false), + _showAllInputs(false), + _materialCompilation(false), + _renderTransparency(true), + _renderDoubleSided(true), + _captureRequested(false), + _exitRequested(false) +{ + // Resolve input filenames, taking both the provided search path and + // current working directory into account. + mx::FileSearchPath localSearchPath = searchPath; + localSearchPath.append(mx::FilePath::getCurrentPath()); + _materialFilename = localSearchPath.find(_materialFilename); + _meshFilename = localSearchPath.find(_meshFilename); + _envRadianceFilename = localSearchPath.find(_envRadianceFilename); + + // Set default Glsl generator options. + _genContext.getOptions().targetColorSpaceOverride = "lin_rec709"; + _genContext.getOptions().fileTextureVerticalFlip = true; + _genContext.getOptions().hwShadowMap = true; +} + +void RenderView::initialize() +{ + // Initialize the standard libraries and color/unit management. + loadStandardLibraries(); + + // Initialize image handler. + _imageHandler = mx::GLTextureHandler::create(mx::StbImageLoader::create()); +#if MATERIALX_BUILD_OIIO + _imageHandler->addLoader(mx::OiioImageLoader::create()); +#endif + _imageHandler->setSearchPath(_searchPath); + + // Create geometry handler. + mx::TinyObjLoaderPtr objLoader = mx::TinyObjLoader::create(); + mx::CgltfLoaderPtr gltfLoader = mx::CgltfLoader::create(); + _geometryHandler = mx::GeometryHandler::create(); + _geometryHandler->addLoader(objLoader); + _geometryHandler->addLoader(gltfLoader); + loadMesh(_searchPath.find(_meshFilename)); + + // Initialize environment light. + loadEnvironmentLight(); + + // Initialize camera. + initCamera(); + + // Update geometry selections. + updateGeometrySelections(); + + // Load the requested material document. + loadDocument(_materialFilename, _stdLib); + + _pixelRatio = 1.f; +} + +void RenderView::assignMaterial(mx::MeshPartitionPtr geometry, MaterialPtr material) +{ + if (!geometry || _geometryHandler->getMeshes().empty()) + { + return; + } + if (geometry == getSelectedGeometry()) + { + setSelectedMaterial(material); + } + if (material) + { + _materialAssignments[geometry] = material; + material->unbindGeometry(); + } + else + { + _materialAssignments.erase(geometry); + } +} + +mx::FilePath RenderView::getBaseOutputPath() +{ + mx::FilePath baseFilename = _searchPath.find(_materialFilename); + baseFilename.removeExtension(); + mx::FilePath outputPath = mx::getEnviron("MATERIALX_VIEW_OUTPUT_PATH"); + if (!outputPath.isEmpty()) + { + baseFilename = outputPath / baseFilename.getBaseName(); + } + return baseFilename; +} + +mx::ElementPredicate RenderView::getElementPredicate() +{ + return [this](mx::ConstElementPtr elem) + { + if (elem->hasSourceUri()) + { + return (_xincludeFiles.count(elem->getSourceUri()) == 0); + } + return true; + }; +} + +void RenderView::updateGeometrySelections() +{ + _geometryList.clear(); + if (_geometryHandler->getMeshes().empty()) + { + return; + } + for (auto mesh : _geometryHandler->getMeshes()) + { + for (size_t partIndex = 0; partIndex < mesh->getPartitionCount(); partIndex++) + { + mx::MeshPartitionPtr part = mesh->getPartition(partIndex); + _geometryList.push_back(part); + } + } + + std::vector items; + for (const mx::MeshPartitionPtr& part : _geometryList) + { + std::string geomName = part->getName(); + mx::StringVec geomSplit = mx::splitString(geomName, ":"); + if (!geomSplit.empty() && !geomSplit[geomSplit.size() - 1].empty()) + { + geomName = geomSplit[geomSplit.size() - 1]; + } + items.push_back(geomName); + } + + _selectedGeom = 0; +} + +void RenderView::loadMesh(const mx::FilePath& filename) +{ + _geometryHandler->clearGeometry(); + if (_geometryHandler->loadGeometry(filename)) + { + _meshFilename = filename; + if (_splitByUdims) + { + for (auto mesh : _geometryHandler->getMeshes()) + { + mesh->splitByUdims(); + } + } + + updateGeometrySelections(); + + // Assign the selected material to all geometries. + _materialAssignments.clear(); + MaterialPtr material = getSelectedMaterial(); + if (material) + { + for (mx::MeshPartitionPtr geom : _geometryList) + { + assignMaterial(geom, material); + } + } + + // Unbind utility materials from the previous geometry. + if (_wireMaterial) + { + _wireMaterial->unbindGeometry(); + } + if (_shadowMaterial) + { + _shadowMaterial->unbindGeometry(); + } + } +} + +void RenderView::loadDocument(const mx::FilePath& filename, mx::DocumentPtr libraries) +{ + // Set up read options. + mx::XmlReadOptions readOptions; + readOptions.readXIncludeFunction = [](mx::DocumentPtr doc, const mx::FilePath& filename, + const mx::FileSearchPath& searchPath, const mx::XmlReadOptions* options) + { + mx::FilePath resolvedFilename = searchPath.find(filename); + if (resolvedFilename.exists()) + { + readFromXmlFile(doc, resolvedFilename, searchPath, options); + } + else + { + std::cerr << "Include file not found: " << filename.asString() << std::endl; + } + }; + + // Clear user data on the generator. + _genContext.clearUserData(); + + // Clear materials if merging is not requested. + if (!_mergeMaterials) + { + for (mx::MeshPartitionPtr geom : _geometryList) + { + if (_materialAssignments.count(geom)) + { + assignMaterial(geom, nullptr); + } + } + _materials.clear(); + } + std::vector newMaterials; + try + { + // Load source document. + mx::DocumentPtr doc = mx::createDocument(); + mx::readFromXmlFile(doc, filename, _searchPath, &readOptions); + _materialSearchPath = mx::getSourceSearchPath(doc); + + // Import libraries. + doc->importLibrary(libraries); + + // Apply direct lights. + applyDirectLights(doc); + + // Apply modifiers to the content document. + applyModifiers(doc, _modifiers); + + // Validate the document. + std::string message; + if (!doc->validate(&message)) + { + std::cerr << "*** Validation warnings for " << _materialFilename.getBaseName() << " ***" << std::endl; + std::cerr << message; + } + + // If requested, add implicit inputs to top-level nodes. + if (_showAllInputs) + { + for (mx::NodePtr node : doc->getNodes()) + { + node->addInputsFromNodeDef(); + } + } + + // Find new renderable elements. + mx::StringVec renderablePaths; + std::vector elems; + std::vector materialNodes; + mx::findRenderableElements(doc, elems); + if (elems.empty()) + { + throw mx::Exception("No renderable elements found in " + _materialFilename.getBaseName()); + } + for (mx::TypedElementPtr elem : elems) + { + mx::TypedElementPtr renderableElem = elem; + mx::NodePtr node = elem->asA(); + materialNodes.push_back(node && node->getType() == mx::MATERIAL_TYPE_STRING ? node : nullptr); + renderablePaths.push_back(renderableElem->getNamePath()); + } + + // Check for any udim set. + mx::ValuePtr udimSetValue = doc->getGeomPropValue(mx::UDIM_SET_PROPERTY); + + // Create new materials. + mx::TypedElementPtr udimElement; + for (size_t i = 0; i < renderablePaths.size(); i++) + { + const auto& renderablePath = renderablePaths[i]; + mx::ElementPtr elem = doc->getDescendant(renderablePath); + mx::TypedElementPtr typedElem = elem ? elem->asA() : nullptr; + if (!typedElem) + { + continue; + } + if (udimSetValue && udimSetValue->isA()) + { + for (const std::string& udim : udimSetValue->asA()) + { + MaterialPtr mat = Material::create(); + mat->setDocument(doc); + mat->setElement(typedElem); + mat->setMaterialNode(materialNodes[i]); + mat->setUdim(udim); + newMaterials.push_back(mat); + + udimElement = typedElem; + } + } + else + { + MaterialPtr mat = Material::create(); + mat->setDocument(doc); + mat->setElement(typedElem); + mat->setMaterialNode(materialNodes[i]); + newMaterials.push_back(mat); + } + } + + if (!newMaterials.empty()) + { + // Extend the image search path to include material source folders. + mx::FileSearchPath extendedSearchPath = _searchPath; + extendedSearchPath.append(_materialSearchPath); + _imageHandler->setSearchPath(extendedSearchPath); + + // Add new materials to the global vector. + _materials.insert(_materials.end(), newMaterials.begin(), newMaterials.end()); + + MaterialPtr udimMaterial = nullptr; + for (MaterialPtr mat : newMaterials) + { + // Clear cached implementations, in case libraries on the file system have changed. + _genContext.clearNodeImplementations(); + + mx::TypedElementPtr elem = mat->getElement(); + + std::string udim = mat->getUdim(); + if (!udim.empty()) + { + if ((udimElement == elem) && udimMaterial) + { + // Reuse existing material for all udims + mat->copyShader(udimMaterial); + } + else + { + // Generate a shader for the new material. + mat->generateShader(_genContext); + if (udimElement == elem) + { + udimMaterial = mat; + } + } + } + else + { + // Generate a shader for the new material. + mat->generateShader(_genContext); + } + + mx::NodePtr materialNode = mat->getMaterialNode(); + if (materialNode) + { + // Apply geometric assignments specified in the document, if any. + for (mx::MeshPartitionPtr part : _geometryList) + { + std::string geom = part->getName(); + for (const std::string& id : part->getSourceNames()) + { + geom += mx::ARRAY_PREFERRED_SEPARATOR + id; + } + if (!getGeometryBindings(materialNode, geom).empty()) + { + assignMaterial(part, mat); + } + } + + // Apply implicit udim assignments, if any. + if (!udim.empty()) + { + for (mx::MeshPartitionPtr geom : _geometryList) + { + if (geom->getName() == udim) + { + assignMaterial(geom, mat); + } + } + } + } + } + + // Apply fallback assignments. + MaterialPtr fallbackMaterial = newMaterials[0]; + if (!_mergeMaterials || fallbackMaterial->getUdim().empty()) + { + for (mx::MeshPartitionPtr geom : _geometryList) + { + if (!_materialAssignments[geom]) + { + assignMaterial(geom, fallbackMaterial); + } + } + } + } + } + catch (mx::ExceptionRenderError& e) + { + for (const std::string& error : e.errorLog()) + { + std::cerr << error << std::endl; + } + } + catch (std::exception& e) + { + std::cout << "Failed to load material" << e.what() << std::endl; + } +} + +void RenderView::setScrollEvent(float scrollY) +{ + if (_userCameraEnabled) + { + _cameraZoom = std::max(0.1f, _cameraZoom * ((scrollY > 0) ? 1.1f : 0.9f)); + } +} + +void RenderView::setKeyEvent(int key) +{ + if (_userCameraEnabled) + { + if (key == ImGuiKey_KeypadAdd) + { + _cameraZoom *= 1.1f; + } + if (key == ImGuiKey_KeypadSubtract) + { + _cameraZoom = std::max(0.1f, _cameraZoom * 0.9f); + } + } +} + +void RenderView::setMouseMotionEvent(mx::Vector2 pos) +{ + if (_viewCamera->applyArcballMotion(pos)) + { + return; + } + if (_userTranslationActive) + { + updateCameras(); + + mx::Vector3 boxMin = _geometryHandler->getMinimumBounds(); + mx::Vector3 boxMax = _geometryHandler->getMaximumBounds(); + mx::Vector3 sphereCenter = (boxMax + boxMin) / 2.0f; + + float viewZ = _viewCamera->projectToViewport(sphereCenter)[2]; + mx::Vector3 pos1 = _viewCamera->unprojectFromViewport( + mx::Vector3(pos[0], (float) _screenWidth - pos[1], viewZ)); + mx::Vector3 pos0 = _viewCamera->unprojectFromViewport( + mx::Vector3(_userTranslationPixel[0], (float) _screenWidth - _userTranslationPixel[1], viewZ)); + _userTranslation = _userTranslationStart + (pos1 - pos0); + } +} + +void RenderView::setMouseButtonEvent(int button, bool down, mx::Vector2 pos) +{ + + if ((button == 0) && !ImGui::IsKeyPressed(GLFW_KEY_RIGHT_SHIFT) && !ImGui::IsKeyPressed(GLFW_KEY_LEFT_SHIFT)) + { + _viewCamera->arcballButtonEvent(pos, down); + } + else if ((button == 1) || ((button == 0) && ImGui::IsKeyDown(GLFW_KEY_RIGHT_SHIFT)) || ((button == 0) && ImGui::IsKeyDown(GLFW_KEY_LEFT_SHIFT))) + { + _userTranslationStart = _userTranslation; + _userTranslationActive = true; + _userTranslationPixel = pos; + } + if ((button == 0) && !down) + { + _viewCamera->arcballButtonEvent(pos, false); + } + if (!down) + { + _userTranslationActive = false; + } +} + +void RenderView::setMaterial(mx::TypedElementPtr elem) +{ + // compare graph element to material in order to assign correct one + for (MaterialPtr mat : _materials) + { + mx::TypedElementPtr telem = mat->getElement(); + if (telem->getNamePath() == elem->getNamePath()) + { + assignMaterial(_geometryList[0], mat); + } + } +} + +void RenderView::updateMaterials(mx::DocumentPtr doc, mx::TypedElementPtr typedElem) +{ + // Clear user data on the generator. + _genContext.clearUserData(); + + // Clear materials if merging is not requested. + if (!_mergeMaterials) + { + for (mx::MeshPartitionPtr geom : _geometryList) + { + if (_materialAssignments.count(geom)) + { + assignMaterial(geom, nullptr); + } + } + _materials.clear(); + } + + //_genContext. + std::vector newMaterials; + try + { + _materialSearchPath = mx::getSourceSearchPath(doc); + + // Apply direct lights. + applyDirectLights(doc); + + //// Apply modifiers to the content document. + applyModifiers(doc, _modifiers); + + //// Check for any udim set. + mx::ValuePtr udimSetValue = doc->getGeomPropValue(mx::UDIM_SET_PROPERTY); + + //// Create new materials. + if (typedElem == nullptr) + { + std::vector elems; + mx::findRenderableElements(doc, elems); + if (elems.empty()) + { + throw mx::Exception("No renderable elements found in " + _materialFilename.getBaseName()); + } + else + { + typedElem = elems[0]; + } + } + mx::TypedElementPtr udimElement; + mx::NodePtr node = typedElem->asA(); + mx::NodePtr materialNode = node && node->getType() == mx::MATERIAL_TYPE_STRING ? node : nullptr; + if (udimSetValue && udimSetValue->isA()) + { + for (const std::string& udim : udimSetValue->asA()) + { + MaterialPtr mat = Material::create(); + mat->setDocument(doc); + mat->setElement(typedElem); + mat->setMaterialNode(materialNode); + mat->setUdim(udim); + newMaterials.push_back(mat); + + udimElement = typedElem; + } + } + else + { + MaterialPtr mat = Material::create(); + mat->setDocument(doc); + mat->setElement(typedElem); + mat->setMaterialNode(materialNode); + newMaterials.push_back(mat); + } + + if (!newMaterials.empty()) + { + // Extend the image search path to include material source folders. + mx::FileSearchPath extendedSearchPath = _searchPath; + extendedSearchPath.append(_materialSearchPath); + _imageHandler->setSearchPath(extendedSearchPath); + + // Add new materials to the global vector. + _materials.insert(_materials.end(), newMaterials.begin(), newMaterials.end()); + + MaterialPtr udimMaterial = nullptr; + for (MaterialPtr mat : newMaterials) + { + // Clear cached implementations, in case libraries on the file system have changed. + _genContext.clearNodeImplementations(); + + mx::TypedElementPtr elem = mat->getElement(); + + std::string udim = mat->getUdim(); + if (!udim.empty()) + { + if ((udimElement == elem) && udimMaterial) + { + // Reuse existing material for all udims + mat->copyShader(udimMaterial); + } + else + { + // Generate a shader for the new material. + mat->generateShader(_genContext); + if (udimElement == elem) + { + udimMaterial = mat; + } + } + } + else + { + // Generate a shader for the new material. + mat->generateShader(_genContext); + } + + if (materialNode) + { + // Apply geometric assignments specified in the document, if any. + for (mx::MeshPartitionPtr part : _geometryList) + { + std::string geom = part->getName(); + for (const std::string& id : part->getSourceNames()) + { + geom += mx::ARRAY_PREFERRED_SEPARATOR + id; + } + if (!getGeometryBindings(materialNode, geom).empty()) + { + assignMaterial(part, mat); + } + } + + // Apply implicit udim assignments, if any. + if (!udim.empty()) + { + for (mx::MeshPartitionPtr geom : _geometryList) + { + if (geom->getName() == udim) + { + assignMaterial(geom, mat); + } + } + } + } + } + + // Apply fallback assignments. + MaterialPtr fallbackMaterial = newMaterials[0]; + if (!_mergeMaterials || fallbackMaterial->getUdim().empty()) + { + for (mx::MeshPartitionPtr geom : _geometryList) + { + if (!_materialAssignments[geom]) + { + assignMaterial(geom, fallbackMaterial); + } + } + } + } + } + catch (mx::ExceptionRenderError& e) + { + for (const std::string& error : e.errorLog()) + { + std::cerr << error << std::endl; + } + } + catch (std::exception& e) + { + std::cerr << "Failed to load material" << e.what(); + } +} + +void RenderView::reloadShaders() +{ + try + { + for (MaterialPtr material : _materials) + { + material->generateShader(_genContext); + for (GLenum error = glGetError(); error; error = glGetError()) + { + std::cerr << "OpenGL error " + << "reload" + << ": " << std::to_string(error) << std::endl; + } + } + return; + } + catch (mx::ExceptionRenderError& e) + { + for (const std::string& error : e.errorLog()) + { + std::cerr << error << std::endl; + } + } + + _materials.clear(); +} + +void RenderView::initContext(mx::GenContext& context) +{ + // Initialize search path + context.registerSourceCodeSearchPath(_searchPath); + + // Initialize color management. + mx::DefaultColorManagementSystemPtr cms = mx::DefaultColorManagementSystem::create(context.getShaderGenerator().getTarget()); + cms->loadLibrary(_stdLib); + context.getShaderGenerator().setColorManagementSystem(cms); + + // Initialize unit management. + mx::UnitSystemPtr unitSystem = mx::UnitSystem::create(context.getShaderGenerator().getTarget()); + unitSystem->loadLibrary(_stdLib); + unitSystem->setUnitConverterRegistry(_unitRegistry); + context.getShaderGenerator().setUnitSystem(unitSystem); + context.getOptions().targetDistanceUnit = "meter"; +} + +void RenderView::loadStandardLibraries() +{ + // Initialize the standard library. + try + { + _stdLib = mx::createDocument(); + _xincludeFiles = mx::loadLibraries(_libraryFolders, _searchPath, _stdLib); + if (_xincludeFiles.empty()) + { + std::cerr << "Could not find standard data libraries on the given search path: " << _searchPath.asString() << std::endl; + } + } + catch (std::exception& e) + { + std::cerr << "Failed to load standard data libraries: " << e.what() << std::endl; + return; + } + + // Initialize unit management. + mx::UnitTypeDefPtr distanceTypeDef = _stdLib->getUnitTypeDef("distance"); + _distanceUnitConverter = mx::LinearUnitConverter::create(distanceTypeDef); + _unitRegistry->addUnitConverter(distanceTypeDef, _distanceUnitConverter); + mx::UnitTypeDefPtr angleTypeDef = _stdLib->getUnitTypeDef("angle"); + mx::LinearUnitConverterPtr angleConverter = mx::LinearUnitConverter::create(angleTypeDef); + _unitRegistry->addUnitConverter(angleTypeDef, angleConverter); + + // Create the list of supported distance units. + auto unitScales = _distanceUnitConverter->getUnitScale(); + _distanceUnitOptions.resize(unitScales.size()); + for (auto unitScale : unitScales) + { + int location = _distanceUnitConverter->getUnitAsInteger(unitScale.first); + _distanceUnitOptions[location] = unitScale.first; + } + + // Initialize the generator context. + initContext(_genContext); +} + +mx::ImagePtr RenderView::getAmbientOcclusionImage(MaterialPtr material) +{ + const mx::string AO_FILENAME_SUFFIX = "_ao"; + const mx::string AO_FILENAME_EXTENSION = "png"; + + if (!material || !_genContext.getOptions().hwAmbientOcclusion) + { + return nullptr; + } + + std::string aoSuffix = material->getUdim().empty() ? AO_FILENAME_SUFFIX : AO_FILENAME_SUFFIX + "_" + material->getUdim(); + mx::FilePath aoFilename = _meshFilename; + aoFilename.removeExtension(); + aoFilename = aoFilename.asString() + aoSuffix; + aoFilename.addExtension(AO_FILENAME_EXTENSION); + return _imageHandler->acquireImage(aoFilename); +} + +void RenderView::drawContents() +{ + if (_geometryList.empty() || _materials.empty()) + { + return; + } + + updateCameras(); + glClearColor(1.0, 1.0, 1.0, 1.0); + + // Render the current frame. + try + { + renderFrame(); + } + catch (std::exception&) + { + _materialAssignments.clear(); + glDisable(GL_FRAMEBUFFER_SRGB); + } + + // Capture the current frame. + if (_captureRequested) + { + _captureRequested = false; + mx::ImagePtr frameImage = getFrameImage(); + if (frameImage && _imageHandler->saveImage(_captureFilename, frameImage, true)) + { + std::cout << "Wrote frame to disk: " << _captureFilename.asString() << std::endl; + } + } +} + +void RenderView::applyDirectLights(mx::DocumentPtr doc) +{ + if (_lightRigDoc) + { + doc->importLibrary(_lightRigDoc); + _xincludeFiles.insert(_lightRigFilename); + } + + try + { + std::vector lights; + _lightHandler->findLights(doc, lights); + _lightHandler->registerLights(doc, lights, _genContext); + _lightHandler->setLightSources(lights); + } + catch (std::exception& e) + { + std::cerr << "Failed to set up lighting" << e.what(); + } +} + +void RenderView::loadEnvironmentLight() +{ + // Load the requested radiance map. + mx::ImagePtr envRadianceMap = _imageHandler->acquireImage(_envRadianceFilename); + if (!envRadianceMap) + { + return; + } + + // Look for an irradiance map using an expected filename convention. + mx::ImagePtr envIrradianceMap; + mx::FilePath envIrradiancePath = _envRadianceFilename.getParentPath() / IRRADIANCE_MAP_FOLDER / _envRadianceFilename.getBaseName(); + envIrradianceMap = _imageHandler->acquireImage(envIrradiancePath); + + // If not found, then generate an irradiance map via spherical harmonics. + if (envIrradianceMap == _imageHandler->getInvalidImage()) + { + mx::Sh3ColorCoeffs shIrradiance = mx::projectEnvironment(envRadianceMap, true); + envIrradianceMap = mx::renderEnvironment(shIrradiance, IRRADIANCE_MAP_WIDTH, IRRADIANCE_MAP_HEIGHT); + } + + // Release any existing environment maps and store the new ones. + _imageHandler->releaseRenderResources(_lightHandler->getEnvRadianceMap()); + _imageHandler->releaseRenderResources(_lightHandler->getEnvIrradianceMap()); + _lightHandler->setEnvRadianceMap(envRadianceMap); + _lightHandler->setEnvIrradianceMap(envIrradianceMap); + + // Look for a light rig using an expected filename convention. + _lightRigFilename = _envRadianceFilename; + _lightRigFilename.removeExtension(); + _lightRigFilename.addExtension(mx::MTLX_EXTENSION); + _lightRigFilename = _searchPath.find(_lightRigFilename); + if (_lightRigFilename.exists()) + { + _lightRigDoc = mx::createDocument(); + mx::readFromXmlFile(_lightRigDoc, _lightRigFilename, _searchPath); + } + else + { + _lightRigDoc = nullptr; + } +} + +void RenderView::renderFrame() +{ + // Initialize OpenGL state + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glDepthFunc(GL_LEQUAL); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glDisable(GL_CULL_FACE); + glDisable(GL_FRAMEBUFFER_SRGB); + + // Update lighting state. + _lightHandler->setLightTransform(mx::Matrix44::createRotationY(_lightRotation / 180.0f * PI)); + + // Update shadow state. + ShadowState shadowState; + shadowState.ambientOcclusionGain = _ambientOcclusionGain; + mx::NodePtr dirLight = _lightHandler->getFirstLightOfCategory(DIR_LIGHT_NODE_CATEGORY); + if (_genContext.getOptions().hwShadowMap && dirLight) + { + mx::ImagePtr shadowMap = getShadowMap(); + if (shadowMap) + { + shadowState.shadowMap = shadowMap; + shadowState.shadowMatrix = _viewCamera->getWorldMatrix().getInverse() * + _shadowCamera->getWorldViewProjMatrix(); + } + else + { + _genContext.getOptions().hwShadowMap = false; + } + } + + // Initialize viewport render. + if (!_renderFrame || + _renderFrame->getWidth() != _screenWidth || + _renderFrame->getHeight() != _screenHeight) + { + _renderFrame = mx::GLFramebuffer::create(_screenWidth, _screenHeight, 4, mx::Image::BaseType::UINT8); + } + + _renderFrame->bind(); + + glClearColor(.70f, .70f, .75f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + glEnable(GL_FRAMEBUFFER_SRGB); + + // Enable backface culling if requested. + if (!_renderDoubleSided) + { + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + } + + // Opaque pass + for (const auto& assignment : _materialAssignments) + { + + mx::MeshPartitionPtr geom = assignment.first; + MaterialPtr material = assignment.second; + shadowState.ambientOcclusionMap = getAmbientOcclusionImage(material); + if (!material) + { + continue; + } + + material->bindShader(); + material->bindMesh(_geometryHandler->findParentMesh(geom)); + if (material->getProgram()->hasUniform(mx::HW::ALPHA_THRESHOLD)) + { + material->getProgram()->bindUniform(mx::HW::ALPHA_THRESHOLD, mx::Value::createValue(0.99f)); + } + material->bindViewInformation(_viewCamera); + material->bindLighting(_lightHandler, _imageHandler, shadowState); + + material->bindImages(_imageHandler, _searchPath); + + glEnable(GL_FRAMEBUFFER_SRGB); + material->drawPartition(geom); + + material->unbindImages(_imageHandler); + } + + // Transparent pass + if (_renderTransparency) + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + for (const auto& assignment : _materialAssignments) + { + mx::MeshPartitionPtr geom = assignment.first; + MaterialPtr material = assignment.second; + shadowState.ambientOcclusionMap = getAmbientOcclusionImage(material); + if (!material || !material->hasTransparency()) + { + continue; + } + + material->bindShader(); + material->bindMesh(_geometryHandler->findParentMesh(geom)); + if (material->getProgram()->hasUniform(mx::HW::ALPHA_THRESHOLD)) + { + material->getProgram()->bindUniform(mx::HW::ALPHA_THRESHOLD, mx::Value::createValue(0.001f)); + } + material->bindViewInformation(_viewCamera); + material->bindLighting(_lightHandler, _imageHandler, shadowState); + material->bindImages(_imageHandler, _searchPath); + material->drawPartition(geom); + material->unbindImages(_imageHandler); + } + glDisable(GL_BLEND); + } + if (!_renderDoubleSided) + { + glDisable(GL_CULL_FACE); + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Store viewport texture for render. + _textureID = _renderFrame->getColorTexture(); +} + +mx::ImagePtr RenderView::getFrameImage() +{ + glFlush(); + + // Create an image with dimensions adjusted for device DPI. + mx::ImagePtr image = mx::Image::create((unsigned int) (_screenWidth * _pixelRatio), + (unsigned int) (_screenHeight * _pixelRatio), 3); + image->createResourceBuffer(); + + // Read pixels into the image buffer. + glReadPixels(0, 0, image->getWidth(), image->getHeight(), GL_RGB, GL_UNSIGNED_BYTE, image->getResourceBuffer()); + + return image; +} + +void RenderView::initCamera() +{ + _viewCamera->setViewportSize(mx::Vector2((float) _screenWidth, (float) _screenHeight)); + + // Disable user camera controls when non-centered views are requested. + _userCameraEnabled = _cameraTarget == mx::Vector3(0.0) && + _meshScale == 1.0f; + + if (!_userCameraEnabled || _geometryHandler->getMeshes().empty()) + { + return; + } + + const mx::Vector3& boxMax = _geometryHandler->getMaximumBounds(); + const mx::Vector3& boxMin = _geometryHandler->getMinimumBounds(); + mx::Vector3 sphereCenter = (boxMax + boxMin) * 0.5; + + mx::Matrix44 meshRotation = mx::Matrix44::createRotationZ(_meshRotation[2] / 180.0f * PI) * + mx::Matrix44::createRotationY(_meshRotation[1] / 180.0f * PI) * + mx::Matrix44::createRotationX(_meshRotation[0] / 180.0f * PI); + _meshTranslation = -meshRotation.transformPoint(sphereCenter); + _meshScale = IDEAL_MESH_SPHERE_RADIUS / (sphereCenter - boxMin).getMagnitude(); +} + +void RenderView::updateCameras() +{ + mx::Matrix44 viewMatrix, projectionMatrix; + float aspectRatio = (float) _screenHeight / _screenHeight; + if (_cameraViewAngle != 0.0f) + { + viewMatrix = mx::Camera::createViewMatrix(_cameraPosition, _cameraTarget, _cameraUp); + float fH = std::tan(_cameraViewAngle / 360.0f * PI) * _cameraNearDist; + float fW = fH * aspectRatio; + projectionMatrix = mx::Camera::createPerspectiveMatrix(-fW, fW, -fH, fH, _cameraNearDist, _cameraFarDist); + } + else + { + viewMatrix = mx::Matrix44::createTranslation(mx::Vector3(0.0f, 0.0f, -ORTHO_VIEW_DISTANCE)); + float fH = ORTHO_PROJECTION_HEIGHT; + float fW = fH * aspectRatio; + projectionMatrix = mx::Camera::createOrthographicMatrix(-fW, fW, -fH, fH, 0.0f, ORTHO_VIEW_DISTANCE + _cameraFarDist); + } + + mx::Matrix44 meshRotation = mx::Matrix44::createRotationZ(_meshRotation[2] / 180.0f * PI) * + mx::Matrix44::createRotationY(_meshRotation[1] / 180.0f * PI) * + mx::Matrix44::createRotationX(_meshRotation[0] / 180.0f * PI); + + mx::Matrix44 arcball = mx::Matrix44::IDENTITY; + if (_userCameraEnabled) + { + arcball = _viewCamera->arcballMatrix(); + } + + _viewCamera->setWorldMatrix(meshRotation * + mx::Matrix44::createTranslation(_meshTranslation + _userTranslation) * + mx::Matrix44::createScale(mx::Vector3(_meshScale * _cameraZoom))); + _viewCamera->setViewMatrix(arcball * viewMatrix); + _viewCamera->setProjectionMatrix(projectionMatrix); + + _envCamera->setWorldMatrix(mx::Matrix44::createScale(mx::Vector3(300.0f))); + _envCamera->setViewMatrix(_viewCamera->getViewMatrix()); + _envCamera->setProjectionMatrix(_viewCamera->getProjectionMatrix()); + + mx::NodePtr dirLight = _lightHandler->getFirstLightOfCategory(DIR_LIGHT_NODE_CATEGORY); + if (dirLight) + { + mx::Vector3 sphereCenter = (_geometryHandler->getMaximumBounds() + _geometryHandler->getMinimumBounds()) * 0.5; + float r = (sphereCenter - _geometryHandler->getMinimumBounds()).getMagnitude(); + _shadowCamera->setWorldMatrix(meshRotation * mx::Matrix44::createTranslation(-sphereCenter)); + _shadowCamera->setProjectionMatrix(mx::Camera::createOrthographicMatrix(-r, r, -r, r, 0.0f, r * 2.0f)); + mx::ValuePtr value = dirLight->getInputValue("direction"); + if (value->isA()) + { + mx::Vector3 dir = mx::Matrix44::createRotationY(_lightRotation / 180.0f * PI).transformVector(value->asA()); + _shadowCamera->setViewMatrix(mx::Camera::createViewMatrix(dir * -r, mx::Vector3(0.0f), _cameraUp)); + } + } +} + +MaterialPtr RenderView::getWireframeMaterial() +{ + if (!_wireMaterial) + { + try + { + mx::ShaderPtr hwShader = mx::createConstantShader(_genContext, _stdLib, "__WIRE_SHADER__", mx::Color3(1.0f)); + _wireMaterial = Material::create(); + _wireMaterial->generateShader(hwShader); + } + catch (std::exception& e) + { + std::cerr << "Failed to generate wireframe shader: " << e.what() << std::endl; + _wireMaterial = nullptr; + } + } + + return _wireMaterial; +} + +void RenderView::renderScreenSpaceQuad(MaterialPtr material) +{ + if (!_quadMesh) + _quadMesh = mx::GeometryHandler::createQuadMesh(); + + material->bindMesh(_quadMesh); + material->drawPartition(_quadMesh->getPartition(0)); +} + +mx::ImagePtr RenderView::getShadowMap() +{ + if (!_shadowMap) + { + // Generate shaders for shadow rendering. + if (!_shadowMaterial) + { + try + { + mx::ShaderPtr hwShader = mx::createDepthShader(_genContext, _stdLib, "__SHADOW_SHADER__"); + _shadowMaterial = Material::create(); + _shadowMaterial->generateShader(hwShader); + } + catch (std::exception& e) + { + std::cerr << "Failed to generate shadow shader: " << e.what() << std::endl; + _shadowMaterial = nullptr; + } + } + if (!_shadowBlurMaterial) + { + try + { + mx::ShaderPtr hwShader = mx::createBlurShader(_genContext, _stdLib, "__SHADOW_BLUR_SHADER__", "gaussian", 1.0f); + _shadowBlurMaterial = Material::create(); + _shadowBlurMaterial->generateShader(hwShader); + } + catch (std::exception& e) + { + std::cerr << "Failed to generate shadow blur shader: " << e.what() << std::endl; + _shadowBlurMaterial = nullptr; + } + } + + if (_shadowMaterial && _shadowBlurMaterial) + { + // Create framebuffer. + mx::GLFramebufferPtr framebuffer = mx::GLFramebuffer::create(SHADOW_MAP_SIZE, SHADOW_MAP_SIZE, 2, mx::Image::BaseType::FLOAT); + framebuffer->bind(); + glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + // Render shadow geometry. + _shadowMaterial->bindShader(); + for (auto mesh : _geometryHandler->getMeshes()) + { + _shadowMaterial->bindMesh(mesh); + _shadowMaterial->bindViewInformation(_shadowCamera); + for (size_t i = 0; i < mesh->getPartitionCount(); i++) + { + mx::MeshPartitionPtr geom = mesh->getPartition(i); + _shadowMaterial->drawPartition(geom); + } + } + _shadowMap = framebuffer->getColorImage(); + + // Apply Gaussian blurring. + mx::ImageSamplingProperties blurSamplingProperties; + blurSamplingProperties.uaddressMode = mx::ImageSamplingProperties::AddressMode::CLAMP; + blurSamplingProperties.vaddressMode = mx::ImageSamplingProperties::AddressMode::CLAMP; + blurSamplingProperties.filterType = mx::ImageSamplingProperties::FilterType::CLOSEST; + for (unsigned int i = 0; i < _shadowSoftness; i++) + { + framebuffer->bind(); + _shadowBlurMaterial->bindShader(); + if (_imageHandler->bindImage(_shadowMap, blurSamplingProperties)) + { + mx::GLTextureHandlerPtr textureHandler = std::static_pointer_cast(_imageHandler); + int textureLocation = textureHandler->getBoundTextureLocation(_shadowMap->getResourceId()); + if (textureLocation >= 0) + { + _shadowBlurMaterial->getProgram()->bindUniform("image_file", mx::Value::createValue(textureLocation)); + } + } + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + renderScreenSpaceQuad(_shadowBlurMaterial); + _imageHandler->releaseRenderResources(_shadowMap); + _shadowMap = framebuffer->getColorImage(); + } + + // Restore state for scene rendering. + glViewport(0, 0, (int32_t) _screenWidth, (int32_t) _screenHeight); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glDrawBuffer(GL_BACK); + } + } + + return _shadowMap; +} diff --git a/source/MaterialXGraphEditor/RenderView.h b/source/MaterialXGraphEditor/RenderView.h new file mode 100644 index 0000000000..073e2f0174 --- /dev/null +++ b/source/MaterialXGraphEditor/RenderView.h @@ -0,0 +1,347 @@ +// +// Copyright Contributors to the MaterialX Project +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef MATERIALX_RENDERVIEW_H +#define MATERIALX_RENDERVIEW_H + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "imgui_impl_glfw.h" + +namespace mx = MaterialX; + +class RenderView +{ + public: + RenderView(const std::string& materialFilename, + const std::string& meshFilename, + const std::string& envRadianceFilename, + const mx::FileSearchPath& searchPath, + const mx::FilePathVec& libraryFolders, + unsigned int screenWidth, + unsigned int screenHeight); + ~RenderView() { } + + // Initialize the viewer for rendering. + void initialize(); + + // Set the method for specular environment rendering. + void setSpecularEnvironmentMethod(mx::HwSpecularEnvironmentMethod method) + { + _genContext.getOptions().hwSpecularEnvironmentMethod = method; + } + + // Set the number of environment samples. + void setEnvSampleCount(int count) + { + _lightHandler->setEnvSampleCount(count); + } + + // Set the rotation of the lighting environment about the Y axis. + void setLightRotation(float rotation) + { + _lightRotation = rotation; + } + + // Enable or disable shadow maps. + void setShadowMapEnable(bool enable) + { + _genContext.getOptions().hwShadowMap = enable; + } + + // Set the modifiers to be applied to loaded documents. + void setDocumentModifiers(const DocumentModifiers& modifiers) + { + _modifiers = modifiers; + } + + // Return true if all inputs should be shown in the property editor. + bool getShowAllInputs() const + { + return _showAllInputs; + } + + void setScreenWidth(unsigned int width) + { + _screenWidth = width; + } + + void setScreenHeight(unsigned int height) + { + _screenHeight = height; + } + + std::vector getGeometryList() + { + return _geometryList; + } + + mx::FileSearchPath getMaterialSearchPath() + { + return _materialSearchPath; + } + + // Return the active image handler. + mx::ImageHandlerPtr getImageHandler() const + { + return _imageHandler; + } + + // Return the selected material. + MaterialPtr getSelectedMaterial() const + { + if (_selectedMaterial < _materials.size()) + { + return _materials[_selectedMaterial]; + } + return nullptr; + } + + // Return the selected mesh partition. + mx::MeshPartitionPtr getSelectedGeometry() const + { + if (_selectedGeom < _geometryList.size()) + { + return _geometryList[_selectedGeom]; + } + return nullptr; + } + + mx::GenContext& getGenContext() + { + return _genContext; + } + + std::map getMaterialAssignments() + { + return _materialAssignments; + } + + bool getMergeMaterials() + { + return _mergeMaterials; + } + + std::vector getMaterials() + { + return _materials; + } + + mx::CameraPtr getViewCamera() + { + return _viewCamera; + } + + mx::ElementPredicate getElementPredicate(); + // Request a capture of the current frame, writing it to the given filename. + void requestFrameCapture(const mx::FilePath& filename) + { + _captureRequested = true; + _captureFilename = filename; + } + + // Request that the viewer be closed after the next frame is rendered. + void requestExit() + { + _exitRequested = true; + } + + // return user camera enabled + bool getUserCameraEnabled() + { + return _userCameraEnabled; + } + + float getCameraZoom() + { + return _cameraZoom; + } + + void setCameraZoom(float amount) + { + _cameraZoom = amount; + } + + bool getMaterialCompilation() + { + return _materialCompilation; + } + + void setMaterialCompilation(bool mat) + { + _materialCompilation = mat; + } + + void drawContents(); + mx::ImagePtr getFrameImage(); + unsigned int _textureID; + void reloadShaders(); + + float _pixelRatio; + unsigned int _screenWidth; + unsigned int _screenHeight; + mx::GLFramebufferPtr _renderFrame; + void loadDocument(const mx::FilePath& filename, mx::DocumentPtr libraries); + void assignMaterial(mx::MeshPartitionPtr geometry, MaterialPtr material); + void updateMaterials(mx::DocumentPtr doc, mx::TypedElementPtr typedElem); + void setMouseButtonEvent(int button, bool down, mx::Vector2 pos); + void setMouseMotionEvent(mx::Vector2 pos); + void setKeyEvent(int key); + void setScrollEvent(float scrollY); + void setMaterial(mx::TypedElementPtr elem); + + private: + void initContext(mx::GenContext& context); + void loadMesh(const mx::FilePath& filename); + void loadEnvironmentLight(); + void applyDirectLights(mx::DocumentPtr doc); + void loadStandardLibraries(); + + // Mark the given material as currently selected in the viewer. + void setSelectedMaterial(MaterialPtr material) + { + for (size_t i = 0; i < _materials.size(); i++) + { + if (material == _materials[i]) + { + _selectedMaterial = i; + break; + } + } + } + + // Generate a base output filepath for data derived from the current material. + mx::FilePath getBaseOutputPath(); + + // Return an element predicate for documents written from the viewer. + + void initCamera(); + void updateCameras(); + void updateGeometrySelections(); + + // Return the ambient occlusion image, if any, associated with the given material. + mx::ImagePtr getAmbientOcclusionImage(MaterialPtr material); + MaterialPtr getWireframeMaterial(); + + mx::ImagePtr getShadowMap(); + mx::ImagePtr _renderMap; + + void renderFrame(); + void renderScreenSpaceQuad(MaterialPtr material); + + private: + mx::FilePath _materialFilename; + mx::FileSearchPath _materialSearchPath; + mx::FilePath _meshFilename; + mx::FilePath _envRadianceFilename; + + mx::FileSearchPath _searchPath; + mx::FilePathVec _libraryFolders; + + mx::Vector3 _meshTranslation; + mx::Vector3 _meshRotation; + float _meshScale; + + mx::Vector3 _cameraPosition; + mx::Vector3 _cameraTarget; + mx::Vector3 _cameraUp; + float _cameraViewAngle; + float _cameraNearDist; + float _cameraFarDist; + float _cameraZoom; + + bool _userCameraEnabled; + mx::Vector3 _userTranslation; + mx::Vector3 _userTranslationStart; + bool _userTranslationActive; + mx::Vector2 _userTranslationPixel; + + // Document management + mx::DocumentPtr _stdLib; + DocumentModifiers _modifiers; + mx::StringSet _xincludeFiles; + + // Lighting information + mx::FilePath _lightRigFilename; + mx::DocumentPtr _lightRigDoc; + float _lightRotation; + + // Shadow mapping + MaterialPtr _shadowMaterial; + MaterialPtr _shadowBlurMaterial; + mx::ImagePtr _shadowMap; + mx::ImagePtr _graphRender; + unsigned int _shadowSoftness; + + // Ambient occlusion + float _ambientOcclusionGain; + + // Geometry selections + std::vector _geometryList; + size_t _selectedGeom; + + // Material selections + std::vector _materials; + MaterialPtr _wireMaterial; + size_t _selectedMaterial; + + // Material assignments + std::map _materialAssignments; + + // Cameras + mx::CameraPtr _viewCamera; + mx::CameraPtr _envCamera; + mx::CameraPtr _shadowCamera; + + // Resource handlers + mx::GeometryHandlerPtr _geometryHandler; + mx::ImageHandlerPtr _imageHandler; + mx::LightHandlerPtr _lightHandler; + + // Supporting geometry. + mx::MeshPtr _quadMesh; + + // Shader generator context + mx::GenContext _genContext; + + // Unit registry + mx::UnitConverterRegistryPtr _unitRegistry; + + // Mesh options + bool _splitByUdims; + + // Material options + bool _mergeMaterials; + bool _showAllInputs; + bool _materialCompilation; + + // Unit options + mx::StringVec _distanceUnitOptions; + mx::LinearUnitConverterPtr _distanceUnitConverter; + + // Render options + bool _renderTransparency; + bool _renderDoubleSided; + + // Frame capture + bool _captureRequested; + mx::FilePath _captureFilename; + bool _exitRequested; +}; + +extern const mx::Vector3 DEFAULT_CAMERA_POSITION; +extern const float DEFAULT_CAMERA_VIEW_ANGLE; +extern const float DEFAULT_CAMERA_ZOOM; + +#endif diff --git a/source/MaterialXGraphEditor/UiNode.cpp b/source/MaterialXGraphEditor/UiNode.cpp new file mode 100644 index 0000000000..be6af0e8a0 --- /dev/null +++ b/source/MaterialXGraphEditor/UiNode.cpp @@ -0,0 +1,146 @@ +// +// Copyright Contributors to the MaterialX Project +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +namespace +{ + +const int INVALID_POS = -10000; + +} // anonymous namespace + +UiNode::UiNode() : + _level(-1), + _showAllInputs(false), + _id(0), + _nodePos(INVALID_POS, INVALID_POS), + _inputNodeNum(0) +{ +} + +UiNode::UiNode(const std::string name, int id) : + _level(-1), + _showAllInputs(false), + _id(id), + _nodePos(INVALID_POS, INVALID_POS), + _name(name), + _inputNodeNum(0) +{ +} + +UiNode::UiNode(int id) : + _level(-1), + _showAllInputs(false), + _id(id), + _nodePos(INVALID_POS, INVALID_POS), + _inputNodeNum(0) +{ +} + +// return the uiNode connected with input name +UiNodePtr UiNode::getConnectedNode(std::string name) +{ + for (UiEdge edge : edges) + { + if (edge.getInputName() == name) + { + return edge.getDown(); + } + else if (edge.getDown()->getName() == name) + { + return edge.getDown(); + } + } + for (UiEdge edge : edges) + { + + if (edge.getInputName() == "") + { + + return edge.getDown(); + } + } + return nullptr; +} + +float UiNode::getAverageY() +{ + float small = 10000000.f; + for (UiNodePtr node : _outputConnections) + { + ImVec2 pos = node->getPos(); + if (pos.y != INVALID_POS) + { + if (pos.y < small) + { + small = pos.x; + } + } + } + return small; +} + +float UiNode::getMinX() +{ + float small = 10000000.f; + for (UiNodePtr node : _outputConnections) + { + ImVec2 pos = node->getPos(); + if (pos.x != INVALID_POS) + { + if (pos.x < small) + { + small = pos.x; + } + } + } + return small; +} + +int UiNode::getEdgeIndex(int id) +{ + int count = 0; + for (UiEdge edge : edges) + { + if (edge.getUp()->getId() == id || edge.getDown()->getId() == id) + { + return count; + } + count++; + } + return -1; +} + +void UiNode::removeOutputConnection(std::string name) +{ + for (size_t i = 0; i < _outputConnections.size(); i++) + { + if (_outputConnections[i]->getName() == name) + { + _outputConnections.erase(_outputConnections.begin() + i); + } + } +} + +mx::ElementPtr UiNode::getMxElement() +{ + if (_currNode != nullptr) + { + return _currNode; + } + else if (_currInput != nullptr) + { + return _currInput; + } + else if (_currOutput != nullptr) + { + return _currOutput; + } + else + { + return nullptr; + } +} diff --git a/source/MaterialXGraphEditor/UiNode.h b/source/MaterialXGraphEditor/UiNode.h new file mode 100644 index 0000000000..73d788a15b --- /dev/null +++ b/source/MaterialXGraphEditor/UiNode.h @@ -0,0 +1,208 @@ +// +// Copyright Contributors to the MaterialX Project +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef MATERIALX_UINODE_H +#define MATERIALX_UINODE_H + +#include + +#include + +#include +#include + +namespace mx = MaterialX; +namespace ed = ax::NodeEditor; + +class UiEdge; +class Pin; + +class UiNode +{ + using UiNodePtr = std::shared_ptr; + + public: + UiNode(); + UiNode(const std::string name, const int id); + UiNode(const int id); + ~UiNode(){}; + + std::string getName() + { + return _name; + } + ImVec2 getPos() + { + return _nodePos; + } + int getInputConnect() + { + return _inputNodeNum; + } + int getId() + { + return _id; + } + std::vector getOutputConnections() + { + return _outputConnections; + } + mx::NodePtr getNode() + { + return _currNode; + } + mx::InputPtr getInput() + { + return _currInput; + } + mx::OutputPtr getOutput() + { + return _currOutput; + } + + void setName(const std::string& newName) + { + _name = newName; + } + void setPos(ImVec2 pos) + { + _nodePos = pos; + } + void setInputNodeNum(int num) + { + _inputNodeNum += num; + } + void setNode(mx::NodePtr node) + { + _currNode = node; + } + void setInput(mx::InputPtr input) + { + _currInput = input; + } + void setOutput(mx::OutputPtr output) + { + _currOutput = output; + } + void setOutputConnection(UiNodePtr connections) + { + _outputConnections.push_back(connections); + } + void setMessage(const std::string& message) + { + _message = message; + } + + std::string getMessage() + { + return _message; + } + + void setCategory(const std::string& category) + { + _category = category; + } + + std::string getCategory() + { + return _category; + } + + void setType(const std::string& type) + { + _type = type; + } + + std::string getType() + { + return _type; + } + mx::NodeGraphPtr getNodeGraph() + { + return _currNodeGraph; + } + + void setNodeGraph(mx::NodeGraphPtr nodeGraph) + { + _currNodeGraph = nodeGraph; + } + + friend bool operator==(const UiNodePtr& lhs, const UiNodePtr& rhs) + { + return lhs->getName() == rhs->getName(); + } + + bool operator()(const UiNodePtr& node1, const UiNodePtr& node2) const + { + return (node1->_level < node2->_level); + } + + // functions + UiNodePtr getConnectedNode(std::string name); + float getAverageY(); + float getMinX(); + int getEdgeIndex(int id); + std::vector edges; + std::vector inputPins; + std::vector outputPins; + void removeOutputConnection(std::string); + mx::ElementPtr getMxElement(); + int _level; + bool _showAllInputs; + + private: + int _id; + ImVec2 _nodePos; + std::string _name; + int _inputNodeNum; + std::vector> _inputs; + // used only for nodegraph nodes + std::vector> _outputs; + std::vector _outputConnections; + mx::NodePtr _currNode; + mx::InputPtr _currInput; + mx::OutputPtr _currOutput; + std::string _category; + std::string _message; + std::string _type; + mx::NodeGraphPtr _currNodeGraph; +}; + +// Based off Pin struct from ImGui Node Editor blueprints-examples.cpp +class Pin +{ + public: + Pin(int id, const char* name, std::string type, std::shared_ptr node, ed::PinKind kind, mx::InputPtr input, mx::OutputPtr output) : + _pinId(id), _name(name), _type(type), _pinNode(node), _kind(kind), _input(input), _output(output), _connected(false) + { + } + void setConnected(bool connected) + { + _connected = connected; + } + bool getConnected() + { + return _connected; + } + void addConnection(Pin pin) + { + _connections.push_back(pin); + } + std::vector getConnection() + { + return _connections; + } + ed::PinId _pinId; + std::string _name; + std::string _type; + std::shared_ptr _pinNode; + ed::PinKind _kind; + mx::InputPtr _input; + mx::OutputPtr _output; + std::vector _connections; + bool _connected; +}; + +#endif diff --git a/source/MaterialXRenderGlsl/CMakeLists.txt b/source/MaterialXRenderGlsl/CMakeLists.txt index 514967dfdf..ea343ce323 100644 --- a/source/MaterialXRenderGlsl/CMakeLists.txt +++ b/source/MaterialXRenderGlsl/CMakeLists.txt @@ -38,6 +38,10 @@ add_library(MaterialXRenderGlsl ${materialx_source} ${materialx_headers}) add_definitions(-DMATERIALX_RENDERGLSL_EXPORTS) +if(MATERIALX_BUILD_SHARED_LIBS) + target_compile_definitions(MaterialXRenderGlsl PUBLIC GLAD_GLAPI_EXPORT PRIVATE GLAD_GLAPI_EXPORT_BUILD) +endif() + set(COMMON_LIBRARIES MaterialXRenderHw MaterialXGenGlsl